├── .nvmrc ├── surveys ├── css2020 │ ├── config │ │ ├── report.mdx │ │ ├── capture.yml │ │ ├── authors.yml │ │ ├── sponsors.yml │ │ ├── config.yml │ │ └── picks.yml │ ├── images │ │ ├── favicon.png │ │ ├── picks │ │ │ ├── jina.jpg │ │ │ ├── 5t3ph.jpg │ │ │ ├── foolip.jpg │ │ │ ├── argyleink.jpg │ │ │ ├── shadeed9.jpg │ │ │ ├── hugogiraudel.jpg │ │ │ ├── piccalilli_.jpg │ │ │ ├── sachagreif.jpg │ │ │ ├── sarasoueidan.jpg │ │ │ ├── christianoliff.jpg │ │ │ ├── kilianvalkhof.jpg │ │ │ └── walterstephanie.jpg │ │ ├── guests │ │ │ └── amelia.png │ │ ├── stateofjs-logo.png │ │ ├── tshirt │ │ │ ├── tshirt4.png │ │ │ ├── tshirt5.jpg │ │ │ └── tshirt6.jpg │ │ ├── resources │ │ │ ├── wesbos │ │ │ │ ├── es6.jpg │ │ │ │ ├── learnnode.jpg │ │ │ │ ├── advancedreact.jpg │ │ │ │ ├── javascript30.jpg │ │ │ │ └── reactforbeginners.jpg │ │ │ ├── designcode1.jpg │ │ │ ├── designcode2.jpg │ │ │ ├── sojs2018-cdd.jpg │ │ │ ├── sojs2018-lsb.jpg │ │ │ ├── learnstorybook.png │ │ │ ├── chroma │ │ │ │ ├── sojs2019-ad.jpg │ │ │ │ └── sojs2019-lsb.jpg │ │ │ ├── vscode │ │ │ │ └── vscodepro.png │ │ │ ├── frontendmasters │ │ │ │ ├── code.jpg │ │ │ │ ├── css.jpg │ │ │ │ ├── grid.jpg │ │ │ │ ├── sass.jpg │ │ │ │ ├── svg.jpg │ │ │ │ ├── react.jpg │ │ │ │ ├── typography.jpg │ │ │ │ └── design-for-developers.jpg │ │ │ ├── newline │ │ │ │ ├── d3-fullstack.png │ │ │ │ ├── angular-ng-book.png │ │ │ │ ├── react-tinyhouse.png │ │ │ │ ├── vue-fullstack-vue.png │ │ │ │ ├── original │ │ │ │ │ ├── d3-fullstack.png │ │ │ │ │ ├── angular-ng-book.png │ │ │ │ │ ├── react-tinyhouse.png │ │ │ │ │ ├── vue-fullstack-vue.png │ │ │ │ │ ├── express-fullstack-nodejs.png │ │ │ │ │ └── react-native-fullstack-react-native.png │ │ │ │ ├── express-fullstack-nodejs.png │ │ │ │ └── react-native-fullstack-react-native.png │ │ │ └── sojs2018-visual-testing-handbook.jpg │ │ ├── stateofjs-socialmedia.png │ │ ├── sponsors │ │ │ ├── newline-logo-dark.png │ │ │ └── frontend-masters-dark.png │ │ └── stateofcss2020_socialmedia.png │ ├── logo │ │ └── IntroLogo.tsx │ └── theme │ │ ├── zIndexes.ts │ │ ├── dimensions.ts │ │ ├── index.ts │ │ ├── typography.ts │ │ └── charts.ts ├── css2021 │ ├── config │ │ ├── report.mdx │ │ ├── capture.yml │ │ ├── authors.yml │ │ ├── sponsors.yml │ │ ├── config.yml │ │ └── picks.yml │ ├── images │ │ ├── favicon.png │ │ ├── picks │ │ │ ├── jina.jpg │ │ │ ├── 5t3ph.jpg │ │ │ ├── foolip.jpg │ │ │ ├── argyleink.jpg │ │ │ ├── shadeed9.jpg │ │ │ ├── hugogiraudel.jpg │ │ │ ├── piccalilli_.jpg │ │ │ ├── sachagreif.jpg │ │ │ ├── sarasoueidan.jpg │ │ │ ├── christianoliff.jpg │ │ │ ├── kilianvalkhof.jpg │ │ │ └── walterstephanie.jpg │ │ ├── guests │ │ │ └── amelia.png │ │ ├── stateofjs-logo.png │ │ ├── tshirt │ │ │ ├── tshirt4.png │ │ │ ├── tshirt5.jpg │ │ │ └── tshirt6.jpg │ │ ├── resources │ │ │ ├── wesbos │ │ │ │ ├── es6.jpg │ │ │ │ ├── learnnode.jpg │ │ │ │ ├── advancedreact.jpg │ │ │ │ ├── javascript30.jpg │ │ │ │ └── reactforbeginners.jpg │ │ │ ├── designcode1.jpg │ │ │ ├── designcode2.jpg │ │ │ ├── sojs2018-cdd.jpg │ │ │ ├── sojs2018-lsb.jpg │ │ │ ├── learnstorybook.png │ │ │ ├── chroma │ │ │ │ ├── sojs2019-ad.jpg │ │ │ │ └── sojs2019-lsb.jpg │ │ │ ├── vscode │ │ │ │ └── vscodepro.png │ │ │ ├── frontendmasters │ │ │ │ ├── code.jpg │ │ │ │ ├── css.jpg │ │ │ │ ├── grid.jpg │ │ │ │ ├── sass.jpg │ │ │ │ ├── svg.jpg │ │ │ │ ├── react.jpg │ │ │ │ ├── typography.jpg │ │ │ │ └── design-for-developers.jpg │ │ │ ├── newline │ │ │ │ ├── d3-fullstack.png │ │ │ │ ├── angular-ng-book.png │ │ │ │ ├── react-tinyhouse.png │ │ │ │ ├── vue-fullstack-vue.png │ │ │ │ ├── original │ │ │ │ │ ├── d3-fullstack.png │ │ │ │ │ ├── angular-ng-book.png │ │ │ │ │ ├── react-tinyhouse.png │ │ │ │ │ ├── vue-fullstack-vue.png │ │ │ │ │ ├── express-fullstack-nodejs.png │ │ │ │ │ └── react-native-fullstack-react-native.png │ │ │ │ ├── express-fullstack-nodejs.png │ │ │ │ └── react-native-fullstack-react-native.png │ │ │ └── sojs2018-visual-testing-handbook.jpg │ │ ├── stateofjs-socialmedia.png │ │ ├── sponsors │ │ │ ├── newline-logo-dark.png │ │ │ └── frontend-masters-dark.png │ │ └── stateofcss2020_socialmedia.png │ ├── logo │ │ └── IntroLogo.tsx │ └── theme │ │ ├── zIndexes.ts │ │ ├── dimensions.ts │ │ ├── index.ts │ │ ├── typography.ts │ │ └── charts.ts └── js2020 │ ├── config │ ├── report.mdx │ ├── capture.yml │ ├── config.yml │ ├── sponsors.yml │ └── picks.yml │ ├── images │ ├── favicon.png │ ├── picks │ │ ├── swyx.jpg │ │ ├── cassidoo.jpg │ │ ├── ladyleet.jpg │ │ ├── midudev.jpeg │ │ ├── midudev.jpg │ │ ├── tomdale.jpg │ │ ├── kentcdodds.jpg │ │ ├── sachagreif.jpg │ │ ├── sarah_edo.jpg │ │ ├── joshwcomeau.jpg │ │ └── markdalgleish.jpg │ ├── tshirt │ │ ├── tshirt1.jpg │ │ ├── tshirt2.jpg │ │ ├── tshirt3.png │ │ ├── stateofjs2019tshirt-illustration.png │ │ └── stateofjs2019tshirt-illustration2.png │ ├── resources │ │ ├── froala.png │ │ ├── wb-es6.jpg │ │ ├── wesbos.jpg │ │ ├── wb-gatsby.png │ │ ├── wb-node.jpg │ │ ├── wb-react.jpg │ │ ├── webtestit.png │ │ ├── wb-graphql.png │ │ ├── wb-javascript.png │ │ ├── m-black-highres.png │ │ └── Sencha-WebTestIt-Logo-svg.png │ ├── stateofjs2020_socialmedia.png │ └── sponsors │ │ └── frontend-masters-dark.png │ ├── theme │ ├── zIndexes.ts │ ├── dimensions.ts │ ├── index.ts │ ├── typography.ts │ └── charts.ts │ └── logo │ ├── SidebarLogo.tsx │ └── LogoCell.tsx ├── src ├── core │ ├── components │ │ ├── sidebar │ │ │ └── index.js │ │ ├── LocaleLink.js │ │ ├── ButtonGroup.js │ │ ├── Debug.js │ │ ├── Hamburger.js │ │ ├── ChartLabel.js │ │ └── Head.js │ ├── blocks │ │ ├── tools │ │ │ ├── ToolsArrowsBlock │ │ │ │ ├── index.ts │ │ │ │ ├── types.ts │ │ │ │ ├── helpers.js │ │ │ │ └── ToolsArrowsBlock.tsx │ │ │ ├── ToolsCityscapeBlock │ │ │ │ └── index.ts │ │ │ ├── ToolsUsageVariationsBlock │ │ │ │ ├── index.ts │ │ │ │ ├── types.ts │ │ │ │ └── Switcher.tsx │ │ │ ├── ToolsExperienceMarimekkoBlock │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ ├── SectionToolsCardinalityByUserBlock │ │ │ │ ├── index.ts │ │ │ │ └── SectionToolsCardinalityByUserBlock.tsx │ │ │ ├── AllSectionsToolsCardinalityByUserBlock │ │ │ │ ├── index.ts │ │ │ │ └── AllSectionsToolsCardinalityByUserChart.tsx │ │ │ ├── ToolPeriodicElement.js │ │ │ └── ToolExperienceGraphBlock.js │ │ ├── happiness │ │ │ └── HappinessHistoryBlock │ │ │ │ ├── index.ts │ │ │ │ └── HappinessHistoryBlock.tsx │ │ ├── block │ │ │ ├── BlockData.js │ │ │ ├── BlockUnitsSelector.js │ │ │ ├── BlockViewSelector.js │ │ │ └── BlockNote.js │ │ ├── other │ │ │ ├── NotFoundBlock.js │ │ │ ├── PageIntroductionBlock.js │ │ │ ├── ConclusionBlock.js │ │ │ ├── SurveyIntroBlock.js │ │ │ ├── TextBlock.js │ │ │ ├── NewsletterBlock.js │ │ │ └── BioBlock.js │ │ ├── types.ts │ │ ├── awards │ │ │ └── AwardIcon.js │ │ ├── features │ │ │ └── FeatureResources.js │ │ └── generic │ │ │ └── HeatmapBlock.js │ ├── charts │ │ ├── generic │ │ │ ├── UsageVariationsChart │ │ │ │ ├── index.ts │ │ │ │ ├── multiple-diverging-lines │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── props.ts │ │ │ │ │ ├── NegativePattern.tsx │ │ │ │ │ ├── types.ts │ │ │ │ │ ├── ResponsiveMultipleDivergingLines.tsx │ │ │ │ │ ├── SubAxes.tsx │ │ │ │ │ └── Labels.tsx │ │ │ │ └── UsageVariationsChart.tsx │ │ │ ├── HorizontalBarStripes.js │ │ │ └── BarTooltip.js │ │ ├── tools │ │ │ ├── ToolExperienceGraphChart │ │ │ │ ├── index.js │ │ │ │ ├── NodeTooltip.js │ │ │ │ ├── LinkTooltip.js │ │ │ │ ├── YearsLayer.js │ │ │ │ └── ToolExperienceGraphChart.js │ │ │ └── ToolLabel.js │ │ ├── demographics │ │ │ ├── ParticipationByCountryTooltip.js │ │ │ └── ParticipationByCountryChart.js │ │ ├── features │ │ │ ├── FeaturesOverviewCirclePackingChart.js │ │ │ └── FeaturesCirclePackingChartTooltip.js │ │ ├── table │ │ │ └── Table.js │ │ └── hooks.ts │ ├── helpers │ │ ├── tools.js │ │ ├── bold.js │ │ ├── stripMarkdown.js │ │ ├── paragraphs.js │ │ ├── replaceAll.js │ │ ├── slugify.js │ │ ├── pageContext.js │ │ ├── useBucketKeys.js │ │ ├── toolsContext.js │ │ └── keydownContext.js │ ├── survey_api │ │ ├── happiness.ts │ │ ├── opinions.ts │ │ ├── tools.ts │ │ └── matrices.ts │ ├── theme │ │ ├── index.js │ │ ├── typography.ts │ │ ├── util.js │ │ ├── mq.js │ │ ├── colors.ts │ │ └── charts.ts │ ├── types.ts │ ├── share │ │ ├── tracking.js │ │ ├── ShareBlockDebug.js │ │ ├── ShareSite.js │ │ ├── ShareFacebook.js │ │ ├── ShareBlockTemplate.js │ │ ├── ShareLinkedIn.js │ │ ├── ShareTwitter.js │ │ ├── ShareEmail.js │ │ └── SharePermalink.js │ ├── i18n │ │ ├── Trans.js │ │ ├── translation-key-getters.js │ │ ├── wording.js │ │ ├── i18nContext.js │ │ ├── translator.js │ │ ├── Locales.js │ │ └── T.js │ ├── pages │ │ ├── PageLabel.js │ │ ├── PageMeta.js │ │ ├── PageLink.js │ │ ├── PageTemplate.js │ │ ├── IntroductionFooter.js │ │ ├── PageHeader.js │ │ ├── PageMetaDebug.js │ │ └── PaginationLink.js │ ├── report │ │ ├── ReportLayout.js │ │ ├── ReportSectionLink.js │ │ ├── ReportBlock.js │ │ └── ReportHeading.js │ └── entities │ │ └── entitiesContext.js └── stylesheets │ ├── screen.scss │ ├── to-remove │ ├── _typography.scss │ ├── _variables.scss │ ├── _breakpoints.scss │ └── _colors.scss │ └── _features.scss ├── .prettierrc ├── .eslintrc.js ├── gatsby-ssr.js ├── tsconfig.json ├── README.md ├── gatsby-browser.js ├── .gitignore ├── scripts └── patch-react-spring.js ├── gatsby-config.js └── LICENSE.md /.nvmrc: -------------------------------------------------------------------------------- 1 | v14.17.5 -------------------------------------------------------------------------------- /surveys/css2020/config/report.mdx: -------------------------------------------------------------------------------- 1 | Delete me -------------------------------------------------------------------------------- /surveys/css2021/config/report.mdx: -------------------------------------------------------------------------------- 1 | Delete me -------------------------------------------------------------------------------- /surveys/js2020/config/report.mdx: -------------------------------------------------------------------------------- 1 | Nothing here yet -------------------------------------------------------------------------------- /src/core/components/sidebar/index.js: -------------------------------------------------------------------------------- 1 | export * from './Sidebar' 2 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolsArrowsBlock/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ToolsArrowsBlock' 2 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolsCityscapeBlock/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ToolsCityscapeBlock' 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | printWidth: 100 2 | semi: false 3 | tabWidth: 4 4 | singleQuote: true 5 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolsArrowsBlock/types.ts: -------------------------------------------------------------------------------- 1 | export interface ToolsArrowsToolData { 2 | } 3 | -------------------------------------------------------------------------------- /src/core/charts/generic/UsageVariationsChart/index.ts: -------------------------------------------------------------------------------- 1 | export * from './UsageVariationsChart' 2 | -------------------------------------------------------------------------------- /src/core/blocks/happiness/HappinessHistoryBlock/index.ts: -------------------------------------------------------------------------------- 1 | export * from './HappinessHistoryBlock' 2 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolsUsageVariationsBlock/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ToolsUsageVariationsBlock' 2 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolsExperienceMarimekkoBlock/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ToolsExperienceMarimekkoBlock' 2 | -------------------------------------------------------------------------------- /src/core/blocks/tools/SectionToolsCardinalityByUserBlock/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SectionToolsCardinalityByUserBlock' -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | __PATH_PREFIX__: true, 4 | }, 5 | extends: `react-app`, 6 | } -------------------------------------------------------------------------------- /src/core/helpers/tools.js: -------------------------------------------------------------------------------- 1 | export const getToolName = (id, translate) => { 2 | return translate(`tool.${id}`) 3 | } 4 | -------------------------------------------------------------------------------- /src/core/survey_api/happiness.ts: -------------------------------------------------------------------------------- 1 | export interface HappinessYearMean { 2 | year: number 3 | mean: number 4 | } 5 | -------------------------------------------------------------------------------- /src/core/helpers/bold.js: -------------------------------------------------------------------------------- 1 | const parseBold = (s) => s.replace(/\*([^*]+)\*/g, '$1') 2 | 3 | export default parseBold 4 | -------------------------------------------------------------------------------- /src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AllSectionsToolsCardinalityByUserBlock' 2 | -------------------------------------------------------------------------------- /src/core/helpers/stripMarkdown.js: -------------------------------------------------------------------------------- 1 | import removeMd from 'remove-markdown' 2 | 3 | export const stripMarkdown = (s) => removeMd(s) 4 | -------------------------------------------------------------------------------- /surveys/css2020/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/favicon.png -------------------------------------------------------------------------------- /surveys/css2021/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/favicon.png -------------------------------------------------------------------------------- /surveys/js2020/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/favicon.png -------------------------------------------------------------------------------- /surveys/css2020/images/picks/jina.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/jina.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/picks/jina.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/jina.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/swyx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/swyx.jpg -------------------------------------------------------------------------------- /surveys/css2020/config/capture.yml: -------------------------------------------------------------------------------- 1 | baseUrl: http://localhost:9000 2 | outputDir: static/images/captures 3 | sitemap: config/sitemap.yml 4 | -------------------------------------------------------------------------------- /surveys/css2020/images/guests/amelia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/guests/amelia.png -------------------------------------------------------------------------------- /surveys/css2020/images/picks/5t3ph.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/5t3ph.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/picks/foolip.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/foolip.jpg -------------------------------------------------------------------------------- /surveys/css2021/config/capture.yml: -------------------------------------------------------------------------------- 1 | baseUrl: http://localhost:9000 2 | outputDir: static/images/captures 3 | sitemap: config/sitemap.yml 4 | -------------------------------------------------------------------------------- /surveys/css2021/images/guests/amelia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/guests/amelia.png -------------------------------------------------------------------------------- /surveys/css2021/images/picks/5t3ph.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/5t3ph.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/picks/foolip.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/foolip.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/cassidoo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/cassidoo.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/ladyleet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/ladyleet.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/midudev.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/midudev.jpeg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/midudev.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/midudev.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/tomdale.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/tomdale.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/tshirt/tshirt1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/tshirt/tshirt1.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/tshirt/tshirt2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/tshirt/tshirt2.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/tshirt/tshirt3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/tshirt/tshirt3.png -------------------------------------------------------------------------------- /surveys/css2020/images/picks/argyleink.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/argyleink.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/picks/shadeed9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/shadeed9.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/stateofjs-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/stateofjs-logo.png -------------------------------------------------------------------------------- /surveys/css2020/images/tshirt/tshirt4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/tshirt/tshirt4.png -------------------------------------------------------------------------------- /surveys/css2020/images/tshirt/tshirt5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/tshirt/tshirt5.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/tshirt/tshirt6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/tshirt/tshirt6.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/picks/argyleink.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/argyleink.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/picks/shadeed9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/shadeed9.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/stateofjs-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/stateofjs-logo.png -------------------------------------------------------------------------------- /surveys/css2021/images/tshirt/tshirt4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/tshirt/tshirt4.png -------------------------------------------------------------------------------- /surveys/css2021/images/tshirt/tshirt5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/tshirt/tshirt5.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/tshirt/tshirt6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/tshirt/tshirt6.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/kentcdodds.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/kentcdodds.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/sachagreif.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/sachagreif.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/sarah_edo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/sarah_edo.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/resources/froala.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/froala.png -------------------------------------------------------------------------------- /surveys/js2020/images/resources/wb-es6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/wb-es6.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/resources/wesbos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/wesbos.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/picks/hugogiraudel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/hugogiraudel.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/picks/piccalilli_.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/piccalilli_.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/picks/sachagreif.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/sachagreif.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/picks/sarasoueidan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/sarasoueidan.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/picks/hugogiraudel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/hugogiraudel.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/picks/piccalilli_.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/piccalilli_.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/picks/sachagreif.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/sachagreif.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/picks/sarasoueidan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/sarasoueidan.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/joshwcomeau.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/joshwcomeau.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/picks/markdalgleish.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/picks/markdalgleish.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/resources/wb-gatsby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/wb-gatsby.png -------------------------------------------------------------------------------- /surveys/js2020/images/resources/wb-node.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/wb-node.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/resources/wb-react.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/wb-react.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/resources/webtestit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/webtestit.png -------------------------------------------------------------------------------- /surveys/css2020/images/picks/christianoliff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/christianoliff.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/picks/kilianvalkhof.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/kilianvalkhof.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/wesbos/es6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/wesbos/es6.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/picks/christianoliff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/christianoliff.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/picks/kilianvalkhof.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/kilianvalkhof.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/wesbos/es6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/wesbos/es6.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/resources/wb-graphql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/wb-graphql.png -------------------------------------------------------------------------------- /surveys/css2020/images/picks/walterstephanie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/picks/walterstephanie.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/designcode1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/designcode1.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/designcode2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/designcode2.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/sojs2018-cdd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/sojs2018-cdd.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/sojs2018-lsb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/sojs2018-lsb.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/stateofjs-socialmedia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/stateofjs-socialmedia.png -------------------------------------------------------------------------------- /surveys/css2021/images/picks/walterstephanie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/picks/walterstephanie.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/designcode1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/designcode1.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/designcode2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/designcode2.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/sojs2018-cdd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/sojs2018-cdd.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/sojs2018-lsb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/sojs2018-lsb.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/stateofjs-socialmedia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/stateofjs-socialmedia.png -------------------------------------------------------------------------------- /surveys/js2020/images/resources/wb-javascript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/wb-javascript.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/learnstorybook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/learnstorybook.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/learnstorybook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/learnstorybook.png -------------------------------------------------------------------------------- /surveys/js2020/images/resources/m-black-highres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/m-black-highres.png -------------------------------------------------------------------------------- /surveys/js2020/images/stateofjs2020_socialmedia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/stateofjs2020_socialmedia.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/chroma/sojs2019-ad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/chroma/sojs2019-ad.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/vscode/vscodepro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/vscode/vscodepro.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/wesbos/learnnode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/wesbos/learnnode.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/sponsors/newline-logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/sponsors/newline-logo-dark.png -------------------------------------------------------------------------------- /surveys/css2020/images/stateofcss2020_socialmedia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/stateofcss2020_socialmedia.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/chroma/sojs2019-ad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/chroma/sojs2019-ad.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/vscode/vscodepro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/vscode/vscodepro.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/wesbos/learnnode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/wesbos/learnnode.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/sponsors/newline-logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/sponsors/newline-logo-dark.png -------------------------------------------------------------------------------- /surveys/css2021/images/stateofcss2020_socialmedia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/stateofcss2020_socialmedia.png -------------------------------------------------------------------------------- /src/core/charts/generic/UsageVariationsChart/multiple-diverging-lines/index.ts: -------------------------------------------------------------------------------- 1 | export * from './MultipleDivergingLines' 2 | export * from './ResponsiveMultipleDivergingLines' 3 | -------------------------------------------------------------------------------- /src/core/charts/tools/ToolExperienceGraphChart/index.js: -------------------------------------------------------------------------------- 1 | import ToolExperienceGraphChart from './ToolExperienceGraphChart' 2 | 3 | export default ToolExperienceGraphChart 4 | -------------------------------------------------------------------------------- /surveys/css2020/images/resources/chroma/sojs2019-lsb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/chroma/sojs2019-lsb.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/frontendmasters/code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/frontendmasters/code.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/frontendmasters/css.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/frontendmasters/css.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/frontendmasters/grid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/frontendmasters/grid.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/frontendmasters/sass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/frontendmasters/sass.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/frontendmasters/svg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/frontendmasters/svg.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/d3-fullstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/d3-fullstack.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/wesbos/advancedreact.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/wesbos/advancedreact.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/wesbos/javascript30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/wesbos/javascript30.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/sponsors/frontend-masters-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/sponsors/frontend-masters-dark.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/chroma/sojs2019-lsb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/chroma/sojs2019-lsb.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/frontendmasters/code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/frontendmasters/code.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/frontendmasters/css.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/frontendmasters/css.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/frontendmasters/grid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/frontendmasters/grid.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/frontendmasters/sass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/frontendmasters/sass.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/frontendmasters/svg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/frontendmasters/svg.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/d3-fullstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/d3-fullstack.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/wesbos/advancedreact.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/wesbos/advancedreact.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/wesbos/javascript30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/wesbos/javascript30.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/sponsors/frontend-masters-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/sponsors/frontend-masters-dark.png -------------------------------------------------------------------------------- /surveys/js2020/images/sponsors/frontend-masters-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/sponsors/frontend-masters-dark.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/frontendmasters/react.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/frontendmasters/react.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/angular-ng-book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/angular-ng-book.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/react-tinyhouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/react-tinyhouse.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/frontendmasters/react.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/frontendmasters/react.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/angular-ng-book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/angular-ng-book.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/react-tinyhouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/react-tinyhouse.png -------------------------------------------------------------------------------- /src/core/theme/index.js: -------------------------------------------------------------------------------- 1 | export { default as mq } from './mq' 2 | export * from './util' 3 | export * from './mixins' 4 | export * from './typography' 5 | export * from './GlobalStyle' 6 | -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/vue-fullstack-vue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/vue-fullstack-vue.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/wesbos/reactforbeginners.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/wesbos/reactforbeginners.jpg -------------------------------------------------------------------------------- /surveys/css2020/logo/IntroLogo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Logo } from './Logo' 3 | 4 | export const IntroLogo = () => 5 | 6 | export default IntroLogo -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/vue-fullstack-vue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/vue-fullstack-vue.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/wesbos/reactforbeginners.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/wesbos/reactforbeginners.jpg -------------------------------------------------------------------------------- /surveys/css2021/logo/IntroLogo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Logo } from './Logo' 3 | 4 | export const IntroLogo = () => 5 | 6 | export default IntroLogo -------------------------------------------------------------------------------- /surveys/js2020/images/resources/Sencha-WebTestIt-Logo-svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/resources/Sencha-WebTestIt-Logo-svg.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/frontendmasters/typography.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/frontendmasters/typography.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/frontendmasters/typography.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/frontendmasters/typography.jpg -------------------------------------------------------------------------------- /surveys/js2020/images/tshirt/stateofjs2019tshirt-illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/tshirt/stateofjs2019tshirt-illustration.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/original/d3-fullstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/original/d3-fullstack.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/original/d3-fullstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/original/d3-fullstack.png -------------------------------------------------------------------------------- /surveys/js2020/images/tshirt/stateofjs2019tshirt-illustration2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/js2020/images/tshirt/stateofjs2019tshirt-illustration2.png -------------------------------------------------------------------------------- /src/core/helpers/paragraphs.js: -------------------------------------------------------------------------------- 1 | import replaceAll from '../helpers/replaceAll' 2 | 3 | const addParagraphs = (s) => `

${replaceAll(s, '\n\n', '

')}

` 4 | 5 | export default addParagraphs 6 | -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/express-fullstack-nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/express-fullstack-nodejs.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/original/angular-ng-book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/original/angular-ng-book.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/original/react-tinyhouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/original/react-tinyhouse.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/sojs2018-visual-testing-handbook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/sojs2018-visual-testing-handbook.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/express-fullstack-nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/express-fullstack-nodejs.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/original/angular-ng-book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/original/angular-ng-book.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/original/react-tinyhouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/original/react-tinyhouse.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/sojs2018-visual-testing-handbook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/sojs2018-visual-testing-handbook.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/original/vue-fullstack-vue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/original/vue-fullstack-vue.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/original/vue-fullstack-vue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/original/vue-fullstack-vue.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/frontendmasters/design-for-developers.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/frontendmasters/design-for-developers.jpg -------------------------------------------------------------------------------- /surveys/css2021/images/resources/frontendmasters/design-for-developers.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/frontendmasters/design-for-developers.jpg -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/original/express-fullstack-nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/original/express-fullstack-nodejs.png -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/react-native-fullstack-react-native.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/react-native-fullstack-react-native.png -------------------------------------------------------------------------------- /surveys/css2020/theme/zIndexes.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | 3 | const zIndexes: DefaultTheme['zIndexes'] = { 4 | popover: 5000, 5 | modal: 6000, 6 | } 7 | 8 | export default zIndexes 9 | -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/original/express-fullstack-nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/original/express-fullstack-nodejs.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/react-native-fullstack-react-native.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/react-native-fullstack-react-native.png -------------------------------------------------------------------------------- /surveys/css2021/theme/zIndexes.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | 3 | const zIndexes: DefaultTheme['zIndexes'] = { 4 | popover: 5000, 5 | modal: 6000, 6 | } 7 | 8 | export default zIndexes 9 | -------------------------------------------------------------------------------- /surveys/js2020/theme/zIndexes.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | 3 | const zIndexes: DefaultTheme['zIndexes'] = { 4 | popover: 5000, 5 | modal: 6000, 6 | } 7 | 8 | export default zIndexes 9 | -------------------------------------------------------------------------------- /surveys/js2020/config/capture.yml: -------------------------------------------------------------------------------- 1 | baseUrl: http://localhost:9000 2 | # baseUrl: https://2020.stateofjs.com 3 | mosaic: 4 | thumb_width: 500 5 | thumb_height: 400 6 | columns: 14 7 | background: 0x232840FF 8 | -------------------------------------------------------------------------------- /src/core/helpers/replaceAll.js: -------------------------------------------------------------------------------- 1 | const replaceAll = function (s, search, replacement) { 2 | const newString = s.replace(new RegExp(search, 'g'), replacement) 3 | return newString 4 | } 5 | 6 | export default replaceAll 7 | -------------------------------------------------------------------------------- /surveys/css2020/images/resources/newline/original/react-native-fullstack-react-native.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2020/images/resources/newline/original/react-native-fullstack-react-native.png -------------------------------------------------------------------------------- /surveys/css2021/images/resources/newline/original/react-native-fullstack-react-native.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devographics/StateOfJS-2020/HEAD/surveys/css2021/images/resources/newline/original/react-native-fullstack-react-native.png -------------------------------------------------------------------------------- /src/core/types.ts: -------------------------------------------------------------------------------- 1 | export interface Entity { 2 | name: string 3 | description?: string 4 | homepage?: string 5 | github?: { 6 | url?: string 7 | } 8 | } 9 | 10 | export interface Completion { 11 | count: number 12 | percentage: number 13 | } 14 | -------------------------------------------------------------------------------- /surveys/css2020/theme/dimensions.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | 3 | const stateOfCSSThemeDimensions: DefaultTheme['dimensions'] = { 4 | spacing: 20, 5 | sidebar: { 6 | width: 270, 7 | }, 8 | } 9 | 10 | export default stateOfCSSThemeDimensions 11 | -------------------------------------------------------------------------------- /surveys/css2021/theme/dimensions.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | 3 | const stateOfCSSThemeDimensions: DefaultTheme['dimensions'] = { 4 | spacing: 20, 5 | sidebar: { 6 | width: 270, 7 | }, 8 | } 9 | 10 | export default stateOfCSSThemeDimensions 11 | -------------------------------------------------------------------------------- /surveys/js2020/theme/dimensions.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | 3 | const stateOfJSThemeDimensions: DefaultTheme['dimensions'] = { 4 | spacing: 20, 5 | sidebar: { 6 | width: 270, 7 | }, 8 | } 9 | 10 | export default stateOfJSThemeDimensions 11 | -------------------------------------------------------------------------------- /src/core/share/tracking.js: -------------------------------------------------------------------------------- 1 | import ReactGA from 'react-ga' 2 | 3 | const trackShare = (platform, trackingId) => () => { 4 | ReactGA.event({ 5 | category: platform, 6 | action: trackingId ? `${trackingId} share` : `site share`, 7 | }) 8 | } 9 | 10 | export default trackShare 11 | -------------------------------------------------------------------------------- /src/core/helpers/slugify.js: -------------------------------------------------------------------------------- 1 | import replaceAll from './replaceAll' 2 | 3 | const slugify = (s, dashToUnderscore = false) => { 4 | const slug = replaceAll(s.toLowerCase(), ' ', '-') 5 | const slugUnderscore = replaceAll(slug, '-', '_') 6 | return dashToUnderscore ? slugUnderscore : slug 7 | } 8 | 9 | export default slugify 10 | -------------------------------------------------------------------------------- /src/core/i18n/Trans.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import { useI18n } from './i18nContext' 3 | 4 | const Trans = ({ children }) => { 5 | const { translate } = useI18n() 6 | 7 | return children(translate) 8 | } 9 | 10 | Trans.propTypes = { 11 | children: PropTypes.func.isRequired, 12 | } 13 | 14 | export default Trans 15 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolsUsageVariationsBlock/types.ts: -------------------------------------------------------------------------------- 1 | import { allMatrixDimensionIds, MatrixDimensionId } from 'core/survey_api/matrices' 2 | 3 | export type DimensionId = Exclude 4 | 5 | export const allDimensionIds = allMatrixDimensionIds.filter( 6 | (dimension) => !['source'].includes(dimension) 7 | ) as DimensionId[] 8 | -------------------------------------------------------------------------------- /src/core/helpers/pageContext.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext } from 'react' 2 | 3 | const pageContext = createContext({}) 4 | 5 | export const PageContextProvider = (props) => { 6 | return {props.children} 7 | } 8 | 9 | export const usePageContext = () => useContext(pageContext) 10 | -------------------------------------------------------------------------------- /gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import LayoutWrapper from 'core/layout/LayoutWrapper' 3 | 4 | export const wrapPageElement = ({ element, props }) => { 5 | const { pageContext, ...rest } = props 6 | 7 | return ( 8 | 9 | {element} 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/core/blocks/block/BlockData.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import Table from '../../charts/table/Table'; 4 | 5 | const BlockData = ({ data, id, headings, tables }) => { 6 | return ( 7 | <> 8 | 9 | 10 | ); 11 | } 12 | 13 | export default BlockData; -------------------------------------------------------------------------------- /src/core/blocks/other/NotFoundBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useI18n } from 'core/i18n/i18nContext' 3 | import TextBlock from 'core/blocks/other/TextBlock' 4 | 5 | const NotFoundBlock = () => { 6 | const { translate } = useI18n() 7 | 8 | return 9 | } 10 | 11 | export default NotFoundBlock 12 | -------------------------------------------------------------------------------- /src/core/charts/generic/UsageVariationsChart/multiple-diverging-lines/props.ts: -------------------------------------------------------------------------------- 1 | import { OrdinalColorScaleConfigScheme } from '@nivo/colors' 2 | 3 | export const defaultProps = { 4 | enableGridX: false, 5 | 6 | colors: { scheme: 'nivo' } as OrdinalColorScaleConfigScheme, 7 | 8 | isInteractive: true, 9 | 10 | animate: true, 11 | motionConfig: 'gentle', 12 | } 13 | -------------------------------------------------------------------------------- /src/core/i18n/translation-key-getters.js: -------------------------------------------------------------------------------- 1 | const keyFrom = (name) => name.replace(/ |-|\/|\./g, '_').toLowerCase() 2 | 3 | export const countryNameToTranslationKey = (name) => `countries.${keyFrom(name)}` 4 | 5 | export const sourceNameToTranslationKey = (name) => `sources.${keyFrom(name)}` 6 | 7 | export const libraryDescriptionToTranslationKey = (name) => `library.descriptions.${keyFrom(name)}` 8 | -------------------------------------------------------------------------------- /src/core/components/LocaleLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | import { usePageContext } from 'core/helpers/pageContext' 4 | import get from 'lodash/get' 5 | 6 | const LocaleLink = ({ to, ...rest }) => { 7 | const context = usePageContext() 8 | return 9 | } 10 | 11 | export default LocaleLink 12 | -------------------------------------------------------------------------------- /src/core/survey_api/opinions.ts: -------------------------------------------------------------------------------- 1 | import { Completion } from 'core/types' 2 | 3 | export interface OpinionBucket { 4 | id: 0 | 1 | 2 | 3 | 4 5 | count: number 6 | percentage: number 7 | } 8 | 9 | export interface OpinionYearData { 10 | year: number 11 | total: number 12 | completion: Completion 13 | buckets: OpinionBucket[] 14 | } 15 | 16 | export type OpinionAllYearsData = OpinionYearData[] 17 | -------------------------------------------------------------------------------- /src/core/theme/typography.ts: -------------------------------------------------------------------------------- 1 | import { css } from 'styled-components' 2 | 3 | export const primaryFontMixin = css` 4 | font-family: ${({ theme }) => theme.typography.fontFamily}; 5 | font-weight: ${({ theme }) => theme.typography.weight.light}; 6 | ` 7 | 8 | export const secondaryFontMixin = css` 9 | font-family: ${({ theme }) => theme.typography.fontFamily}; 10 | font-weight: ${({ theme }) => theme.typography.weight.bold}; 11 | ` 12 | -------------------------------------------------------------------------------- /src/core/theme/util.js: -------------------------------------------------------------------------------- 1 | export const spacing = (multiplier = 1) => ({ theme }) => 2 | `${theme.dimensions.spacing * multiplier}px` 3 | 4 | export const fontSize = (size) => ({ theme }) => theme.typography.size[size] 5 | 6 | export const fontWeight = (weight) => ({ theme }) => theme.typography.weight[weight] 7 | 8 | export const color = (id) => ({ theme }) => theme.colors[id] 9 | 10 | export const zIndex = (id) => ({ theme }) => theme.zIndexes[id] 11 | -------------------------------------------------------------------------------- /src/core/pages/PageLabel.js: -------------------------------------------------------------------------------- 1 | import { getPageLabel } from 'core/helpers/pageHelpers' 2 | import { useI18n } from 'core/i18n/i18nContext' 3 | // import { useTools } from 'core/helpers/toolsContext' 4 | 5 | const PageLabel = ({ page, isContextual, includeWebsite }) => { 6 | const { translate } = useI18n() 7 | // const { getToolName } = useTools() 8 | 9 | return getPageLabel(page, translate, { isContextual, includeWebsite }) 10 | } 11 | 12 | export default PageLabel 13 | -------------------------------------------------------------------------------- /src/core/components/ButtonGroup.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { mq } from 'core/theme' 3 | 4 | const ButtonGroup = styled.div` 5 | @media ${mq.small} { 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | justify-content: center; 10 | } 11 | @media ${mq.mediumLarge} { 12 | display: inline-flex; 13 | vertical-align: middle; 14 | } 15 | ` 16 | 17 | export default ButtonGroup 18 | -------------------------------------------------------------------------------- /src/stylesheets/screen.scss: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Variables 4 | 5 | */ 6 | @import '_normalize.scss'; 7 | @import 'to-remove/_colors.scss'; 8 | @import 'to-remove/_variables.scss'; 9 | @import 'to-remove/_breakpoints.scss'; 10 | @import 'to-remove/_typography.scss'; 11 | 12 | /* 13 | 14 | Global 15 | 16 | */ 17 | @import '_global.scss'; 18 | 19 | /* 20 | 21 | Elements 22 | 23 | */ 24 | @import '_logo.scss'; 25 | @import '_features.scss'; 26 | 27 | /* 28 | 29 | Charts 30 | 31 | */ 32 | 33 | @import '_charts.scss'; 34 | -------------------------------------------------------------------------------- /src/core/charts/generic/UsageVariationsChart/multiple-diverging-lines/NegativePattern.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PatternLines } from '@nivo/core' 3 | 4 | interface NegativePatternProps { 5 | id: string 6 | color: string 7 | } 8 | 9 | export const NegativePattern = ({ id, color }: NegativePatternProps) => ( 10 | 18 | ) 19 | -------------------------------------------------------------------------------- /src/core/pages/PageMeta.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Helmet from 'react-helmet' 3 | import { usePageContext } from '../helpers/pageContext' 4 | import { useI18n } from '../i18n/i18nContext' 5 | import { getPageSocialMeta } from '../helpers/pageHelpers' 6 | 7 | const PageMeta = ({ overrides = {} }) => { 8 | const context = usePageContext() 9 | const { translate } = useI18n() 10 | 11 | const meta = getPageSocialMeta(context, translate, overrides) 12 | 13 | return 14 | } 15 | 16 | export default PageMeta 17 | -------------------------------------------------------------------------------- /src/core/pages/PageLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Link from 'core/components/LocaleLink' 4 | 5 | const PageLink = ({ page, children, className, activeClassName, onClick }) => ( 6 | 7 | {children} 8 | 9 | ) 10 | 11 | PageLink.propTypes = { 12 | page: PropTypes.shape({ 13 | path: PropTypes.string.isRequired, 14 | }).isRequired, 15 | } 16 | 17 | export default PageLink 18 | -------------------------------------------------------------------------------- /surveys/js2020/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | import colors from './colors' 3 | import dimensions from './dimensions' 4 | import typography from './typography' 5 | import charts from './charts' 6 | import zIndexes from './zIndexes' 7 | 8 | const stateOfJSTheme: DefaultTheme = { 9 | typography, 10 | dimensions, 11 | colors, 12 | charts, 13 | zIndexes, 14 | separationBorder: `1px solid ${colors.border}`, 15 | blockShadow: `0px 6px 16px rgba(0, 0, 0, 0.3), 0px 2px 6px rgba(0, 0, 0, 0.2)`, 16 | } 17 | 18 | export default stateOfJSTheme -------------------------------------------------------------------------------- /surveys/css2020/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | import colors from './colors' 3 | import dimensions from './dimensions' 4 | import typography from './typography' 5 | import charts from './charts' 6 | import zIndexes from './zIndexes' 7 | 8 | const stateOfCSSTheme: DefaultTheme = { 9 | dimensions, 10 | typography, 11 | colors, 12 | charts, 13 | zIndexes, 14 | separationBorder: `1px dashed ${colors.border}`, 15 | blockShadow: `0px 16px 24px rgba(0, 0, 0, 0.4), 0px 2px 6px rgba(0, 0, 0, 0.3)`, 16 | } 17 | 18 | export default stateOfCSSTheme -------------------------------------------------------------------------------- /surveys/css2021/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | import colors from './colors' 3 | import dimensions from './dimensions' 4 | import typography from './typography' 5 | import charts from './charts' 6 | import zIndexes from './zIndexes' 7 | 8 | const stateOfCSSTheme: DefaultTheme = { 9 | dimensions, 10 | typography, 11 | colors, 12 | charts, 13 | zIndexes, 14 | separationBorder: `1px dashed ${colors.border}`, 15 | blockShadow: `0px 16px 24px rgba(0, 0, 0, 0.4), 0px 2px 6px rgba(0, 0, 0, 0.3)`, 16 | } 17 | 18 | export default stateOfCSSTheme -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["./src/**/*"], 3 | "exclude": ["node_modules"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "jsx": "react", 7 | "skipLibCheck": true, 8 | "allowSyntheticDefaultImports": true, 9 | "esModuleInterop": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "typeRoots": ["node_modules/@types/", "./src/@types/", ".src/**/@types/"], 15 | "strict": true, 16 | "baseUrl": "./", 17 | "paths": { 18 | "core/*": ["./src/core/*"] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # State of JS 2020 report 2 | 3 | The repo for the [2020 State of JS survey](https://2020.stateofjs.com/) site. Powered by [Gatsby](https://www.gatsbyjs.org/). 4 | 5 | ## Setup 6 | 7 | 1. Create `.env` file at the root of this repo. 8 | 2. Add the following line*: `API_URL=http://api.stateofjs.com/graphql` 9 | 3. `yarn` 10 | 11 | ## Selecting a survey 12 | 13 | This monorepo contains all our survey results sites going forward. To activate a specific survey add `SURVEY=js2020` or `SURVEY=css2020` in `.env`. 14 | 15 | ## Running the app 16 | 17 | - `yarn dev` 18 | - `yarn dev:clean` to run after cleaning all Gatsby caches. 19 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import * as ReactGA from 'react-ga' 3 | import LayoutWrapper from 'core/layout/LayoutWrapper' 4 | import config from 'Config/config.yml' 5 | 6 | const { gaUAid } = config 7 | 8 | ReactGA.initialize(gaUAid) 9 | 10 | export const onRouteUpdate = ({ location }) => { 11 | ReactGA.pageview(location.pathname) 12 | } 13 | 14 | export const wrapPageElement = ({ element, props }) => { 15 | const { pageContext, ...rest } = props 16 | 17 | return ( 18 | 19 | {element} 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Captures 6 | # static/images/captures 7 | 8 | # Builds 9 | public 10 | # images are survey-dependent and copied over from surveys/foo/images by webpack 11 | static/images 12 | 13 | # Dependency directory 14 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 15 | node_modules 16 | 17 | # Cache 18 | .cache 19 | .sass-cache 20 | .gatsby-context.js 21 | 22 | # IDE 23 | .idea 24 | 25 | # OSX 26 | .DS_Store 27 | 28 | # Secrets 29 | .env 30 | 31 | # docker volumes 32 | esdata 33 | 34 | .env 35 | 36 | sitemap.yml 37 | blocks.yml 38 | 39 | .logs 40 | .logs/* -------------------------------------------------------------------------------- /src/core/charts/tools/ToolExperienceGraphChart/NodeTooltip.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useI18n } from 'core/i18n/i18nContext' 3 | 4 | const NodeTooltip = (node) => { 5 | const { translate } = useI18n() 6 | 7 | return ( 8 |
9 | {translate('toolExperienceGraph.node.tooltip', { 10 | values: { 11 | count: node.value, 12 | year: node.year, 13 | experience: translate(`toolExperience.${node.experience}.long`), 14 | }, 15 | })} 16 |
17 | ) 18 | } 19 | 20 | export default NodeTooltip 21 | -------------------------------------------------------------------------------- /src/core/charts/generic/UsageVariationsChart/multiple-diverging-lines/types.ts: -------------------------------------------------------------------------------- 1 | export interface PointDatum { 2 | index: string 3 | count: number 4 | percentage: number 5 | percentageDelta: number 6 | } 7 | 8 | export interface ComputedPoint { 9 | x: number 10 | y: number 11 | data: PointDatum 12 | } 13 | 14 | export interface Datum { 15 | id: string 16 | name: string 17 | baseline: number 18 | data: PointDatum[] 19 | } 20 | 21 | export interface ComputedDatum { 22 | id: string 23 | index: number 24 | name: string 25 | baseline: number 26 | color: string 27 | data: ComputedPoint[] 28 | } 29 | -------------------------------------------------------------------------------- /src/core/charts/generic/UsageVariationsChart/multiple-diverging-lines/ResponsiveMultipleDivergingLines.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ResponsiveWrapper } from '@nivo/core' 3 | import { MultipleDivergingLines, MultipleDivergingLinesSvgProps } from './MultipleDivergingLines' 4 | 5 | export const ResponsiveMultipleDivergingLines = ( 6 | props: Omit 7 | ) => ( 8 | 9 | {({ width, height }: { width: number; height: number }) => ( 10 | 11 | )} 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /surveys/css2020/config/authors.yml: -------------------------------------------------------------------------------- 1 | - name: Raphaël Benitte 2 | slug: raphael 3 | avatar: https://avatars2.githubusercontent.com/u/488689?v=3&s=400 4 | url: https://github.com/plouc/ 5 | bio: > 6 | Creator of the [Nivo](http://nivo.rocks/) JavaScript data visualization library as well 7 | as [Mozaik](http://mozaik.rocks/), a tool for building beautiful dashboards. 8 | 9 | - name: Sacha Greif 10 | slug: sacha 11 | avatar: sacha.jpg 12 | url: http://sachagreif.com 13 | bio: > 14 | Creator of the [Sidebar](http://sidebar.io) daily newsletter and 15 | [VulcanJS](http://vulcanjs.org), a React+GraphQL open-source framework. -------------------------------------------------------------------------------- /surveys/css2021/config/authors.yml: -------------------------------------------------------------------------------- 1 | - name: Raphaël Benitte 2 | slug: raphael 3 | avatar: https://avatars2.githubusercontent.com/u/488689?v=3&s=400 4 | url: https://github.com/plouc/ 5 | bio: > 6 | Creator of the [Nivo](http://nivo.rocks/) JavaScript data visualization library as well 7 | as [Mozaik](http://mozaik.rocks/), a tool for building beautiful dashboards. 8 | 9 | - name: Sacha Greif 10 | slug: sacha 11 | avatar: sacha.jpg 12 | url: http://sachagreif.com 13 | bio: > 14 | Creator of the [Sidebar](http://sidebar.io) daily newsletter and 15 | [VulcanJS](http://vulcanjs.org), a React+GraphQL open-source framework. -------------------------------------------------------------------------------- /src/core/report/ReportLayout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, { css } from 'styled-components' 3 | import { mq, spacing } from 'core/theme' 4 | 5 | const EssayLayout = ({ props }) => { 6 | return ( 7 | 8 | 9 | {/* */} 10 | {props.children} 11 | 12 | 13 | ) 14 | } 15 | 16 | const PageContent = styled.main` 17 | max-width: 800px; 18 | margin: 0 auto; 19 | ` 20 | 21 | const Page = styled.div` 22 | 23 | ` 24 | 25 | const PageMain = styled.main` 26 | 27 | ` 28 | 29 | export default EssayLayout 30 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolsExperienceMarimekkoBlock/types.ts: -------------------------------------------------------------------------------- 1 | import { Entity } from '../../../types' 2 | 3 | export interface ToolExperienceBucket { 4 | id: string 5 | count: number 6 | percentage: number 7 | } 8 | 9 | export interface ToolsExperienceToolData { 10 | id: string 11 | entity: Entity 12 | experience: { 13 | year: { 14 | total: number 15 | buckets: ToolExperienceBucket[] 16 | } 17 | } 18 | } 19 | 20 | export interface ToolsExperienceMarimekkoToolData { 21 | tool: Entity 22 | awareness: number 23 | would_not_use: number 24 | not_interested: number 25 | interested: number 26 | would_use: number 27 | } 28 | -------------------------------------------------------------------------------- /surveys/js2020/theme/typography.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | 3 | const stateOfJSThemeTypography: DefaultTheme['typography'] = { 4 | fontFamily: `'IBM Plex Mono', monospace`, 5 | rootSize: { 6 | mobile: '13px', 7 | desktop: '17px', 8 | }, 9 | size: { 10 | smaller: '0.7rem', 11 | small: '0.8rem', 12 | smallish: '0.9rem', 13 | medium: '1rem', 14 | large: '1.1rem', 15 | larger: '1.3rem', 16 | largest: '2rem', 17 | huge: '4rem', 18 | }, 19 | weight: { 20 | light: 300, 21 | medium: 400, 22 | bold: 800, 23 | }, 24 | } 25 | 26 | export default stateOfJSThemeTypography 27 | -------------------------------------------------------------------------------- /surveys/css2020/theme/typography.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | 3 | const stateOfCSSThemeTypography: DefaultTheme['typography'] = { 4 | fontFamily: `'IBM Plex Mono', monospace`, 5 | rootSize: { 6 | mobile: '13px', 7 | desktop: '17px', 8 | }, 9 | size: { 10 | smaller: '0.7rem', 11 | small: '0.8rem', 12 | smallish: '0.9rem', 13 | medium: '1rem', 14 | large: '1.1rem', 15 | larger: '1.3rem', 16 | largest: '2rem', 17 | huge: '4rem', 18 | }, 19 | weight: { 20 | light: 300, 21 | medium: 400, 22 | bold: 800, 23 | }, 24 | } 25 | 26 | export default stateOfCSSThemeTypography 27 | -------------------------------------------------------------------------------- /surveys/css2021/theme/typography.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | 3 | const stateOfCSSThemeTypography: DefaultTheme['typography'] = { 4 | fontFamily: `'IBM Plex Mono', monospace`, 5 | rootSize: { 6 | mobile: '13px', 7 | desktop: '17px', 8 | }, 9 | size: { 10 | smaller: '0.7rem', 11 | small: '0.8rem', 12 | smallish: '0.9rem', 13 | medium: '1rem', 14 | large: '1.1rem', 15 | larger: '1.3rem', 16 | largest: '2rem', 17 | huge: '4rem', 18 | }, 19 | weight: { 20 | light: 300, 21 | medium: 400, 22 | bold: 800, 23 | }, 24 | } 25 | 26 | export default stateOfCSSThemeTypography 27 | -------------------------------------------------------------------------------- /src/core/charts/generic/HorizontalBarStripes.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react' 2 | import { useTheme } from 'styled-components' 3 | 4 | const HorizontalBarStripes = ({ bars, width, yScale }) => { 5 | const theme = useTheme() 6 | 7 | const step = yScale.step() 8 | 9 | return bars.map((bar, i) => { 10 | if (i % 2 !== 0) return null 11 | 12 | return ( 13 | 20 | ) 21 | }) 22 | } 23 | 24 | export default memo(HorizontalBarStripes) 25 | -------------------------------------------------------------------------------- /surveys/css2020/config/sponsors.yml: -------------------------------------------------------------------------------- 1 | - name: Frontend Masters 2 | id: frontendmasters 3 | url: https://frontendmasters.com/ 4 | image: frontend-masters-dark.png 5 | description: > 6 | Advance your skills with in-depth, modern front-end engineering courses. 7 | # - name: Newline 8 | # id: newline 9 | # url: https://www.newline.co/ 10 | # image: newline-logo-dark.png 11 | # - name: Chroma 12 | # id: chroma 13 | # url: https://www.hichroma.com/ 14 | # image: chroma_logo.svg 15 | # - name: ZensHome 16 | # id: zenshome 17 | # url: https://zenshome.jp 18 | # image: zenshome-whitebg.svg 19 | # - name: Design+Code 20 | # id: designcode 21 | # url: https://designcode.io/ 22 | # image: designcode.svg 23 | -------------------------------------------------------------------------------- /surveys/css2021/config/sponsors.yml: -------------------------------------------------------------------------------- 1 | - name: Frontend Masters 2 | id: frontendmasters 3 | url: https://frontendmasters.com/ 4 | image: frontend-masters-dark.png 5 | description: > 6 | Advance your skills with in-depth, modern front-end engineering courses. 7 | # - name: Newline 8 | # id: newline 9 | # url: https://www.newline.co/ 10 | # image: newline-logo-dark.png 11 | # - name: Chroma 12 | # id: chroma 13 | # url: https://www.hichroma.com/ 14 | # image: chroma_logo.svg 15 | # - name: ZensHome 16 | # id: zenshome 17 | # url: https://zenshome.jp 18 | # image: zenshome-whitebg.svg 19 | # - name: Design+Code 20 | # id: designcode 21 | # url: https://designcode.io/ 22 | # image: designcode.svg 23 | -------------------------------------------------------------------------------- /src/core/blocks/other/PageIntroductionBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { mq, spacing } from 'core/theme' 4 | import T from 'core/i18n/T' 5 | 6 | const PageIntroductionBlock = ({ block }) => ( 7 | 8 | 9 | 10 | ) 11 | 12 | const Introduction = styled.div` 13 | @media ${mq.smallMedium} { 14 | margin-bottom: ${spacing(2)}; 15 | } 16 | 17 | @media ${mq.large} { 18 | font-size: ${(props) => props.theme.typography.size.large}; 19 | margin-bottom: ${spacing(4)}; 20 | } 21 | ` 22 | 23 | export default PageIntroductionBlock 24 | -------------------------------------------------------------------------------- /src/core/charts/demographics/ParticipationByCountryTooltip.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { BasicTooltip } from '@nivo/tooltip' 4 | 5 | const ParticipationByCountryTooltip = ({ feature }) => { 6 | if (feature.data === undefined) return null 7 | return ( 8 | 14 | ) 15 | } 16 | 17 | ParticipationByCountryTooltip.propTypes = { 18 | feature: PropTypes.object.isRequired, 19 | } 20 | 21 | export default memo(ParticipationByCountryTooltip) 22 | -------------------------------------------------------------------------------- /src/core/share/ShareBlockDebug.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { usePageContext } from 'core/helpers/pageContext' 4 | import { useI18n } from 'core/i18n/i18nContext' 5 | import { getBlockMeta } from 'core/helpers/blockHelpers' 6 | import Debug from '../components/Debug' 7 | 8 | const ShareBlockDebug = ({ block }) => { 9 | const context = usePageContext() 10 | const { translate } = useI18n() 11 | 12 | if (!context.isDebugEnabled) return null 13 | 14 | const meta = getBlockMeta(block, context, translate) 15 | 16 | return 17 | } 18 | 19 | ShareBlockDebug.propTypes = { 20 | block: PropTypes.object.isRequired, 21 | } 22 | 23 | export default ShareBlockDebug 24 | -------------------------------------------------------------------------------- /src/core/report/ReportSectionLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Button from 'core/components/Button' 3 | import styled from 'styled-components' 4 | import { mq, spacing, fontSize } from 'core/theme' 5 | import Link from 'core/components/LocaleLink' 6 | 7 | const EssaySectionLink = ({ path, children }) => ( 8 | 9 | 10 | {children} 11 | 12 | 13 | ) 14 | 15 | const SectionLinkWrapper = styled.div` 16 | margin-bottom: ${spacing(2)}; 17 | display: flex; 18 | justify-content: center; 19 | ` 20 | 21 | const SectionLink = styled(Button)`` 22 | 23 | export default EssaySectionLink 24 | -------------------------------------------------------------------------------- /surveys/js2020/logo/SidebarLogo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, { useTheme } from 'styled-components' 3 | import { LogoCell } from './LogoCell' 4 | 5 | export const SidebarLogo = () => { 6 | const theme = useTheme() 7 | 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | ) 16 | } 17 | 18 | const Container = styled.span` 19 | display: grid; 20 | grid-template-columns: 1fr 1fr 1fr 1fr; 21 | ` 22 | 23 | export default SidebarLogo -------------------------------------------------------------------------------- /scripts/patch-react-spring.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | const packagesToPatch = [ 5 | 'animated', 6 | 'core', 7 | 'konva', 8 | 'native', 9 | 'shared', 10 | 'three', 11 | 'web', 12 | 'zdog', 13 | ] 14 | 15 | packagesToPatch.forEach(patchPackage) 16 | 17 | function patchPackage(package) { 18 | const packageJsonPath = path.join( 19 | 'node_modules', 20 | '@react-spring', 21 | package, 22 | 'package.json', 23 | ) 24 | const packageJson = fs.readFileSync(packageJsonPath, 'utf-8') 25 | const modifiedPackageJson = packageJson.replace( 26 | '"sideEffects": false,', 27 | '', 28 | ) 29 | fs.writeFileSync(packageJsonPath, modifiedPackageJson, { 30 | encoding: 'utf-8', 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /src/core/charts/tools/ToolExperienceGraphChart/LinkTooltip.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useI18n } from 'core/i18n/i18nContext' 3 | 4 | const LinkTooltip = (link) => { 5 | const { translate } = useI18n() 6 | 7 | return ( 8 |
9 | {translate('toolExperienceGraph.link.tooltip', { 10 | values: { 11 | count: link.value, 12 | previousYear: link.source.year, 13 | previousExperience: translate(`toolExperience.${link.source.experience}.short`), 14 | nextYear: link.target.year, 15 | nextExperience: translate(`toolExperience.${link.target.experience}.short`), 16 | }, 17 | })} 18 |
19 | ) 20 | } 21 | 22 | export default LinkTooltip 23 | -------------------------------------------------------------------------------- /src/core/i18n/wording.js: -------------------------------------------------------------------------------- 1 | import get from 'lodash/get' 2 | import template from 'lodash/template' 3 | import wording from 'translations/__old/en-US.yml' 4 | import bestOfJsData from 'data/bestofjs' 5 | 6 | export const getWording = (id, values, fallback = id) => { 7 | const label = get(wording, id) 8 | 9 | if (label === undefined) return fallback 10 | if (values === undefined) return label 11 | 12 | try { 13 | return template(label)(values) 14 | } catch (error) { 15 | // console.error(error) 16 | return label 17 | } 18 | } 19 | 20 | export const getToolName = (toolId) => { 21 | const bestOfJsProject = bestOfJsData.projects.find((p) => p.slug === toolId) 22 | if (bestOfJsProject !== undefined) { 23 | return bestOfJsProject.name 24 | } 25 | 26 | return getWording(`tools.${toolId}`) 27 | } 28 | -------------------------------------------------------------------------------- /surveys/css2020/config/config.yml: -------------------------------------------------------------------------------- 1 | translationContexts: [common, results, state_of_css, state_of_css_2020] 2 | socialMediaImage: stateofcss2020_socialmedia.png 3 | hashtag: '#StateOfCSS' 4 | siteContext: stateofcss 5 | siteTitle: The State of CSS 2020 6 | siteUrl: https://2020.stateofcss.com 7 | emailOctopusUrl: https://emailoctopus.com/lists/ed0386c4-2f55-11e9-a3c9-06b79b628af2/members/embedded/1.3/add 8 | emailOctopusSiteKey: 6LdYsmsUAAAAAPXVTt-ovRsPIJ_IVhvYBBhGvRV6 9 | emailOctopusCode: hpc4b27b6e-eb38-11e9-be00-06b4694bee2a 10 | translationLink: https://github.com/StateOfJS/locale-en-US 11 | capturesUrl: https://stateofx-images.netlify.app/captures/css2020 12 | discordUrl: https://discord.gg/zRDb35jfrt 13 | issuesUrl: https://github.com/StateOfJS/StateOfCSS-2020 14 | 15 | tshirtImages: ['tshirt5.jpg', 'tshirt6.jpg', 'tshirt4.png'] 16 | tshirtLink: https://gumroad.com/l/stateofcss-tshirt 17 | tshirtPrice: 24 18 | 19 | -------------------------------------------------------------------------------- /surveys/css2021/config/config.yml: -------------------------------------------------------------------------------- 1 | translationContexts: [common, results, state_of_css, state_of_css_2021] 2 | socialMediaImage: stateofcss2021_socialmedia.png 3 | hashtag: '#StateOfCSS' 4 | siteContext: stateofcss 5 | siteTitle: The State of CSS 2021 6 | siteUrl: https://2021.stateofcss.com 7 | emailOctopusUrl: https://emailoctopus.com/lists/ed0386c4-2f55-11e9-a3c9-06b79b628af2/members/embedded/1.3/add 8 | emailOctopusSiteKey: 6LdYsmsUAAAAAPXVTt-ovRsPIJ_IVhvYBBhGvRV6 9 | emailOctopusCode: hpc4b27b6e-eb38-11e9-be00-06b4694bee2a 10 | translationLink: https://github.com/StateOfJS/locale-en-US 11 | capturesUrl: https://stateofx-images.netlify.app/captures/css2021 12 | discordUrl: https://discord.gg/zRDb35jfrt 13 | issuesUrl: https://github.com/StateOfJS/StateOfCSS-2020 14 | 15 | tshirtImages: ['tshirt5.jpg', 'tshirt6.jpg', 'tshirt4.png'] 16 | tshirtLink: https://gumroad.com/l/stateofcss-tshirt 17 | tshirtPrice: 24 18 | 19 | -------------------------------------------------------------------------------- /src/core/survey_api/tools.ts: -------------------------------------------------------------------------------- 1 | import { Entity } from 'core/types' 2 | 3 | export interface ToolExperienceBucket { 4 | id: string 5 | count: number 6 | percentage: number 7 | } 8 | 9 | export interface ToolsExperienceToolData { 10 | id: string 11 | entity: Entity 12 | experience: { 13 | year: { 14 | total: number 15 | buckets: ToolExperienceBucket[] 16 | } 17 | } 18 | } 19 | 20 | export interface ToolYearExperience { 21 | year: number 22 | total: number 23 | buckets: ToolExperienceBucket[] 24 | } 25 | 26 | export interface ToolAllYearsExperience { 27 | id: string 28 | entity: Entity 29 | experience: { 30 | all_years: ToolYearExperience[] 31 | } 32 | } 33 | 34 | // For `tools_cardinality_by_user` 35 | export interface ToolsCardinalityByUserBucket { 36 | cardinality: number 37 | count: number 38 | percentage: number 39 | } -------------------------------------------------------------------------------- /src/core/pages/PageTemplate.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PageHeader from 'core/pages/PageHeader' 3 | import PageFooter from 'core/pages/PageFooter' 4 | import { usePageContext } from 'core/helpers/pageContext' 5 | import BlockSwitcher from 'core/blocks/block/BlockSwitcher' 6 | 7 | const PageTemplate = ({ pageContext = {} }) => { 8 | const context = usePageContext() 9 | const { pageData, showTitle = true, is_hidden = false } = pageContext 10 | return ( 11 | <> 12 | {showTitle && } 13 |
14 | {context.blocks && 15 | context.blocks.map((block, i) => ( 16 | 17 | ))} 18 |
19 | {!is_hidden && } 20 | 21 | ) 22 | } 23 | 24 | export default PageTemplate 25 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | module.exports = { 4 | siteMetadata: { 5 | title: `State Of JS 2020`, 6 | }, 7 | plugins: [ 8 | 'gatsby-transformer-yaml', 9 | { 10 | resolve: 'gatsby-source-filesystem', 11 | options: { 12 | name: `data`, 13 | path: `${__dirname}/src/data/`, 14 | }, 15 | }, 16 | { 17 | resolve: 'gatsby-source-graphql', 18 | options: { 19 | typeName: 'SurveyApi', 20 | fieldName: 'surveyApi', 21 | url: process.env.API_URL, 22 | }, 23 | }, 24 | 'gatsby-plugin-react-helmet', 25 | 'gatsby-plugin-sass', 26 | { resolve: 'gatsby-plugin-netlify', options: {} }, 27 | 'gatsby-plugin-styled-components', 28 | `gatsby-plugin-mdx`, 29 | // 'gatsby-plugin-webpack-bundle-analyzer', 30 | ], 31 | } 32 | -------------------------------------------------------------------------------- /src/core/components/Debug.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const Debug = ({ title, data }) => ( 5 |
6 |
[debug] {title}
7 |
8 | {Object.keys(data).map((key) => { 9 | let value = data[key] 10 | if (value !== undefined && value !== null && value.indexOf('http') === 0) { 11 | value = {value} 12 | } 13 | return ( 14 |
15 | {key}: {value === undefined ? undefined : {value}} 16 |
17 | ) 18 | })} 19 |
20 |
21 | ) 22 | 23 | Debug.propTypes = { 24 | title: PropTypes.string.isRequired, 25 | data: PropTypes.object.isRequired, 26 | } 27 | 28 | export default Debug 29 | -------------------------------------------------------------------------------- /src/core/theme/mq.js: -------------------------------------------------------------------------------- 1 | const breakpoints = { 2 | xSmall: 600, 3 | small: 600, 4 | medium: 1000, 5 | xLarge: 1200, 6 | } 7 | 8 | export default { 9 | breakpoints, 10 | // smaller than x-small-break 11 | xSmall: `screen and (max-width: ${breakpoints.xSmall - 1}px)`, 12 | // smaller than small-break 13 | small: `screen and (max-width: ${breakpoints.small - 1}px)`, 14 | // smaller than medium-break 15 | smallMedium: `screen and (max-width: ${breakpoints.medium - 1}px)`, 16 | // between small and medium-break 17 | medium: `screen and (min-width: ${breakpoints.small}px) and (max-width: ${ 18 | breakpoints.medium - 1 19 | }px)`, 20 | // larger than small-break 21 | mediumLarge: `screen and (min-width: ${breakpoints.small}px)`, 22 | // larger than medium-break 23 | large: `screen and (min-width: ${breakpoints.medium}px)`, 24 | // larger than large-break 25 | xLarge: `screen and (min-width: ${breakpoints.xLarge}px)`, 26 | } 27 | -------------------------------------------------------------------------------- /src/core/blocks/types.ts: -------------------------------------------------------------------------------- 1 | export interface BlockContext< 2 | BlockTemplate, 3 | BlockType, 4 | PageVariables = unknown, 5 | BlockVariables = unknown 6 | > { 7 | // unique identifier of the block, should be unique 8 | // for the whole survey 9 | id: string 10 | blockName: string 11 | // define the block implementation to use, defined in 12 | // `/src/core/helpers/blockRegistry.js` 13 | blockType: BlockType 14 | // URI of the block 15 | path: string 16 | // Unique identifier for the block's page 17 | pageId: string 18 | // GraphQL query for the block 19 | query: string 20 | // Template of the block, available templates being defined 21 | // in `/config/block_templates.yml` 22 | template: BlockTemplate 23 | // Injected variables for the block's page 24 | pageVariables: PageVariables 25 | // Injected variables for the block 26 | variables: BlockVariables 27 | enableExport: boolean 28 | showLegend: boolean 29 | } 30 | -------------------------------------------------------------------------------- /src/core/pages/IntroductionFooter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { usePageContext } from 'core/helpers/pageContext' 4 | import { useI18n } from 'core/i18n/i18nContext' 5 | import Link from 'core/components/LocaleLink' 6 | import Button from 'core/components/Button' 7 | import { spacing } from 'core/theme' 8 | 9 | const IntroductionFooter = () => { 10 | const context = usePageContext() 11 | const { translate } = useI18n() 12 | 13 | return ( 14 | 15 | 23 | 24 | ) 25 | } 26 | 27 | const Container = styled.div` 28 | margin: ${spacing(2)} 0; 29 | ` 30 | 31 | export default IntroductionFooter 32 | -------------------------------------------------------------------------------- /surveys/js2020/config/config.yml: -------------------------------------------------------------------------------- 1 | translationContexts: [common, results, state_of_js, state_of_js_2020] 2 | socialMediaImage: stateofjs2020_socialmedia.png 3 | hashtag: '#StateOfJS' 4 | year: 2020 5 | siteContext: stateofjs 6 | siteTitle: State of JS 2020 7 | siteUrl: https://2020.stateofjs.com 8 | emailOctopusUrl: https://emailoctopus.com/lists/ed0386c4-2f55-11e9-a3c9-06b79b628af2/members/embedded/1.3/add 9 | emailOctopusSiteKey: 6LdYsmsUAAAAAPXVTt-ovRsPIJ_IVhvYBBhGvRV6 10 | emailOctopusCode: hpc4b27b6e-eb38-11e9-be00-06b4694bee2a 11 | translationLink: https://github.com/StateOfJS/locale-en-US 12 | capturesUrl: https://stateofx-images.netlify.app/captures/js2020 13 | discordUrl: https://discord.gg/zRDb35jfrt 14 | issuesUrl: https://github.com/StateOfJS/StateOfJS-2020 15 | tshirtImages: ['stateofjs2019tshirt1.jpg', 'stateofjs2019tshirt2.jpg', 'stateofjs2020-tshirt-export.png'] 16 | tshirtLink: https://cottonbureau.com/products/state-of-js-2020-edition 17 | tshirtPrice: 29 18 | gaUAid: UA-83022212-9 19 | gaNewid: G-24J0W9RDC8 -------------------------------------------------------------------------------- /src/core/i18n/i18nContext.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useMemo } from 'react' 2 | import { getTranslator, getStringTranslator } from './translator' 3 | import { usePageContext } from '../helpers/pageContext' 4 | 5 | export const I18nContext = createContext() 6 | 7 | const I18nContextProviderInner = ({ children }) => { 8 | const context = usePageContext() 9 | const { locale = {} } = context 10 | const translate = getTranslator(locale) 11 | const getString = getStringTranslator(locale) 12 | 13 | const value = useMemo( 14 | () => ({ 15 | locale, 16 | translate, 17 | getString, 18 | }), 19 | [locale, translate, getString] 20 | ) 21 | 22 | return {children} 23 | } 24 | 25 | export const I18nContextProvider = ({ children }) => { 26 | return {children} 27 | } 28 | 29 | export const useI18n = () => useContext(I18nContext) 30 | -------------------------------------------------------------------------------- /src/core/blocks/other/ConclusionBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { mq, spacing, fontSize } from 'core/theme' 4 | import T from 'core/i18n/T' 5 | 6 | const ConclusionBlock = () => { 7 | return ( 8 | 9 | 10 | <T k="sections.conclusion.title" /> 11 | 12 | 13 | 14 | ) 15 | } 16 | 17 | const Title = styled.h2` 18 | font-size: ${fontSize('largest')}; 19 | ` 20 | const Conclusion = styled.div` 21 | @media ${mq.large} { 22 | max-width: 700px; 23 | margin: 0 auto; 24 | margin-bottom: ${spacing(4)}; 25 | } 26 | .block__content { 27 | p:first-child { 28 | @media ${mq.mediumLarge} { 29 | max-width: 700px; 30 | font-size: $larger-font; 31 | } 32 | } 33 | } 34 | ` 35 | 36 | export default ConclusionBlock 37 | -------------------------------------------------------------------------------- /surveys/js2020/config/sponsors.yml: -------------------------------------------------------------------------------- 1 | - name: Idera 2 | id: idera 3 | url: https://www.ideracorp.com/developertools 4 | image: IderaInc-DevTools-Reversed_dark_v1.svg 5 | description: > 6 | Industry leader of high productivity developer tools for app development using 7 | powerful and scalable JavaScript framework and components. 8 | 9 | - name: Frontend Masters 10 | id: frontendmasters 11 | url: https://frontendmasters.com/ 12 | image: frontend-masters-dark.png 13 | description: > 14 | Advance your skills with in-depth, modern front-end engineering courses. 15 | 16 | # - name: Newline 17 | # id: newline 18 | # url: https://www.newline.co/ 19 | # image: newline-logo-dark.png 20 | # - name: Chroma 21 | # id: chroma 22 | # url: https://www.hichroma.com/ 23 | # image: chroma_logo.svg 24 | # - name: ZensHome 25 | # id: zenshome 26 | # url: https://zenshome.jp 27 | # image: zenshome-whitebg.svg 28 | # - name: Design+Code 29 | # id: designcode 30 | # url: https://designcode.io/ 31 | # image: designcode.svg 32 | -------------------------------------------------------------------------------- /src/core/blocks/other/SurveyIntroBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import IntroductionFooter from 'core/pages/IntroductionFooter' 3 | import T from 'core/i18n/T' 4 | import styled from 'styled-components' 5 | import { mq } from 'core/theme' 6 | import IntroLogo from 'Logo/IntroLogo' 7 | 8 | const SurveyIntroBlock = () => ( 9 | <> 10 | 11 |
12 | 13 | 14 | 15 | 16 |
17 | 18 | ) 19 | 20 | const Content = styled.div` 21 | @media ${mq.large} { 22 | max-width: 700px; 23 | margin: 0 auto; 24 | } 25 | .block__content { 26 | p:first-child { 27 | @media ${mq.mediumLarge} { 28 | max-width: 700px; 29 | font-size: $larger-font; 30 | } 31 | } 32 | } 33 | ` 34 | 35 | export default SurveyIntroBlock 36 | -------------------------------------------------------------------------------- /src/core/pages/PageHeader.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styled from 'styled-components' 4 | import { usePageContext } from '../helpers/pageContext' 5 | import { getPageLabelKey } from '../helpers/pageHelpers' 6 | import { mq } from 'core/theme' 7 | import T from 'core/i18n/T' 8 | 9 | const PageHeader = () => { 10 | const page = usePageContext() 11 | return ( 12 | 13 | 14 | 15 | ) 16 | } 17 | 18 | const PageTitle = styled.h2` 19 | font-size: ${(props) => props.theme.typography.size.largest}; 20 | @media ${mq.small} { 21 | font-size: ${(props) => props.theme.typography.size.larger}; 22 | } 23 | @media ${mq.mediumLarge} { 24 | font-size: ${(props) => props.theme.typography.size.largest}; 25 | } 26 | ` 27 | 28 | PageHeader.propTypes = { 29 | title: PropTypes.string, 30 | showIntro: PropTypes.bool, 31 | introduction: PropTypes.node, 32 | } 33 | 34 | export default PageHeader 35 | -------------------------------------------------------------------------------- /src/stylesheets/to-remove/_typography.scss: -------------------------------------------------------------------------------- 1 | $root-size-mobile: 13px; 2 | $root-size: 17px; 3 | 4 | $smaller-font: 0.7rem; 5 | $small-font: 0.8rem; 6 | $smallish-font: 0.9rem; 7 | $medium-font: 1rem; 8 | $large-font: 1.1rem; 9 | $larger-font: 1.3rem; 10 | $largest-font: 2rem; 11 | 12 | $light: 300; // not used currently 13 | $medium: 400; 14 | $bold: 800; 15 | 16 | @mixin font-light { 17 | font-weight: $light; 18 | } 19 | 20 | @mixin font-regular { 21 | font-weight: $medium; 22 | } 23 | 24 | @mixin font-bold { 25 | font-weight: $bold; 26 | } 27 | 28 | @mixin primary-font { 29 | font-family: 'IBM Plex Mono', monospace; 30 | font-weight: $medium; 31 | } 32 | @mixin secondary-font { 33 | font-family: 'IBM Plex Mono', monospace; 34 | font-weight: $bold; 35 | } 36 | 37 | pre, 38 | code { 39 | @include secondary-font; 40 | } 41 | 42 | strong { 43 | font-weight: $bold; 44 | } 45 | 46 | .first-letter { 47 | float: left; 48 | display: block; 49 | font-size: 7rem; 50 | margin-right: $spacing; 51 | line-height: 1; 52 | } -------------------------------------------------------------------------------- /src/core/blocks/other/TextBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | // import ReactMarkdown from 'react-markdown' 4 | // import rehypeRaw from 'rehype-raw' 5 | 6 | import { spacing } from 'core/theme' 7 | 8 | const Content = styled.div` 9 | li { 10 | margin-bottom: ${spacing(0.5)}; 11 | } 12 | ` 13 | 14 | const TextBlock = ({ className, text, title, children }) => { 15 | const cssClass = `block block--text ${className}` 16 | if (children) { 17 | return
{children}
18 | } else { 19 | return ( 20 |
21 | {title &&

{title}

} 22 | {text && ( 23 | 24 | {/* {text} */} 25 | {text} 26 | 27 | )} 28 |
29 | ) 30 | } 31 | } 32 | 33 | export default TextBlock 34 | -------------------------------------------------------------------------------- /src/core/helpers/useBucketKeys.js: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | import { useTheme } from 'styled-components' 3 | import { keys } from 'core/bucket_keys' 4 | import { useI18n } from 'core/i18n/i18nContext' 5 | 6 | export const useBucketKeys = (bucketKeysId) => { 7 | const theme = useTheme() 8 | const { translate } = useI18n() 9 | 10 | const keysConfig = keys[bucketKeysId] 11 | if (!keysConfig) { 12 | throw new Error(`Could not find bucket keys config for: "${bucketKeysId}"`) 13 | } 14 | 15 | return useMemo(() => { 16 | let colorRange 17 | if (keysConfig.colorRange) { 18 | colorRange = theme.colors.ranges[keysConfig.colorRange] 19 | } 20 | 21 | return keysConfig.keys.map((key) => { 22 | return { 23 | id: key.id, 24 | label: translate(key.label), 25 | shortLabel: key.shortLabel ? translate(key.shortLabel) : undefined, 26 | color: colorRange ? colorRange[key.id] : undefined, 27 | } 28 | }) 29 | }, [keysConfig, theme, translate]) 30 | } 31 | -------------------------------------------------------------------------------- /src/core/pages/PageMetaDebug.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { usePageContext } from '../helpers/pageContext' 3 | import { useI18n } from '../i18n/i18nContext' 4 | import Debug from '../components/Debug' 5 | import { getPageSocialMeta } from '../helpers/pageHelpers' 6 | // import { useTools } from 'core/helpers/toolsContext' 7 | 8 | const PageMetaDebug = ({ overrides = {} }) => { 9 | const context = usePageContext() 10 | const { translate } = useI18n() 11 | // const { getToolName } = useTools() 12 | 13 | if (!context.isDebugEnabled) return null 14 | 15 | // const toolName = getToolName(context) 16 | // if (toolName) { 17 | // overrides.title = `${websiteTitle}: ${toolName}` 18 | // } 19 | 20 | const meta = getPageSocialMeta(context, translate, overrides) 21 | const metaObject = meta.reduce((acc, meta) => { 22 | const key = meta.property || meta.name 23 | 24 | return { 25 | ...acc, 26 | [key]: meta.content, 27 | } 28 | }, {}) 29 | 30 | return 31 | } 32 | 33 | export default PageMetaDebug 34 | -------------------------------------------------------------------------------- /src/core/blocks/other/NewsletterBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import Newsletter from 'core/components/Newsletter' 4 | import { useI18n } from 'core/i18n/i18nContext' 5 | import { spacing } from 'core/theme' 6 | 7 | const NewsletterBlock = () => { 8 | const { translate } = useI18n() 9 | 10 | return ( 11 | 12 | {translate('blocks.newsletter.title')} 13 | {translate('blocks.newsletter.description')} 14 | 15 | 16 | ) 17 | } 18 | 19 | const Container = styled.div` 20 | padding: ${spacing(1.5)}; 21 | margin-bottom: ${spacing(2)}; 22 | border: ${(props) => props.theme.separationBorder}; 23 | max-width: 700px; 24 | margin-left: auto; 25 | margin-right: auto; 26 | ` 27 | 28 | const Heading = styled.h3` 29 | margin-bottom: ${spacing(0.5)}; 30 | font-size: ${(props) => props.theme.typography.size.larger}; 31 | ` 32 | 33 | const Description = styled.div` 34 | margin-bottom: ${spacing()}; 35 | ` 36 | 37 | export default NewsletterBlock 38 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolsArrowsBlock/helpers.js: -------------------------------------------------------------------------------- 1 | import { scaleLinear } from 'd3-scale' 2 | 3 | /* 4 | 5 | For a given set of points, get the total velocity between start and finish 6 | 7 | */ 8 | export const getVelocity = (points) => { 9 | const first = points[0] 10 | const last = points[points.length - 1] 11 | const vx = last[0] - first[0] 12 | const vy = last[1] - first[1] 13 | const v = vx + vy 14 | return v 15 | } 16 | 17 | /* 18 | 19 | For a given velocity and a theme, get the associated color 20 | 21 | */ 22 | export const getVelocityColor = (v, theme) => { 23 | const scale = theme.colors.velocity 24 | const scaleSteps = scale.length - 1 25 | const stepValue = Math.round(((v + 100) * scaleSteps) / 200) 26 | // add a floor and ceiling to make sure the step stays "inside" the array 27 | const stepIndex = Math.max(0, Math.min(stepValue, scaleSteps)) 28 | return scale[stepIndex] 29 | } 30 | 31 | /* 32 | 33 | For a given velocity and a theme, get the associated color scale 34 | 35 | */ 36 | export const getVelocityColorScale = (v, theme) => { 37 | const color = getVelocityColor(v, theme) 38 | return scaleLinear().domain([1, 0]).range([color, '#303652']).clamp(true) 39 | } 40 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolsUsageVariationsBlock/Switcher.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | // @ts-ignore 3 | import { useI18n } from 'core/i18n/i18nContext' 4 | // @ts-ignore 5 | import ButtonGroup from 'core/components/ButtonGroup' 6 | // @ts-ignore 7 | import Button from 'core/components/Button' 8 | import { allDimensionIds, DimensionId } from './types' 9 | 10 | export const Switcher = ({ 11 | setDimension, 12 | dimension, 13 | }: { 14 | setDimension: (dimension: DimensionId) => void 15 | dimension: DimensionId 16 | }) => { 17 | const { translate } = useI18n() 18 | 19 | return ( 20 | 21 | {allDimensionIds.map((dimensionId) => ( 22 | 31 | ))} 32 | 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /src/core/blocks/happiness/HappinessHistoryBlock/HappinessHistoryBlock.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | // @ts-ignore 3 | import Block from 'core/blocks/block/Block' 4 | import { BlockContext } from 'core/blocks/types' 5 | import { HappinessYearMean } from 'core/survey_api/happiness' 6 | import { HappinessHistoryChart } from './HappinessHistoryChart' 7 | // @ts-ignore 8 | import { useI18n } from 'core/i18n/i18nContext' 9 | 10 | interface HappinessHistoryBlockProps { 11 | block: BlockContext<'happinessHistoryTemplate', 'HappinessHistoryBlock'> 12 | data: HappinessYearMean[] 13 | } 14 | 15 | export const HappinessHistoryBlock = ({ block, data }: HappinessHistoryBlockProps) => { 16 | const [view, setView] = useState('viz'); 17 | const { translate } = useI18n() 18 | 19 | const headings = [{id: 'year', label: translate('table.year')}, {id: 'mean', label: translate('table.mean')}]; 20 | const rows = []; 21 | data.forEach((row) => { 22 | rows.push([{id: 'year', label: row.year}, {id: 'mean', label: `${row.mean}/5`}]); 23 | }); 24 | 25 | const tables = [{headings: headings, rows: rows}]; 26 | 27 | return 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/stylesheets/to-remove/_variables.scss: -------------------------------------------------------------------------------- 1 | $spacing: 20px; 2 | 3 | @mixin flex-center { 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | } 8 | 9 | $border: 1px solid $border-color-dark; 10 | $border2: 1px solid $border-color; 11 | 12 | @mixin border { 13 | border: $border; 14 | } 15 | 16 | @mixin border2 { 17 | border: $border2; 18 | } 19 | 20 | @function black($opacity) { 21 | @return rgba(0, 0, 0, $opacity); 22 | } 23 | 24 | @function white($opacity) { 25 | @return rgba(255, 255, 255, $opacity); 26 | } 27 | 28 | @mixin sr-only { 29 | border: 0; 30 | clip: rect(0 0 0 0); 31 | clip-path: polygon(0px 0px, 0px 0px, 0px 0px); 32 | -webkit-clip-path: polygon(0px 0px, 0px 0px, 0px 0px); 33 | height: 1px; 34 | margin: -1px; 35 | overflow: hidden; 36 | padding: 0; 37 | position: absolute; 38 | width: 1px; 39 | white-space: nowrap; 40 | } 41 | .sr-only { 42 | @include sr-only; 43 | .capture & { 44 | display: none !important; 45 | } 46 | } 47 | 48 | @keyframes svgAnts { 49 | to { 50 | stroke-dashoffset: 100%; 51 | } 52 | } 53 | 54 | @mixin svgAnts { 55 | stroke-dasharray: 8,4; 56 | animation: svgAnts 50s linear infinite; 57 | animation-fill-mode: forwards; 58 | } 59 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright 2019 Sacha Greif, Raphaël Benitte 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | Note: this license does *NOT* apply to the following, which are not considered to be part of the project's code and cannot be shared/distributed/etc.: 12 | 13 | - Image files 14 | - Markdown files 15 | - YAML files 16 | -------------------------------------------------------------------------------- /src/core/blocks/block/BlockUnitsSelector.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react' 2 | import PropTypes from 'prop-types' 3 | import ButtonGroup from 'core/components/ButtonGroup' 4 | import Button from 'core/components/Button' 5 | import T from 'core/i18n/T' 6 | 7 | const BlockUnitsSelector = ({ units, onChange }) => { 8 | return ( 9 | 10 | 18 | 26 | 27 | ) 28 | } 29 | 30 | BlockUnitsSelector.propTypes = { 31 | units: PropTypes.oneOf(['percentage', 'count']).isRequired, 32 | onChange: PropTypes.func.isRequired, 33 | } 34 | 35 | export default memo(BlockUnitsSelector) 36 | -------------------------------------------------------------------------------- /src/core/charts/features/FeaturesOverviewCirclePackingChart.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import FeaturesCirclePackingChart from 'core/charts/features/FeaturesCirclePackingChart' 3 | 4 | // reuse chart component but keep separate file in order to define different propTypes 5 | FeaturesCirclePackingChart.propTypes = { 6 | data: PropTypes.shape({ 7 | sections: PropTypes.arrayOf( 8 | PropTypes.shape({ 9 | features: PropTypes.arrayOf( 10 | PropTypes.shape({ 11 | id: PropTypes.string.isRequired, 12 | usage: PropTypes.shape({ 13 | total: PropTypes.number.isRequired, 14 | buckets: PropTypes.arrayOf( 15 | PropTypes.shape({ 16 | id: PropTypes.string.isRequired, 17 | count: PropTypes.number.isRequired, 18 | percentage: PropTypes.number.isRequired, 19 | }) 20 | ).isRequired, 21 | }).isRequired, 22 | }) 23 | ), 24 | }) 25 | ), 26 | }), 27 | } 28 | 29 | export default FeaturesCirclePackingChart 30 | -------------------------------------------------------------------------------- /src/core/survey_api/matrices.ts: -------------------------------------------------------------------------------- 1 | import { Entity } from 'core/types' 2 | 3 | export const allToolMatrixExperienceIds = [ 4 | 'would_use', 5 | 'would_not_use', 6 | 'interested', 7 | 'not_interested', 8 | 'never_heard', 9 | 'usage', 10 | ] as const 11 | export type ToolMatrixExperienceId = typeof allToolMatrixExperienceIds[number] 12 | 13 | export const allMatrixDimensionIds = [ 14 | 'years_of_experience', 15 | 'yearly_salary', 16 | 'company_size', 17 | 'source', 18 | ] as const 19 | export type MatrixDimensionId = typeof allMatrixDimensionIds[number] 20 | 21 | export interface MatrixBucket { 22 | id: string 23 | count: number 24 | percentage: number 25 | total_in_range: number 26 | range_total: number 27 | range_percentage: number 28 | range_percentage_delta: number 29 | } 30 | 31 | export interface ToolMatrix { 32 | id: string 33 | entity: Entity 34 | total: number 35 | percentage: number 36 | buckets: MatrixBucket[] 37 | } 38 | 39 | export interface ToolsExperienceDimensionMatrix { 40 | dimension: MatrixDimensionId 41 | tools: ToolMatrix[] 42 | } 43 | 44 | export interface ToolsExperienceMatrices { 45 | experience: ToolMatrixExperienceId 46 | dimensions: ToolsExperienceDimensionMatrix[] 47 | } 48 | 49 | export interface Matrices { 50 | tools: ToolsExperienceMatrices[] 51 | } 52 | -------------------------------------------------------------------------------- /src/stylesheets/to-remove/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | $xsmall-break: 600px; 2 | $small-break: 600px; 3 | $medium-break: 1000px; 4 | $large-break: 1200px; 5 | 6 | $xsmall-minus-one: $xsmall-break - 1; 7 | $small-minus-one: $small-break - 1; 8 | $medium-minus-one: $medium-break - 1; 9 | 10 | @mixin xsmall() { 11 | // smaller than small-break 12 | @media screen and (max-width: $xsmall-minus-one) { 13 | @content; 14 | } 15 | } 16 | @mixin small() { 17 | // smaller than small-break 18 | @media screen and (max-width: $small-minus-one) { 19 | @content; 20 | } 21 | } 22 | @mixin small-medium() { 23 | // smaller than medium-break 24 | @media screen and (max-width: $medium-minus-one) { 25 | @content; 26 | } 27 | } 28 | @mixin medium() { 29 | // between small and medium-break 30 | @media screen and (min-width: $small-break) and (max-width: $medium-minus-one) { 31 | @content; 32 | } 33 | } 34 | @mixin medium-large() { 35 | // larger than small-break 36 | @media screen and (min-width: $small-break) { 37 | @content; 38 | } 39 | } 40 | @mixin large() { 41 | // larger than medium-break 42 | @media screen and (min-width: $medium-break) { 43 | @content; 44 | } 45 | } 46 | 47 | @mixin xlarge() { 48 | // larger than large-break 49 | @media screen and (min-width: $large-break) { 50 | @content; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/core/charts/generic/BarTooltip.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { useTheme } from '@nivo/core' 4 | import { useI18n } from 'core/i18n/i18nContext' 5 | import { useEntities } from 'core/entities/entitiesContext' 6 | 7 | /** 8 | * This tooltip can be used for general bar charts: 9 | * - HorizontalBarChart 10 | * - VerticalBarChart 11 | */ 12 | const BarTooltip = ({ indexValue, data, i18nNamespace, shouldTranslate }) => { 13 | const { getName } = useEntities() 14 | const { translate } = useI18n() 15 | const label = shouldTranslate 16 | ? translate(`options.${i18nNamespace}.${indexValue}`) 17 | : getName(indexValue) 18 | const nivoTheme = useTheme() 19 | 20 | return ( 21 |
22 | {label}:  23 | {data.percentage}% 24 |  ({data.count}) 25 |
26 | ) 27 | } 28 | 29 | BarTooltip.propTypes = { 30 | indexValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, 31 | data: PropTypes.shape({ 32 | percentage: PropTypes.number.isRequired, 33 | count: PropTypes.number.isRequired, 34 | }).isRequired, 35 | i18nNamespace: PropTypes.string.isRequired, 36 | shouldTranslate: PropTypes.bool.isRequired, 37 | } 38 | 39 | export default memo(BarTooltip) 40 | -------------------------------------------------------------------------------- /src/core/theme/colors.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | greyLight: '#e0e4e4', 3 | grey: '#d9dedf', 4 | greyMedium: '#cecdcc', 5 | greyMediumer: '#616868', 6 | greyMediumest: '#5c6069', 7 | greyDark: '#3F4141', 8 | greyDarkish: '#2a2d33', 9 | greyDarker: '#1E252E', 10 | 11 | greyTeal: '#a3cacd', 12 | greyTealDark: '#789B9D', 13 | 14 | blueLighter: '#B2BBEE', 15 | blueLight: '#808EE1', 16 | blue: '#576DE7', 17 | blueDark: '#273aa2', 18 | 19 | pinkLightest: '#D3BBF2', 20 | pinkLighter: '#D68DF0', 21 | pinkLight: '#EC75CB', 22 | pink: '#F95DB2', 23 | pinkDark: '#e86ebf', 24 | 25 | greenLighter: '#E7FFED', 26 | greenLight: '#ACFFC3', 27 | green: '#85EBA2', 28 | greenDark: '#59DF7F', 29 | 30 | tealLighter: '#94eeee', 31 | tealLight: '#65e0e0', 32 | teal: '#41c7c7', 33 | tealDark: '#2ba7a7', 34 | tealDarker: '#1d7e7e', 35 | 36 | purpleLight: '#B096E7', 37 | purple: '#7854C3', 38 | purpleDark: '#57457C', 39 | 40 | redLighter: '#f8a8a8', 41 | redLight: '#fc8f8f', 42 | red: '#FE6A6A', 43 | redDark: '#ec5555', 44 | redDarker: '#D13F3F', 45 | 46 | yellow: '#fbf34c', 47 | skyblue: '#1ea0f2', 48 | orange: '#EF8D33', 49 | olive: '#599E38', 50 | aqua: '#3ABBB3', 51 | indigo: '#4861EC', 52 | 53 | white: '#ffffff', 54 | 55 | navyLightest: '#7e86ad', 56 | navyLighter: '#484F73', 57 | navyLight: '#303652', 58 | navy: '#232840', 59 | navyDark: '#1a1f35', 60 | } 61 | -------------------------------------------------------------------------------- /src/core/helpers/toolsContext.js: -------------------------------------------------------------------------------- 1 | // import React, { createContext, useContext } from 'react' 2 | // import { StaticQuery, graphql } from 'gatsby' 3 | // import get from 'lodash/get' 4 | 5 | // export const ToolsContext = createContext() 6 | 7 | // const toolsQuery = graphql` 8 | // query { 9 | // surveyApi { 10 | // survey(survey: state_of_css) { 11 | // tools { 12 | // id 13 | // entity { 14 | // name 15 | // } 16 | // } 17 | // } 18 | // } 19 | // } 20 | // ` 21 | 22 | // export const ToolsContextProvider = ({ children }) => { 23 | // return ( 24 | // 25 | // {(data) => { 26 | // const tools = get(data, 'surveyApi.survey.tools') 27 | // const getToolName = ({ id }) => { 28 | // const tool = tools.find((t) => t.id === id) 29 | // return get(tool, 'entity.name') 30 | // } 31 | // return ( 32 | // 33 | // {children} 34 | // 35 | // ) 36 | // }} 37 | // 38 | // ) 39 | // } 40 | 41 | // export const useTools = () => useContext(ToolsContext) 42 | -------------------------------------------------------------------------------- /src/core/share/ShareSite.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import ShareTwitter from './ShareTwitter' 4 | import ShareEmail from './ShareEmail' 5 | import ShareFacebook from './ShareFacebook' 6 | import ShareLinkedIn from './ShareLinkedIn' 7 | import { useI18n } from 'core/i18n/i18nContext' 8 | import config from 'Config/config.yml' 9 | 10 | const { hashtag, year, siteTitle, siteUrl } = config 11 | 12 | const ShareSite = () => { 13 | const { translate } = useI18n() 14 | 15 | const options = { values: { hashtag, year, siteTitle, link: siteUrl } } 16 | const title = translate('share.site.title', options) 17 | const twitterText = translate('share.site.twitter_text', options) 18 | const subject = translate('share.site.subject', options) 19 | const body = translate('share.site.body', options) 20 | 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | 31 | const Container = styled.div` 32 | border-top: ${(props) => props.theme.separationBorder}; 33 | display: flex; 34 | justify-content: space-evenly; 35 | position: relative; 36 | z-index: 1; 37 | ` 38 | 39 | export default ShareSite 40 | -------------------------------------------------------------------------------- /surveys/js2020/config/picks.yml: -------------------------------------------------------------------------------- 1 | - twitterName: joshwcomeau 2 | fullName: Josh W. Comeau 3 | url: https://a11y.coffee/ 4 | pickName: A11y Coffee 5 | 6 | - twitterName: swyx 7 | fullName: Shawn Wang 8 | url: https://svelte.dev/ 9 | pickName: Svelte 10 | 11 | - twitterName: kentcdodds 12 | fullName: Kent C. Dodds 13 | url: https://remix.run/ 14 | pickName: Remix 15 | 16 | - twitterName: sarah_edo 17 | fullName: Sarah Drasner 18 | url: https://insomnia.rest/ 19 | pickName: Insomnia 20 | 21 | - twitterName: ladyleet 22 | fullName: Tracy Lee 23 | url: https://redwoodjs.com/ 24 | pickName: RedwoodJS 25 | 26 | - twitterName: midudev 27 | fullName: Miguel Ángel Durán 28 | url: https://nodejs.org/api/esm.html#esm_modules_ecmascript_modules 29 | pickName: ECMAScript Modules 30 | 31 | - twitterName: cassidoo 32 | fullName: Cassidy Williams 33 | url: https://twitter.com/bencodezen 34 | pickName: Ben Hong 35 | 36 | - twitterName: lauragift_ 37 | fullName: Gift Egwuenu 38 | url: https://www.joshwcomeau.com/ 39 | pickName: Josh W. Comeau 40 | 41 | - twitterName: tomdale 42 | fullName: Tom Dale 43 | url: https://volta.sh/ 44 | pickName: Volta 45 | 46 | - twitterName: markdalgleish 47 | fullName: Mark Dalgleish 48 | url: https://github.com/atlassian/changesets 49 | pickName: Changesets 50 | 51 | - twitterName: sachagreif 52 | fullName: Sacha Greif 53 | url: https://bestofjs.org/ 54 | pickName: Best Of JS -------------------------------------------------------------------------------- /src/core/charts/generic/UsageVariationsChart/multiple-diverging-lines/SubAxes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ScaleLinear } from 'd3-scale' 3 | import { useTheme } from '@nivo/core' 4 | import { ComputedDatum } from './types' 5 | 6 | interface SubAxesProps { 7 | data: ComputedDatum[] 8 | valueScale: ScaleLinear 9 | itemHeight: number 10 | width: number 11 | } 12 | 13 | export const SubAxes = ({ data, itemHeight, width }: SubAxesProps) => { 14 | const theme = useTheme() 15 | 16 | return ( 17 | 18 | {data.map((datum) => { 19 | return ( 20 | 21 | {datum.index === 0 && } 22 | 23 | 24 | 25 | 31 | {datum.baseline}% 32 | 33 | 34 | ) 35 | })} 36 | 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/stylesheets/_features.scss: -------------------------------------------------------------------------------- 1 | .Feature__Support { 2 | font-size: $smallish-font; 3 | @include font-regular; 4 | } 5 | 6 | .Feature__Links { 7 | font-size: $smallish-font; 8 | @include font-regular; 9 | } 10 | 11 | .FTBlock__Links__Item { 12 | margin-bottom: $spacing/3; 13 | } 14 | 15 | .Features__Overview { 16 | display: flex; 17 | flex-wrap: wrap; 18 | justify-content: space-between; 19 | } 20 | 21 | .Features__Overview__Item { 22 | width: 240px; 23 | height: 260px; 24 | margin-bottom: $spacing * 2; 25 | display: grid; 26 | grid-template-rows: 210px 50px; 27 | } 28 | 29 | .Features__Overview__Item__Footer { 30 | border-top: 2px solid $border-color; 31 | border-bottom: 2px solid $border-color; 32 | font-size: $smallish-font; 33 | @include font-bold; 34 | display: flex; 35 | justify-content: center; 36 | align-items: center; 37 | } 38 | 39 | .Features__Overview__Item__Footer:hover { 40 | background: $border-color; 41 | color: #ffffff; 42 | cursor: pointer; 43 | } 44 | .FTBlock__Description { 45 | .capture & { 46 | grid-column: 1 / 3; 47 | } 48 | } 49 | .FTBlock__Resources { 50 | @include small { 51 | margin-top: $spacing; 52 | } 53 | h3 { 54 | margin-bottom: $spacing/2; 55 | font-size: $medium-font; 56 | } 57 | ul { 58 | padding-left: $spacing; 59 | } 60 | .capture & { 61 | display: none; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/core/blocks/block/BlockViewSelector.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import ButtonGroup from 'core/components/ButtonGroup' 4 | import Button from 'core/components/Button' 5 | import T from 'core/i18n/T' 6 | import { BsTable } from 'react-icons/bs'; 7 | import { GoGraph } from 'react-icons/go'; 8 | 9 | const VisuallyHidden = styled.span` 10 | clip: rect(0 0 0 0); 11 | clip-path: inset(50%); 12 | height: 1px; 13 | overflow: hidden; 14 | position: absolute; 15 | white-space: nowrap; 16 | width: 1px; 17 | `; 18 | 19 | const BlockViewSelector = ({ view, setView }) => { 20 | return ( 21 | 22 | 33 | 34 | 45 | 46 | ); 47 | } 48 | 49 | export default BlockViewSelector; -------------------------------------------------------------------------------- /src/core/components/Hamburger.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { color } from 'core/theme' 4 | 5 | const Hamburger = () => ( 6 | 7 | 8 | 19 | 30 | 41 | 42 | 43 | ) 44 | 45 | const Container = styled.svg` 46 | stroke: ${color('link')}; 47 | ` 48 | 49 | export default Hamburger 50 | -------------------------------------------------------------------------------- /src/core/blocks/awards/AwardIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | export const AwardIcon2 = () => ( 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ? 21 | 22 | 23 | 24 | 25 | ) 26 | 27 | const Icon = styled.div` 28 | path { 29 | stroke: ${({ theme }) => theme.colors.background}; 30 | } 31 | ` 32 | export default AwardIcon2 33 | -------------------------------------------------------------------------------- /surveys/js2020/logo/LogoCell.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { mq, color } from 'core/theme' 4 | 5 | interface LogoCellProps { 6 | index: number 7 | text: string 8 | color: string 9 | } 10 | 11 | export const LogoCell = ({ index, text, color: textColor }: LogoCellProps) => ( 12 | 13 | {index} 14 | {text} 15 | 16 | ) 17 | 18 | const Container = styled.span` 19 | width: 67px; 20 | height: 67px; 21 | display: flex; 22 | align-items: center; 23 | justify-content: center; 24 | font-size: 26px; 25 | position: relative; 26 | border-left: ${(props) => props.theme.separationBorder}; 27 | background: ${color('background')}; 28 | text-decoration: none; 29 | 30 | .Logo:hover & { 31 | background: ${color('backgroundAlt')}; 32 | } 33 | 34 | @media ${mq.smallMedium} { 35 | &:last-child { 36 | border-right: ${(props) => props.theme.separationBorder}; 37 | } 38 | } 39 | 40 | @media ${mq.large} { 41 | &:first-child { 42 | border-left: 0; 43 | } 44 | } 45 | ` 46 | 47 | const Index = styled.span` 48 | position: absolute; 49 | top: 0; 50 | left: 0; 51 | font-size: 12px; 52 | line-height: 12px; 53 | padding: 6px 8px; 54 | color: ${color('text')}; 55 | opacity: 0.35; 56 | font-weight: 300; 57 | ` 58 | 59 | export default LogoCell -------------------------------------------------------------------------------- /src/core/report/ReportBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReportContents from 'Config/report.mdx' 3 | import styled from 'styled-components' 4 | import { mq, color, spacing, fontSize, fontWeight } from 'core/theme' 5 | // import Logo from 'core/components/Logo' 6 | 7 | const debug = false 8 | 9 | export default () => ( 10 | 11 | {/* */} 12 | 13 | {debug && ( 14 | <> 15 | 16 | 17 | 18 | )} 19 | 20 | ) 21 | 22 | const Trigger = styled.div` 23 | position: fixed; 24 | height: 1px; 25 | background: red; 26 | left: 0px; 27 | right: 0px; 28 | z-index: 10000; 29 | ` 30 | const TopTrigger = styled(Trigger)` 31 | top: 20vh; 32 | ` 33 | const BottomTrigger = styled(Trigger)` 34 | top: 80vh; 35 | ` 36 | const Essay = styled.div` 37 | .first { 38 | } 39 | & > p { 40 | font-size: ${fontSize('large')}; 41 | line-height: 2; 42 | margin-bottom: ${spacing(1.5)}; 43 | &:first-of-type:first-line { 44 | font-size: ${fontSize('largest')}; 45 | } 46 | } 47 | code { 48 | background: ${color('backgroundAlt')}; 49 | padding: 3px 6px; 50 | font-weight: ${fontWeight('medium')}; 51 | color: ${color('contrast')}; 52 | border: 1px dotted ${color('contrast')}; 53 | font-size: ${fontSize('smallish')}; 54 | } 55 | ` 56 | -------------------------------------------------------------------------------- /src/core/helpers/keydownContext.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, createContext, useContext } from 'react' 2 | 3 | const keydownContext = createContext({}) 4 | 5 | const targetKey = 'Alt' 6 | 7 | const KeydownContextProviderInner = ({ children }) => { 8 | const [modKeyDown, setModKeyDown] = useState(false) 9 | const value = { 10 | modKeyDown, 11 | } 12 | 13 | function downHandler({ key }) { 14 | if (key === targetKey) { 15 | setModKeyDown(true) 16 | } 17 | } 18 | 19 | const upHandler = ({ key }) => { 20 | if (key === targetKey) { 21 | setModKeyDown(false) 22 | } 23 | } 24 | useEffect(() => { 25 | window.addEventListener('keydown', downHandler) 26 | window.addEventListener('keyup', upHandler) 27 | // Remove event listeners on cleanup 28 | return () => { 29 | window.removeEventListener('keydown', downHandler) 30 | window.removeEventListener('keyup', upHandler) 31 | } 32 | }, []) // Empty array ensures that effect is only run on mount and unmount 33 | 34 | return ( 35 | 36 |
{children}
37 |
38 | ) 39 | } 40 | 41 | export const KeydownContextProvider = ({ children }) => { 42 | return {children} 43 | } 44 | 45 | export const useKeydownContext = () => useContext(keydownContext) 46 | -------------------------------------------------------------------------------- /src/core/share/ShareFacebook.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { useI18n } from 'core/i18n/i18nContext' 4 | import track from './tracking' 5 | import ShareLink from './ShareLink' 6 | 7 | const ShareFacebook = ({ link, trackingId }) => { 8 | const { translate } = useI18n() 9 | 10 | return ( 11 | 19 | 32 | {translate('share.facebook')} 33 | 34 | ) 35 | } 36 | 37 | ShareFacebook.propTypes = { 38 | link: PropTypes.string.isRequired, 39 | trackingId: PropTypes.string, 40 | } 41 | 42 | export default ShareFacebook 43 | -------------------------------------------------------------------------------- /src/core/share/ShareBlockTemplate.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Redirect, useLocation } from '@reach/router' 3 | import { getBlockTitle, getBlockDescription } from 'core/helpers/blockHelpers' 4 | import { mergePageContext } from '../helpers/pageHelpers' 5 | import PageMeta from '../pages/PageMeta' 6 | import PageMetaDebug from '../pages/PageMetaDebug' 7 | import config from 'Config/config.yml' 8 | import { useI18n } from 'core/i18n/i18nContext' 9 | import { usePageContext } from 'core/helpers/pageContext' 10 | 11 | const ShareBlockTemplate = () => { 12 | const pageContext = usePageContext() 13 | const { block } = pageContext 14 | const location = useLocation() 15 | const { translate } = useI18n() 16 | const context = mergePageContext(pageContext, location) 17 | 18 | const blockTitle = getBlockTitle(block, context, translate, { 19 | format: 'full', 20 | }) 21 | const blockDescription = getBlockDescription(context.block, context, translate, { 22 | isMarkdownEnabled: false, 23 | }) 24 | const overrides = { 25 | title: `${config.siteTitle}: ${blockTitle} ${config.hashtag}`, 26 | } 27 | if (blockDescription) { 28 | overrides.description = blockDescription 29 | } 30 | 31 | return ( 32 |
33 | 34 | 35 | {!context.isDebugEnabled && } 36 | Redirecting… 37 |
38 | ) 39 | } 40 | 41 | export default ShareBlockTemplate 42 | -------------------------------------------------------------------------------- /src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/AllSectionsToolsCardinalityByUserChart.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { ToolsCardinalityByUserBucket } from 'core/survey_api/tools' 4 | // @ts-ignore 5 | import { spacing, mq } from 'core/theme' 6 | import { SectionItem } from './SectionItem' 7 | 8 | export const AllSectionsToolsCardinalityByUserChart = ({ 9 | data, 10 | units, 11 | maxNumberOfTools, 12 | }: { 13 | data: { 14 | sectionId: string 15 | data: ToolsCardinalityByUserBucket[] 16 | }[] 17 | units: 'percentage' | 'count' 18 | maxNumberOfTools: number 19 | }) => ( 20 | 21 | {data.map((section) => ( 22 | 29 | ))} 30 | 31 | ) 32 | 33 | const GridContainer = styled.div` 34 | display: grid; 35 | column-gap: ${spacing(1.5)}; 36 | 37 | @media ${mq.small} { 38 | grid-template-columns: 1fr; 39 | row-gap: ${spacing(1)}; 40 | } 41 | 42 | @media ${mq.mediumLarge} { 43 | grid-template-columns: repeat(3, minmax(120px, 1fr)); 44 | row-gap: ${spacing(1.5)}; 45 | } 46 | 47 | @media ${mq.large} { 48 | grid-template-columns: repeat(4, minmax(120px, 1fr)); 49 | row-gap: ${spacing(1.5)}; 50 | column-gap: ${spacing(1.5)}; 51 | } 52 | ` 53 | -------------------------------------------------------------------------------- /surveys/css2020/theme/charts.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | import defaultsDeep from 'lodash/defaultsDeep' 3 | import charts from 'core/theme/charts' 4 | import baseColors from 'core/theme/colors' 5 | import colors from './colors' 6 | import typography from './typography' 7 | 8 | const stateOfCSSThemeCharts: DefaultTheme['charts'] = defaultsDeep( 9 | { 10 | fontFamily: typography.fontFamily, 11 | axis: { 12 | ticks: { 13 | line: { 14 | fill: colors.text, 15 | }, 16 | text: { 17 | fill: colors.text, 18 | }, 19 | }, 20 | legend: { 21 | text: { 22 | fill: colors.text, 23 | }, 24 | }, 25 | }, 26 | streamTimelineAxis: { 27 | ticks: { 28 | line: { 29 | stroke: colors.text, 30 | }, 31 | text: { 32 | fill: colors.text, 33 | }, 34 | }, 35 | }, 36 | tooltip: { 37 | container: { 38 | fontSize: 14, 39 | background: baseColors.greyLight, 40 | color: baseColors.blueDark, 41 | borderRadius: 0, 42 | boxShadow: `9px 9px 0 rgba(0, 0, 0, 0.15)`, 43 | }, 44 | }, 45 | legends: { 46 | text: { 47 | fill: colors.text, 48 | }, 49 | }, 50 | }, 51 | charts 52 | ) 53 | 54 | export default stateOfCSSThemeCharts 55 | -------------------------------------------------------------------------------- /surveys/css2021/theme/charts.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | import defaultsDeep from 'lodash/defaultsDeep' 3 | import charts from 'core/theme/charts' 4 | import baseColors from 'core/theme/colors' 5 | import colors from './colors' 6 | import typography from './typography' 7 | 8 | const stateOfCSSThemeCharts: DefaultTheme['charts'] = defaultsDeep( 9 | { 10 | fontFamily: typography.fontFamily, 11 | axis: { 12 | ticks: { 13 | line: { 14 | fill: colors.text, 15 | }, 16 | text: { 17 | fill: colors.text, 18 | }, 19 | }, 20 | legend: { 21 | text: { 22 | fill: colors.text, 23 | }, 24 | }, 25 | }, 26 | streamTimelineAxis: { 27 | ticks: { 28 | line: { 29 | stroke: colors.text, 30 | }, 31 | text: { 32 | fill: colors.text, 33 | }, 34 | }, 35 | }, 36 | tooltip: { 37 | container: { 38 | fontSize: 14, 39 | background: baseColors.greyLight, 40 | color: baseColors.blueDark, 41 | borderRadius: 0, 42 | boxShadow: `9px 9px 0 rgba(0, 0, 0, 0.15)`, 43 | }, 44 | }, 45 | legends: { 46 | text: { 47 | fill: colors.text, 48 | }, 49 | }, 50 | }, 51 | charts 52 | ) 53 | 54 | export default stateOfCSSThemeCharts 55 | -------------------------------------------------------------------------------- /src/core/theme/charts.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | emptyPattern: { 3 | id: 'empty', 4 | type: 'patternLines', 5 | background: 'inherit', 6 | color: 'rgba(0, 0, 0, .07)', 7 | rotation: -45, 8 | lineWidth: 3, 9 | spacing: 6, 10 | }, 11 | axis: { 12 | domain: { 13 | line: { 14 | strokeWidth: 0, 15 | }, 16 | }, 17 | ticks: { 18 | text: { 19 | fontSize: 12, 20 | }, 21 | }, 22 | legend: { 23 | text: { 24 | fontSize: 14, 25 | fontWeight: 600, 26 | }, 27 | }, 28 | }, 29 | streamTimelineAxis: { 30 | ticks: { 31 | line: { 32 | strokeWidth: 2, 33 | }, 34 | text: { 35 | fontSize: 12, 36 | }, 37 | }, 38 | }, 39 | grid: { 40 | line: { 41 | strokeDasharray: '1 2', 42 | strokeOpacity: 0.4, 43 | }, 44 | }, 45 | legends: { 46 | text: { 47 | fontSize: 11, 48 | }, 49 | }, 50 | tooltip: { 51 | container: { 52 | fontSize: 14, 53 | borderRadius: 0, 54 | boxShadow: `9px 9px 0 rgba(0, 0, 0, 0.15)`, 55 | }, 56 | }, 57 | labels: { 58 | text: { 59 | fontSize: 12, 60 | fontWeight: 500, 61 | textShadow: `0px 2px 3px rgba(0,0,0,0.35)`, 62 | }, 63 | }, 64 | dots: { 65 | text: { 66 | fontSize: 12, 67 | }, 68 | }, 69 | } 70 | -------------------------------------------------------------------------------- /src/core/blocks/block/BlockNote.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react' 2 | import PropTypes from 'prop-types' 3 | import styled from 'styled-components' 4 | import { spacing } from 'core/theme' 5 | import { useI18n } from 'core/i18n/i18nContext' 6 | import T from 'core/i18n/T' 7 | 8 | const BlockNote = ({ block }) => { 9 | const { translate } = useI18n() 10 | // for "_others" blocks (freeform answers), replace suffix with ".others" 11 | const blockId = block.id && block.id.replace('_others', '.others') 12 | const key = `blocks.${block.blockName || blockId}.note` 13 | const blockNote = translate(key, {}, null) 14 | if (blockNote) { 15 | return ( 16 | 17 | 18 | 19 | ) 20 | } else { 21 | return null 22 | } 23 | } 24 | 25 | BlockNote.propTypes = { 26 | block: PropTypes.shape({ 27 | id: PropTypes.string.isRequired, 28 | title: PropTypes.node, 29 | titleId: PropTypes.string, 30 | description: PropTypes.node, 31 | descriptionId: PropTypes.string, 32 | }).isRequired, 33 | } 34 | 35 | BlockNote.defaultProps = { 36 | showDescription: true, 37 | isShareable: true, 38 | } 39 | 40 | const Note = styled.div` 41 | background: ${(props) => props.theme.colors.backgroundAlt}; 42 | padding: ${spacing()}; 43 | margin-top: ${spacing(2)}; 44 | font-size: ${(props) => props.theme.typography.size.small}; 45 | p, 46 | ul, 47 | ol { 48 | &:last-child { 49 | margin: 0; 50 | } 51 | } 52 | ` 53 | 54 | export default memo(BlockNote) 55 | -------------------------------------------------------------------------------- /src/core/blocks/features/FeatureResources.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react' 2 | import { useI18n } from 'core/i18n/i18nContext' 3 | 4 | const FeatureResources = ({ id, mdnInfo, caniuseInfo }) => { 5 | const { translate } = useI18n() 6 | if (!caniuseInfo && !mdnInfo) { 7 | return null 8 | } 9 | 10 | return ( 11 |
12 |

{translate('feature.learn_more')}

13 | 39 |
40 | ) 41 | } 42 | 43 | export default memo(FeatureResources) 44 | -------------------------------------------------------------------------------- /src/core/charts/table/Table.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import T from 'core/i18n/T' 3 | import React from 'react' 4 | import { useTheme } from 'styled-components' 5 | 6 | const Table = ({tables}) => { 7 | const theme = useTheme(); 8 | 9 | return ( 10 | <> 11 | { 12 | tables.map((table) => { 13 | return <> 14 | {table.title && {table.title}} 15 | 16 |
17 | 18 | {table.headings.map((heading) => )} 19 | 20 | 21 | 22 | {table.rows.map((row) => { 23 | return 24 | {table.headings.map((cell, index) => { 25 | return index > 0 26 | ? 27 | : 28 | })} 29 | 30 | })} 31 | 32 | 33 | 34 | }) 35 | } 36 | 37 | ) 38 | }; 39 | 40 | const DataTable = styled.table` 41 | width: 100%; 42 | border-collapse: collapse; 43 | margin-bottom: 2rem; 44 | 45 | th { 46 | text-align: left; 47 | } 48 | 49 | td, th { 50 | padding: 0.75rem 0.45rem; 51 | border: 1px solid ${({ theme }) => theme.colors.border}; 52 | margin: 0; 53 | } 54 | `; 55 | 56 | const Title = styled.h4` 57 | margin-bottom: 0.25rem; 58 | `; 59 | 60 | export default Table; -------------------------------------------------------------------------------- /src/core/i18n/translator.js: -------------------------------------------------------------------------------- 1 | import template from 'lodash/template' 2 | 3 | /* 4 | 5 | Returns the translation string object 6 | 7 | */ 8 | export const getStringTranslator = (locale = {}) => (key, { values } = {}, fallback) => { 9 | const { strings = [], ...rest } = locale 10 | // reverse strings so that strings added last take priority 11 | const s = strings 12 | .slice() 13 | .reverse() 14 | .find((t) => t.key === key) 15 | 16 | if (s && values) { 17 | try { 18 | s.t = template(s.t, { interpolate: /{([\s\S]+?)}/g })(values) 19 | } catch (error) { 20 | console.error(error) 21 | s.t = `[${locale.id}][ERR] ${key}` 22 | } 23 | } 24 | 25 | return { ...s, locale: rest } 26 | } 27 | 28 | /* 29 | 30 | Returns the translated string (legacy) 31 | 32 | */ 33 | export const getTranslator = (locale = {}) => (key, { values } = {}, fallback) => { 34 | const { id, strings = [] } = locale 35 | // reverse strings so that strings added last take priority 36 | const translation = strings 37 | .slice() 38 | .reverse() 39 | .find((t) => t.key === key) 40 | 41 | if (translation === undefined) { 42 | return typeof fallback === 'undefined' ? `[${id}] ${key}` : fallback 43 | } 44 | 45 | if (values === undefined) return translation.t 46 | 47 | try { 48 | return template(translation.t, { interpolate: /{([\s\S]+?)}/g })(values) 49 | } catch (error) { 50 | // console.error(error) 51 | return `[${id}][ERR] ${key}` 52 | } 53 | } 54 | 55 | export const translateOrFallback = (translatedKey, fallback) => 56 | translatedKey.match(/\[[a-z]{2}-[A-Z]{2}?\] [a-z_\-.]+/) ? fallback : translatedKey 57 | -------------------------------------------------------------------------------- /src/core/share/ShareLinkedIn.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { useI18n } from 'core/i18n/i18nContext' 4 | import track from './tracking' 5 | import ShareLink from './ShareLink' 6 | 7 | const ShareLinkedIn = ({ link, title, summary = '', trackingId }) => { 8 | const { translate } = useI18n() 9 | 10 | return ( 11 | 21 | 30 | {translate('share.linkedin')} 31 | 32 | ) 33 | } 34 | 35 | ShareLinkedIn.propTypes = { 36 | link: PropTypes.string.isRequired, 37 | title: PropTypes.string.isRequired, 38 | summary: PropTypes.string, 39 | trackingId: PropTypes.string, 40 | } 41 | 42 | export default ShareLinkedIn 43 | -------------------------------------------------------------------------------- /src/core/charts/tools/ToolLabel.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useEntities } from 'core/entities/entitiesContext' 3 | import styled from 'styled-components' 4 | import { fontSize } from 'core/theme' 5 | import Button from 'core/components/Button' 6 | import { ToolExperienceBlock } from 'core/blocks/tools/ToolExperienceBlock' 7 | import { usePageContext } from 'core/helpers/pageContext' 8 | import get from 'lodash/get' 9 | import ModalTrigger from 'core/components/ModalTrigger' 10 | 11 | const ToolLabel = ({ id }) => { 12 | const { getEntity } = useEntities() 13 | 14 | const entity = getEntity(id) 15 | if (!entity) { 16 | return {id} 17 | } 18 | 19 | const { name, homepage } = entity 20 | 21 | return ( 22 | 25 | 26 | {name} 27 | 28 | 29 | } 30 | label={name} 31 | > 32 | 33 | 34 | ) 35 | } 36 | 37 | const ToolLabelModal = ({ id, closeComponent }) => { 38 | const pageContext = usePageContext() 39 | const block = pageContext.blocks.find((block) => block.id === id) 40 | const blockData = get(pageContext.pageData, block.dataPath) 41 | return 42 | } 43 | 44 | const LabelLink = styled(Button)` 45 | padding: 4px 12px; 46 | border-radius: 500px; 47 | display: inline-block; 48 | font-size: ${fontSize('smaller')}; 49 | white-space: nowrap; 50 | ` 51 | 52 | export default ToolLabel 53 | -------------------------------------------------------------------------------- /src/core/charts/generic/UsageVariationsChart/UsageVariationsChart.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react' 2 | import { useTheme } from 'styled-components' 3 | import { Margin } from '@nivo/core' 4 | import { TickFormatter } from '@nivo/axes' 5 | // @ts-ignore 6 | import { useI18n } from 'core/i18n/i18nContext' 7 | import { ResponsiveMultipleDivergingLines } from './multiple-diverging-lines' 8 | 9 | interface UsageVariationsChartProps { 10 | data: { 11 | id: string 12 | name: string 13 | baseline: number 14 | data: { 15 | index: string 16 | count: number 17 | percentage: number 18 | percentageDelta: number 19 | }[] 20 | }[] 21 | margin: Margin 22 | keys: string[] 23 | i18nNamespace: string 24 | } 25 | 26 | export const UsageVariationsChart = ({ 27 | data, 28 | margin, 29 | keys, 30 | i18nNamespace, 31 | }: UsageVariationsChartProps) => { 32 | const theme = useTheme() 33 | const { translate } = useI18n() 34 | 35 | const translateTick = useCallback( 36 | (key: string) => translate(`options.${i18nNamespace}.${key}.short`) as string, 37 | [translate, i18nNamespace] 38 | ) as TickFormatter 39 | 40 | return ( 41 | 57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /surveys/js2020/theme/charts.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components' 2 | import defaultsDeep from 'lodash/defaultsDeep' 3 | import charts from 'core/theme/charts' 4 | import baseColors from 'core/theme/colors' 5 | import colors from './colors' 6 | import typography from './typography' 7 | 8 | const stateOfJSThemeCharts: DefaultTheme['charts'] = defaultsDeep( 9 | { 10 | fontFamily: typography.fontFamily, 11 | axis: { 12 | ticks: { 13 | line: { 14 | fill: colors.text, 15 | }, 16 | text: { 17 | fill: colors.text, 18 | }, 19 | }, 20 | legend: { 21 | text: { 22 | fill: colors.text, 23 | }, 24 | }, 25 | }, 26 | streamTimelineAxis: { 27 | ticks: { 28 | line: { 29 | stroke: baseColors.greyLight, 30 | }, 31 | text: { 32 | fill: baseColors.greyLight, 33 | }, 34 | }, 35 | }, 36 | grid: { 37 | line: { 38 | stroke: baseColors.greyMedium, 39 | }, 40 | }, 41 | legends: { 42 | text: { 43 | fill: baseColors.greyLight, 44 | }, 45 | }, 46 | tooltip: { 47 | container: { 48 | background: baseColors.greyLight, 49 | color: baseColors.blueDark, 50 | }, 51 | }, 52 | labels: { 53 | text: { 54 | fill: baseColors.navyDark, 55 | }, 56 | }, 57 | dots: { 58 | text: { 59 | fill: baseColors.greyDark, 60 | }, 61 | }, 62 | }, 63 | charts 64 | ) 65 | 66 | export default stateOfJSThemeCharts 67 | -------------------------------------------------------------------------------- /src/core/components/ChartLabel.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { useTheme } from 'styled-components' 4 | 5 | /** 6 | * This component is used to render a custom label for charts, 7 | * its main advantage is to add an outline to the labels so 8 | * they are more legible. 9 | */ 10 | const ChartLabel = ({ 11 | label, 12 | fontSize = 13, 13 | outlineColor: _outlineColor, 14 | textColor: _textColor, 15 | ...rest 16 | }) => { 17 | const theme = useTheme() 18 | 19 | const outlineColor = _outlineColor || theme.colors.background 20 | const textColor = _textColor || theme.colors.text 21 | 22 | return ( 23 | 24 | 37 | {label} 38 | 39 | 49 | {label} 50 | 51 | 52 | ) 53 | } 54 | 55 | ChartLabel.propTypes = { 56 | label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, 57 | fontSize: PropTypes.number, 58 | outlineColor: PropTypes.string, 59 | textColor: PropTypes.string, 60 | } 61 | 62 | export default memo(ChartLabel) 63 | -------------------------------------------------------------------------------- /src/core/pages/PaginationLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styled from 'styled-components' 4 | import { mq, spacing, fontSize } from 'core/theme' 5 | import PageLabel from './PageLabel' 6 | import PageLink from './PageLink' 7 | 8 | const StyledLink = styled(PageLink)` 9 | display: flex; 10 | align-items: center; 11 | white-space: nowrap; 12 | overflow: hidden; 13 | text-overflow: ellipsis; 14 | justify-content: ${(props) => (props.type === 'previous' ? 'flex-start' : 'flex-end')}; 15 | padding: ${spacing()}; 16 | 17 | @media ${mq.smallMedium} { 18 | font-size: ${fontSize('smaller')}; 19 | span { 20 | display: block; 21 | text-overflow: ellipsis; 22 | overflow: hidden; 23 | white-space: nowrap; 24 | width: 100%; 25 | text-align: center; 26 | } 27 | } 28 | @media ${mq.large} { 29 | font-size: ${fontSize('medium')}; 30 | } 31 | 32 | &:hover, &:focus { 33 | background: ${(props) => props.theme.colors.backgroundAlt}; 34 | } 35 | ` 36 | 37 | const PaginationLink = ({ page, type }) => ( 38 | 39 | {type === 'previous' && ( 40 | 41 | «  42 | 43 | 44 | )} 45 | {type === 'next' && ( 46 | 47 | 48 |  » 49 | 50 | )} 51 | 52 | ) 53 | 54 | PaginationLink.propTypes = { 55 | page: PropTypes.shape({ 56 | id: PropTypes.string.isRequired, 57 | path: PropTypes.string.isRequired, 58 | }).isRequired, 59 | type: PropTypes.oneOf(['previous', 'next']).isRequired, 60 | } 61 | 62 | export default PaginationLink 63 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolsArrowsBlock/ToolsArrowsBlock.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import React, { useMemo } from 'react' 3 | import { keyBy, sortBy } from 'lodash' 4 | // @ts-ignore 5 | import { useI18n } from 'core/i18n/i18nContext' 6 | // @ts-ignore 7 | import Block from 'core/blocks/block/Block' 8 | // @ts-ignore 9 | import ChartContainer from 'core/charts/ChartContainer' 10 | import { BlockContext } from 'core/blocks/types' 11 | import { ToolsArrowsToolData } from './types' 12 | import { ToolsArrowsChart } from './ToolsArrowsChart.js' 13 | import get from 'lodash/get' 14 | import { ToolsExperienceToolData } from 'core/survey_api/tools' 15 | 16 | /** 17 | * Convert raw API data to be compatible with tools arrows chart. 18 | */ 19 | const useNormalizedData = (rawData: ToolsExperienceToolData[]): ToolsArrowsToolData[] => 20 | useMemo(() => { 21 | let data: ToolsArrowsToolData[] = rawData.map((tool) => { 22 | return {} 23 | }) 24 | 25 | return data 26 | }, [rawData]) 27 | 28 | interface ToolsArrowsBlockProps { 29 | index: number 30 | block: BlockContext< 31 | 'toolsExperienceMarimekkoTemplate', 32 | 'ToolsExperienceMarimekkoBlock', 33 | { toolIds: string }, 34 | any 35 | > 36 | data: ToolsExperienceToolData[] 37 | triggerId: string | null 38 | } 39 | 40 | export const ToolsArrowsBlock = ({ block, data, triggerId = null }: ToolsArrowsBlockProps) => { 41 | // const normalizedData = useNormalizedData(data) 42 | const controlledCurrent = triggerId 43 | 44 | return ( 45 | 53 | 54 | 55 | 56 | 57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /src/core/components/Head.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Helmet from 'react-helmet' 3 | import { usePageContext } from 'core/helpers/pageContext' 4 | import { getPageSocialMeta, getPageMeta } from 'core/helpers/pageHelpers' 5 | import { useI18n } from 'core/i18n/i18nContext' 6 | // import { useTools } from 'core/helpers/toolsContext' 7 | 8 | const Head = () => { 9 | const context = usePageContext() 10 | const { translate } = useI18n() 11 | // const { getToolName } = useTools() 12 | 13 | let overrides = {} 14 | // const toolName = getToolName(context) 15 | // if (toolName) { 16 | // overrides.title = `${websiteTitle}: ${toolName}` 17 | // } 18 | 19 | const meta = getPageMeta(context, translate, overrides) 20 | const socialMeta = getPageSocialMeta(context, translate, overrides) 21 | const description = translate(`general.results.description`) 22 | 23 | const mergedMeta = [ 24 | { charset: 'utf-8' }, 25 | { name: 'description', content: description }, 26 | // responsive 27 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 28 | // google check 29 | { 30 | name: 'google-site-verification', 31 | content: 'hrTRsz9fkGmQlVbLBWA4wmhn0qsI6_M3NKemTGCkpps', 32 | }, 33 | // social 34 | ...socialMeta, 35 | ] 36 | 37 | return ( 38 | <> 39 | 40 | 41 | {meta.title} 42 | 43 | 44 | 48 | 49 | 50 | ) 51 | } 52 | 53 | export default Head 54 | -------------------------------------------------------------------------------- /src/core/i18n/Locales.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { usePageContext } from 'core/helpers/pageContext' 4 | import { Link } from 'gatsby' 5 | import { mq, spacing, fontSize, fontWeight } from 'core/theme' 6 | import get from 'lodash/get' 7 | import BlockCompletionIndicator from 'core/blocks/block/BlockCompletionIndicator' 8 | 9 | const Container = styled.div` 10 | display: grid; 11 | grid-template-columns: auto auto; 12 | grid-column-gap: ${spacing(1.5)}; 13 | grid-row-gap: ${spacing(1.5)}; 14 | ` 15 | 16 | const Item = styled.span` 17 | text-align: center; 18 | font-size: ${fontSize('medium')}; 19 | display: flex; 20 | align-items: center; 21 | @media ${mq.smallMedium} { 22 | font-size: ${fontSize('small')}; 23 | } 24 | @media ${mq.large} { 25 | font-size: ${fontSize('medium')}; 26 | } 27 | 28 | &._is-current { 29 | font-weight: ${fontWeight('bold')}; 30 | } 31 | } 32 | ` 33 | 34 | const Locales = () => { 35 | const context = usePageContext() 36 | const links = get(context, 'locales', []).map((locale) => { 37 | return { 38 | ...locale, 39 | link: locale.path + context.basePath, 40 | isCurrent: locale.locale === context.locale, 41 | } 42 | }) 43 | 44 | return ( 45 | 46 | {links.map(({ label, id, link, isCurrent, completion }) => ( 47 | 48 | {label} 49 | {completion < 95 && ( 50 | 54 | )} 55 | 56 | ))} 57 | 58 | ) 59 | } 60 | 61 | export default Locales 62 | -------------------------------------------------------------------------------- /src/core/share/ShareTwitter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { useI18n } from 'core/i18n/i18nContext' 4 | import track from './tracking' 5 | import ShareLink from './ShareLink' 6 | 7 | const ShareTwitter = ({ text, trackingId }) => { 8 | const { translate } = useI18n() 9 | 10 | return ( 11 | 19 | 32 | {translate('share.twitter')} 33 | 34 | ) 35 | } 36 | 37 | ShareTwitter.propTypes = { 38 | text: PropTypes.string.isRequired, 39 | trackingId: PropTypes.string, 40 | } 41 | 42 | export default ShareTwitter 43 | -------------------------------------------------------------------------------- /src/core/blocks/other/BioBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | // import ReactMarkdown from 'react-markdown' 3 | // import rehypeRaw from 'rehype-raw' 4 | import styled from 'styled-components' 5 | import { mq, spacing, fontSize } from 'core/theme' 6 | 7 | const BioBlock = ({ subheading, heading, photo, bio }) => { 8 | return ( 9 | 10 | {subheading &&

{subheading}

} 11 | 12 | 13 | {heading} 14 | 15 | 16 | 20 | {/* {bio} */} 21 | {bio} 22 | 23 | 24 |
25 | ) 26 | } 27 | 28 | const Bio = styled.div` 29 | background: ${({ theme }) => theme.colors.backgroundAlt}; 30 | margin-top: ${spacing(2)}; 31 | box-shadow: ${({ theme }) => theme.blockShadow}; 32 | ` 33 | 34 | const BioHeading = styled.h3` 35 | margin-bottom: ${spacing(0.33)}; 36 | ` 37 | 38 | const BioContent = styled.div` 39 | @media ${mq.large} { 40 | display: grid; 41 | grid-template-columns: 170px auto; 42 | } 43 | 44 | p:last-child { 45 | margin: 0; 46 | } 47 | ` 48 | 49 | const BioBio = styled.div` 50 | padding: ${spacing()}; 51 | font-size: ${fontSize('smallish')}; 52 | ` 53 | 54 | const BioPhoto = styled.div` 55 | overflow: hidden; 56 | 57 | @media ${mq.smallMedium} { 58 | max-width: 120px; 59 | margin: 0 auto; 60 | padding-top: ${spacing()}; 61 | } 62 | 63 | img { 64 | display: block; 65 | width: 100%; 66 | } 67 | ` 68 | 69 | export default BioBlock 70 | -------------------------------------------------------------------------------- /src/core/blocks/tools/SectionToolsCardinalityByUserBlock/SectionToolsCardinalityByUserBlock.tsx: -------------------------------------------------------------------------------- 1 | import React, {useMemo, useState} from 'react' 2 | import { Pie } from '@nivo/pie' 3 | // @ts-ignore 4 | import Block from 'core/blocks/block/Block' 5 | import { BlockContext } from 'core/blocks/types' 6 | import { ToolsCardinalityByUserBucket } from 'core/survey_api/tools' 7 | 8 | interface SectionToolsCardinalityByUserBlockProps { 9 | block: BlockContext< 10 | 'sectionToolsCardinalityByUserTemplate', 11 | 'SectionToolsCardinalityByUserBlock' 12 | > 13 | data: ToolsCardinalityByUserBucket[] 14 | units?: 'percentage' | 'count' 15 | } 16 | 17 | export const SectionToolsCardinalityByUserBlock = ({ 18 | block, 19 | data, 20 | units: defaultUnits = 'percentage', 21 | }: SectionToolsCardinalityByUserBlockProps) => { 22 | const [units, setUnits] = useState(defaultUnits) 23 | const [view, setView] = useState('viz') 24 | 25 | // exclude datums with a percentage lower than 1 26 | const filteredData = useMemo(() => data.filter(datum => datum.percentage >= 1).reverse(), [data]) 27 | 28 | return ( 29 | 40 | ({ 42 | id: datum.cardinality, 43 | value: datum.count, 44 | }))} 45 | height={400} 46 | width={400} 47 | innerRadius={0.6} 48 | colors={{ 49 | scheme: 'blues' 50 | }} 51 | margin={{ 52 | top: 40, 53 | right: 40, 54 | bottom: 40, 55 | left: 40, 56 | }} 57 | /> 58 | 59 | ) 60 | } -------------------------------------------------------------------------------- /src/core/charts/generic/UsageVariationsChart/multiple-diverging-lines/Labels.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Margin } from '@nivo/core' 3 | import { ComputedDatum } from './types' 4 | import { useTheme } from 'styled-components' 5 | 6 | interface LabelsProps { 7 | data: ComputedDatum[] 8 | itemHeight: number 9 | width: number 10 | margin: Margin 11 | } 12 | 13 | const textStyle = { 14 | fontSize: 12, 15 | fontWeight: 600, 16 | } 17 | 18 | export const Labels = ({ data, itemHeight, width, margin }: LabelsProps) => { 19 | const theme = useTheme() 20 | 21 | return ( 22 | 23 | {data.map((datum) => { 24 | return ( 25 | 29 | 37 | 38 | 46 | {datum.name} 47 | 48 | 49 | {datum.name} 50 | 51 | 52 | ) 53 | })} 54 | 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /src/stylesheets/to-remove/_colors.scss: -------------------------------------------------------------------------------- 1 | /*****************************************************************************\ 2 | base colors 3 | \*****************************************************************************/ 4 | 5 | $greyLight: #e0e4e4; 6 | $grey: #e1e1e1; 7 | $greyMedium: #cecdcc; 8 | $greyMediumer: #616868; 9 | $greyMediumest: #5c6069; 10 | $greyDark: #3d4049; 11 | $greyDarkish: #2a2d33; 12 | $greyDarker: #212227; 13 | 14 | $greyTeal: #a3cacd; 15 | 16 | $blueLighter: #B2BBEE; 17 | $blueLight: #808EE1; 18 | $blue: #576DE7; 19 | $blueDark: #273aa2; 20 | 21 | $pinkLightest: #D3BBF2; 22 | $pinkLighter: #D68DF0; 23 | $pinkLight: #EC75CB; 24 | $pink: #F95DB2; 25 | 26 | $greenLighter: #E7FFED; 27 | $greenLight: #ACFFC3; 28 | $green: #85EBA2; 29 | $greenDark: #59DF7F; 30 | 31 | $tealLighter: #94eeee; 32 | $tealLight: #65e0e0; 33 | $teal: #41c7c7; 34 | $tealDark: #2ba7a7; 35 | $tealDarker: #1d7e7e; 36 | 37 | $purpleLight: #B096E7; 38 | $purple: #7854C3; 39 | $purpleDark: #57457C; 40 | 41 | $redLighter: #f8a8a8; 42 | $redLight: #fc8f8f; 43 | $red: #fe6a6a; 44 | $redDark: #ec5555; 45 | $redDarker: #D13F3F; 46 | 47 | $yellow: #fbf34c; 48 | $yellowDark: #d4cc33; 49 | $aqua: #1ea0f2; 50 | 51 | $white: #ffffff; 52 | $black: #000000; 53 | 54 | $navyLightest: #7e86ad; 55 | $navyLighter: #484F73; 56 | $navyLight: #303652; 57 | $navy: #232840; 58 | $navyDark: #1a1f35; 59 | 60 | /*****************************************************************************\ 61 | "functional" colors 62 | \*****************************************************************************/ 63 | 64 | 65 | $active-color: $teal; 66 | $active-color-dark: $tealDark; 67 | 68 | $contrast-color: $red; 69 | $contrast-color-dark: $redDark; 70 | 71 | $bg-color: $greyDarker; 72 | $bg-color-light: $greyDarkish; 73 | $bg-color-lighter: $greyDark; 74 | $bg-color-inverted: $greyLight; 75 | 76 | $border-color: #9ac6c9; 77 | $border-color-dark: $greyMediumest; 78 | 79 | $text-color: $grey; 80 | $text-color-dark: $greyMediumer; 81 | $text-color-inverted: $greyDarker; 82 | 83 | $link-color: $active-color; 84 | $hover-color: $contrast-color; 85 | -------------------------------------------------------------------------------- /src/core/share/ShareEmail.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { useI18n } from 'core/i18n/i18nContext' 4 | import track from './tracking' 5 | import ShareLink from './ShareLink' 6 | 7 | const ShareEmail = ({ subject, body, trackingId }) => { 8 | const { translate } = useI18n() 9 | 10 | return ( 11 | 18 | 31 | {translate('share.email')} 32 | 33 | ) 34 | } 35 | 36 | ShareEmail.propTypes = { 37 | subject: PropTypes.string.isRequired, 38 | body: PropTypes.string.isRequired, 39 | trackingId: PropTypes.string, 40 | } 41 | 42 | export default ShareEmail 43 | -------------------------------------------------------------------------------- /src/core/charts/features/FeaturesCirclePackingChartTooltip.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, { useTheme } from 'styled-components' 3 | import round from 'lodash/round' 4 | import { useTheme as useNivoTheme } from '@nivo/core' 5 | import { useI18n } from 'core/i18n/i18nContext' 6 | import { spacing, fontWeight } from 'core/theme' 7 | 8 | const Chip = ({ color, color2 }) => ( 9 | 10 | 11 | {color2 && } 12 | 13 | ) 14 | 15 | export const FeaturesCirclePackingChartTooltip = (props) => { 16 | const { translate } = useI18n() 17 | const { data } = props 18 | const { name, awareness, usage } = data 19 | const nivoTheme = useNivoTheme() 20 | const theme = useTheme() 21 | const color = theme.colors.ranges.featureSections[data.sectionId] 22 | 23 | return ( 24 |
25 |
26 | {name} 27 | 28 | 29 | {translate('options.features_simplified.know_it')} 30 | {awareness} 31 | 32 | 33 | {translate('options.features_simplified.used_it')} 34 | {usage} 35 | 36 | 37 | {translate('options.features_simplified.usage_ratio')} 38 | {round((usage / awareness) * 100, 1)}% 39 | 40 |
41 |
42 | ) 43 | } 44 | 45 | const Heading = styled.h4` 46 | margin-bottom: ${spacing(0.25)}; 47 | ` 48 | 49 | const Grid = styled.div` 50 | display: grid; 51 | align-items: center; 52 | grid-template-columns: 12px auto auto; 53 | column-gap: ${spacing(0.5)}; 54 | ` 55 | 56 | const Value = styled.span` 57 | font-weight: ${fontWeight('bold')}; 58 | ` 59 | -------------------------------------------------------------------------------- /src/core/charts/hooks.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | import ceil from 'lodash/ceil' 3 | // @ts-ignore 4 | import { useI18n } from 'core/i18n/i18nContext' 5 | 6 | export const useBarChart = ({ 7 | buckets, 8 | total, 9 | mode, 10 | units, 11 | i18nNamespace, 12 | shouldTranslate, 13 | }: { 14 | buckets: { 15 | count: number 16 | percentage: number 17 | }[] 18 | total: number 19 | mode: 'relative' | 'absolute' 20 | units: 'count' | 'percentage' 21 | i18nNamespace: string 22 | shouldTranslate?: boolean 23 | }) => { 24 | const { translate } = useI18n() 25 | 26 | const formatTick = useMemo(() => { 27 | if (shouldTranslate !== true) { 28 | return (value: string | number) => `${value}` 29 | } 30 | 31 | // automatically pick from `options` using 32 | // the provided namespace. 33 | return (value: string | number) => translate(`options.${i18nNamespace}.${value}.short`) 34 | }, [translate, shouldTranslate, i18nNamespace]) 35 | 36 | const formatValue = useMemo(() => { 37 | if (units === 'percentage') { 38 | return (value: string | number) => `${value}%` 39 | } 40 | 41 | return '.2s' 42 | }, [units]) 43 | 44 | const maxValue = useMemo(() => { 45 | if (units === 'percentage') { 46 | if (mode === 'absolute') { 47 | return 100 48 | } 49 | 50 | const maxBucketPercentage = Math.max(...buckets.map((b) => b.percentage)) 51 | 52 | return ceil(maxBucketPercentage, -1) 53 | } 54 | 55 | if (mode === 'absolute') { 56 | return ceil(total, -3) 57 | } 58 | 59 | const maxBucketCount = Math.max(...buckets.map((b) => b.count)) 60 | const precision = `${maxBucketCount}`.length - 1 61 | 62 | return ceil(maxBucketCount, -precision) 63 | }, [buckets, total, mode, units]) 64 | 65 | const tickCount = 6 66 | 67 | const ticks = Array.from({ length: tickCount }, (_, i) => 68 | Math.round((i * maxValue) / tickCount) 69 | ) 70 | 71 | return { formatTick, formatValue, maxValue, tickCount, ticks } 72 | } 73 | -------------------------------------------------------------------------------- /src/core/report/ReportHeading.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { mq, spacing, fontSize, fontWeight } from 'core/theme' 4 | // import { logoElements } from 'core/components/Logo' 5 | import sample from 'lodash/sample' 6 | import random from 'lodash/random' 7 | 8 | export default ({ children }) => { 9 | const [part1, part2] = children.split('|') 10 | return ( 11 | 12 | {/* 13 | {[1, 2, 3].map((i) => { 14 | const Component = sample(logoElements) 15 | return ( 16 |
24 | 25 |
26 | ) 27 | })} 28 |
*/} 29 | 30 | {part1.trim()} {part2.trim()} 31 | 32 |
33 | ) 34 | } 35 | 36 | const Heading = styled.h2` 37 | text-align: center; 38 | font-size: ${fontSize('huge')}; 39 | width: 100vw; 40 | margin-left: calc(50% - 50vw); 41 | padding: 0 ${spacing(2)}; 42 | font-weight: ${fontWeight('bold')}; 43 | display: flex; 44 | flex-direction: column; 45 | align-items: center; 46 | position: relative; 47 | overflow: visible !important; 48 | ` 49 | 50 | const LogoElements = styled.div` 51 | position: absolute !important; 52 | opacity: 0.5; 53 | ` 54 | const LogoContents = styled.div`` 55 | 56 | const Part1 = styled.span` 57 | display: block; 58 | position: relative; 59 | /* left: -${spacing(6)}; */ 60 | ` 61 | 62 | const Part2 = styled.span` 63 | display: block; 64 | position: relative; 65 | /* right: -${spacing(6)}; */ 66 | ` 67 | -------------------------------------------------------------------------------- /src/core/blocks/generic/HeatmapBlock.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react' 2 | import PropTypes from 'prop-types' 3 | import Block from 'core/blocks/block/Block' 4 | import HeatmapChart from 'core/charts/generic/HeatmapChart' 5 | import { useI18n } from 'core/i18n/i18nContext' 6 | import ChartContainer from 'core/charts/ChartContainer' 7 | import { useBucketKeys } from 'core/helpers/useBucketKeys' 8 | 9 | const HeatmapBlock = ({ block, data }) => { 10 | const { translate } = useI18n() 11 | 12 | const { subject, heatmapId } = block.variables 13 | 14 | const title = translate(`blocks.${subject}_${heatmapId}_heatmap.title`) 15 | const description = translate(`blocks.${subject}_${heatmapId}_heatmap.description`) 16 | 17 | const bucketKeys = useBucketKeys(heatmapId) 18 | 19 | return ( 20 | 21 | 22 | 27 | 28 | 29 | ) 30 | } 31 | 32 | HeatmapBlock.propTypes = { 33 | block: PropTypes.shape({ 34 | id: PropTypes.string.isRequired, 35 | variables: PropTypes.shape({ 36 | subject: PropTypes.oneOf(['tools', 'features']).isRequired, 37 | heatmapId: PropTypes.string.isRequired, 38 | }).isRequired, 39 | }).isRequired, 40 | data: PropTypes.shape({ 41 | year: PropTypes.number.isRequired, 42 | buckets: PropTypes.arrayOf( 43 | PropTypes.shape({ 44 | id: PropTypes.string.isRequired, 45 | total: PropTypes.number.isRequired, 46 | ranges: PropTypes.arrayOf( 47 | PropTypes.shape({ 48 | range: PropTypes.string.isRequired, 49 | count: PropTypes.number.isRequired, 50 | percentage: PropTypes.number.isRequired, 51 | }) 52 | ).isRequired, 53 | }) 54 | ).isRequired, 55 | }), 56 | } 57 | 58 | export default memo(HeatmapBlock) 59 | -------------------------------------------------------------------------------- /surveys/css2020/config/picks.yml: -------------------------------------------------------------------------------- 1 | - twitterName: shadeed9 2 | fullName: Ahmad Shadeed 3 | avatarUrl: shadeed9.jpg 4 | url: https://www.bram.us/2020/11/05/container-queries-are-coming-to-chromium/ 5 | pickName: Container Queries 6 | 7 | - twitterName: argyleink 8 | fullName: Adam Argyle 9 | avatarUrl: argyleink.jpg 10 | url: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties/Basic_concepts 11 | pickName: Logical Properties 12 | 13 | - twitterName: sachagreif 14 | fullName: Sacha Greif 15 | avatarUrl: sachagreif.jpg 16 | url: https://wattenberger.com/blog/css-cascade 17 | pickName: The CSS Cascade 18 | 19 | - twitterName: kilianvalkhof 20 | fullName: Kilian Valkhof 21 | avatarUrl: kilianvalkhof.jpg 22 | url: https://web.dev/content-visibility/ 23 | pickName: Content-Visibility 24 | 25 | - twitterName: christianoliff 26 | fullName: Christian Oliff 27 | avatarUrl: christianoliff.jpg 28 | url: https://purgecss.com/ 29 | pickName: PurgeCSS 30 | 31 | - twitterName: walterstephanie 32 | fullName: Stéphanie Walter 33 | avatarUrl: walterstephanie.jpg 34 | url: https://www.youtube.com/c/LayoutLand 35 | pickName: Jen Simmons' Layout Land 36 | 37 | - twitterName: piccalilli_ 38 | fullName: Andy Bell 39 | avatarUrl: piccalilli_.jpg 40 | url: https://css-irl.info/ 41 | pickName: CSS IRL 42 | 43 | - twitterName: sarasoueidan 44 | fullName: Sara Soueidan 45 | avatarUrl: sarasoueidan.jpg 46 | url: https://rachelandrew.co.uk/ 47 | pickName: Rachel Andrew 48 | 49 | - twitterName: 5t3ph 50 | fullName: Stephanie Eckles 51 | avatarUrl: 5t3ph.jpg 52 | url: https://www.youtube.com/watch?v=o6ssu5oKyaU 53 | pickName: Writing even more CSS with Accessibility in Mind 54 | 55 | - twitterName: hugogiraudel 56 | fullName: Hugo “Kitty” Giraudel 57 | avatarUrl: hugogiraudel.jpg 58 | url: https://fela.js.org/ 59 | pickName: Fela 60 | 61 | - twitterName: foolip 62 | fullName: Philip Jägenstedt 63 | avatarUrl: foolip.jpg 64 | url: https://blogs.igalia.com/svillar/2020/10/01/closing-the-gap-in-flexbox/ 65 | pickName: Sergio Villar 66 | 67 | - twitterName: jina 68 | fullName: Jina Anne 69 | avatarUrl: jina.jpg 70 | url: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion 71 | pickName: prefers-reduced-motion 72 | -------------------------------------------------------------------------------- /surveys/css2021/config/picks.yml: -------------------------------------------------------------------------------- 1 | - twitterName: shadeed9 2 | fullName: Ahmad Shadeed 3 | avatarUrl: shadeed9.jpg 4 | url: https://www.bram.us/2020/11/05/container-queries-are-coming-to-chromium/ 5 | pickName: Container Queries 6 | 7 | - twitterName: argyleink 8 | fullName: Adam Argyle 9 | avatarUrl: argyleink.jpg 10 | url: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties/Basic_concepts 11 | pickName: Logical Properties 12 | 13 | - twitterName: sachagreif 14 | fullName: Sacha Greif 15 | avatarUrl: sachagreif.jpg 16 | url: https://wattenberger.com/blog/css-cascade 17 | pickName: The CSS Cascade 18 | 19 | - twitterName: kilianvalkhof 20 | fullName: Kilian Valkhof 21 | avatarUrl: kilianvalkhof.jpg 22 | url: https://web.dev/content-visibility/ 23 | pickName: Content-Visibility 24 | 25 | - twitterName: christianoliff 26 | fullName: Christian Oliff 27 | avatarUrl: christianoliff.jpg 28 | url: https://purgecss.com/ 29 | pickName: PurgeCSS 30 | 31 | - twitterName: walterstephanie 32 | fullName: Stéphanie Walter 33 | avatarUrl: walterstephanie.jpg 34 | url: https://www.youtube.com/c/LayoutLand 35 | pickName: Jen Simmons' Layout Land 36 | 37 | - twitterName: piccalilli_ 38 | fullName: Andy Bell 39 | avatarUrl: piccalilli_.jpg 40 | url: https://css-irl.info/ 41 | pickName: CSS IRL 42 | 43 | - twitterName: sarasoueidan 44 | fullName: Sara Soueidan 45 | avatarUrl: sarasoueidan.jpg 46 | url: https://rachelandrew.co.uk/ 47 | pickName: Rachel Andrew 48 | 49 | - twitterName: 5t3ph 50 | fullName: Stephanie Eckles 51 | avatarUrl: 5t3ph.jpg 52 | url: https://www.youtube.com/watch?v=o6ssu5oKyaU 53 | pickName: Writing even more CSS with Accessibility in Mind 54 | 55 | - twitterName: hugogiraudel 56 | fullName: Hugo “Kitty” Giraudel 57 | avatarUrl: hugogiraudel.jpg 58 | url: https://fela.js.org/ 59 | pickName: Fela 60 | 61 | - twitterName: foolip 62 | fullName: Philip Jägenstedt 63 | avatarUrl: foolip.jpg 64 | url: https://blogs.igalia.com/svillar/2020/10/01/closing-the-gap-in-flexbox/ 65 | pickName: Sergio Villar 66 | 67 | - twitterName: jina 68 | fullName: Jina Anne 69 | avatarUrl: jina.jpg 70 | url: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion 71 | pickName: prefers-reduced-motion 72 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolPeriodicElement.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styled from 'styled-components' 4 | import { navigate } from 'gatsby' 5 | import { fontWeight } from 'core/theme' 6 | 7 | const PeriodicElement = ({ className, name = '', symbol, number, size, path, x, y }) => { 8 | return ( 9 | { 19 | if (path) { 20 | navigate(path) 21 | } 22 | }} 23 | > 24 | 25 | 26 | {number} 27 | 28 | 35 | {symbol} 36 | 37 | 45 | 46 | ) 47 | } 48 | 49 | PeriodicElement.propTypes = { 50 | name: PropTypes.string, 51 | symbol: PropTypes.string.isRequired, 52 | } 53 | 54 | const Frame = styled.rect` 55 | stroke-width: 1px; 56 | stroke: ${({ theme }) => theme.colors.border}; 57 | fill: ${({ theme }) => theme.colors.background}; 58 | ` 59 | 60 | const NumberNode = styled.text` 61 | opacity: 0.6; 62 | pointer-events: none; 63 | fill: white; 64 | ` 65 | 66 | const Symbol = styled.text` 67 | font-weight: ${fontWeight('bold')}; 68 | pointer-events: none; 69 | fill: ${({ theme }) => theme.colors.link}; 70 | ` 71 | 72 | const Label = styled.text` 73 | opacity: 0.6; 74 | pointer-events: none; 75 | fill: ${({ theme }) => theme.colors.text}; 76 | ` 77 | 78 | export default PeriodicElement 79 | -------------------------------------------------------------------------------- /src/core/entities/entitiesContext.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useCallback, useMemo } from 'react' 2 | import { StaticQuery, graphql } from 'gatsby' 3 | 4 | export const EntitiesContext = createContext() 5 | 6 | const entitiesQuery = graphql` 7 | query { 8 | entities: surveyApi { 9 | entities { 10 | id 11 | name 12 | homepage 13 | mdn { 14 | url 15 | } 16 | } 17 | } 18 | } 19 | ` 20 | 21 | const findEntity = (entities, key) => 22 | entities.find(({ id, name }) => { 23 | const lowerCaseKey = key.toLowerCase() 24 | const idMatch = id.toLowerCase() === lowerCaseKey 25 | const nameMatch = name.toLowerCase() === lowerCaseKey 26 | 27 | return idMatch || nameMatch 28 | }) 29 | 30 | const EntitiesContextProviderInner = ({ children, entities }) => { 31 | const getEntity = useCallback( 32 | (id) => { 33 | const entity = findEntity(entities, id) 34 | 35 | return entity 36 | }, 37 | [entities] 38 | ) 39 | 40 | const getName = useCallback( 41 | (id) => { 42 | const entity = findEntity(entities, id) 43 | 44 | return (entity && entity.name) || id 45 | }, 46 | [entities] 47 | ) 48 | 49 | const getUrl = useCallback( 50 | (id) => { 51 | const entity = findEntity(entities, id) 52 | 53 | return (entity && entity.homepage) || null 54 | }, 55 | [entities] 56 | ) 57 | 58 | const value = useMemo( 59 | () => ({ 60 | getEntity, 61 | getName, 62 | getUrl, 63 | }), 64 | [getName, getUrl, getEntity] 65 | ) 66 | 67 | return {children} 68 | } 69 | 70 | export const EntitiesContextProvider = ({ children }) => { 71 | return ( 72 | 73 | {({ entities: { entities } }) => ( 74 | 75 | {children} 76 | 77 | )} 78 | 79 | ) 80 | } 81 | 82 | export const useEntities = () => useContext(EntitiesContext) 83 | -------------------------------------------------------------------------------- /src/core/charts/tools/ToolExperienceGraphChart/YearsLayer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { useTheme } from 'styled-components' 4 | 5 | const YearLabel = ({ year, x }) => { 6 | const theme = useTheme() 7 | 8 | return ( 9 | 10 | {year} 11 | 12 | ) 13 | } 14 | 15 | YearLabel.propTypes = { 16 | year: PropTypes.number.isRequired, 17 | x: PropTypes.number.isRequired, 18 | } 19 | 20 | const YearsLayer = ({ nodes, height }) => { 21 | const allYears = [] 22 | nodes.forEach((node) => { 23 | if (!allYears.includes(node.year)) { 24 | allYears.push(node.year) 25 | } 26 | }) 27 | allYears.sort() 28 | 29 | const yearLegends = allYears.map((year) => { 30 | const node = nodes.find((n) => n.year === year) 31 | 32 | return { 33 | year, 34 | x: node.x0 + (node.x1 - node.x0) / 2, 35 | } 36 | }) 37 | 38 | return ( 39 | <> 40 | 41 | {yearLegends.map((yearLegend) => ( 42 | 43 | ))} 44 | 45 | {yearLegends.map((yearLegend) => ( 46 | 55 | ))} 56 | 57 | {yearLegends.map((yearLegend) => ( 58 | 59 | ))} 60 | 61 | 62 | ) 63 | } 64 | 65 | YearsLayer.propTypes = { 66 | height: PropTypes.number.isRequired, 67 | nodes: PropTypes.arrayOf( 68 | PropTypes.shape({ 69 | year: PropTypes.number.isRequired, 70 | x0: PropTypes.number.isRequired, 71 | x1: PropTypes.number.isRequired, 72 | }) 73 | ).isRequired, 74 | } 75 | 76 | export default YearsLayer 77 | -------------------------------------------------------------------------------- /src/core/charts/tools/ToolExperienceGraphChart/ToolExperienceGraphChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ResponsiveSankey } from '@nivo/sankey' 3 | import { useTheme } from 'styled-components' 4 | import { keys } from 'core/bucket_keys' 5 | import YearsLayer from './YearsLayer' 6 | import NodeTooltip from './NodeTooltip' 7 | import LinkTooltip from './LinkTooltip' 8 | 9 | const sortedExperienceKeys = [ 10 | 'never_heard', 11 | 'not_interested', 12 | 'interested', 13 | 'would_not_use', 14 | 'would_use', 15 | ] 16 | 17 | const getColor = (d) => { 18 | // it's a node 19 | if (d.id) { 20 | return keys.tools.keys.find((xp) => xp.id === d.experience).color 21 | } 22 | 23 | // otherwise it's a link 24 | // the returned color for links does not matter as link gradients 25 | // are enabled and will use source/target node colors 26 | return '#000000' 27 | } 28 | 29 | const minNodeValueOnTop = (nodeA, nodeB) => { 30 | return ( 31 | sortedExperienceKeys.findIndex((xp) => xp === nodeA.experience) - 32 | sortedExperienceKeys.findIndex((xp) => xp === nodeB.experience) 33 | ) 34 | } 35 | 36 | const ToolExperienceGraphChart = ({ data }) => { 37 | const theme = useTheme() 38 | 39 | const links = data.links.map((link) => ({ 40 | ...link, 41 | value: link.count, 42 | })) 43 | 44 | return ( 45 | } 64 | linkContract={1} 65 | linkBlendMode="screen" 66 | enableLinkGradient 67 | linkOpacity={0.75} 68 | linkHoverOpacity={1} 69 | linkTooltip={(link) => } 70 | /> 71 | ) 72 | } 73 | 74 | export default ToolExperienceGraphChart 75 | -------------------------------------------------------------------------------- /src/core/blocks/tools/ToolExperienceGraphBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { useI18n } from 'core/i18n/i18nContext' 4 | import Block from 'core/blocks/block/Block' 5 | import ChartContainer from 'core/charts/ChartContainer' 6 | import ToolExperienceGraphChart from 'core/charts/tools/ToolExperienceGraphChart' 7 | 8 | const ToolExperienceGraphBlock = ({ block, data }) => { 9 | const { translate } = useI18n() 10 | 11 | // skip if there's not enough data to show the block 12 | if (data.experienceGraph.nodes.length === 0) { 13 | return null 14 | } 15 | 16 | const toolName = data.entity.name 17 | const title = translate(`block.title.${block.blockName}`, { values: { name: toolName } }) 18 | const description = translate(`block.description.${block.blockName}`, { 19 | values: { name: toolName }, 20 | }) 21 | 22 | return ( 23 | 32 | 33 | 34 | 35 | 36 | ) 37 | } 38 | 39 | ToolExperienceGraphBlock.propTypes = { 40 | block: PropTypes.shape({ 41 | id: PropTypes.string.isRequired, 42 | blockName: PropTypes.string.isRequired, 43 | dataPath: PropTypes.string.isRequired, 44 | }).isRequired, 45 | data: PropTypes.shape({ 46 | entity: PropTypes.shape({ 47 | name: PropTypes.string.isRequired, 48 | }).isRequired, 49 | experienceGraph: PropTypes.shape({ 50 | nodes: PropTypes.arrayOf( 51 | PropTypes.shape({ 52 | id: PropTypes.string.isRequired, 53 | year: PropTypes.number.isRequired, 54 | experience: PropTypes.string.isRequired, 55 | }) 56 | ).isRequired, 57 | links: PropTypes.arrayOf( 58 | PropTypes.shape({ 59 | source: PropTypes.string.isRequired, 60 | target: PropTypes.string.isRequired, 61 | count: PropTypes.number.isRequired, 62 | }) 63 | ).isRequired, 64 | }).isRequired, 65 | }).isRequired, 66 | } 67 | 68 | export default ToolExperienceGraphBlock 69 | -------------------------------------------------------------------------------- /src/core/share/SharePermalink.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import mq from 'core/theme/mq' 4 | import track from './tracking' 5 | import { useI18n } from 'core/i18n/i18nContext' 6 | 7 | const Link = styled.a` 8 | float: left; 9 | opacity: 0; 10 | line-height: 1; 11 | width: 22px; 12 | margin-left: -30px; 13 | margin-right: 8px; 14 | transition: none; 15 | position: relative; 16 | 17 | @media ${mq.small} { 18 | /* top: 3px; */ 19 | display: none; 20 | } 21 | @media ${mq.mediumLarge} { 22 | top: 1px; 23 | } 24 | 25 | path { 26 | fill: rgba(255, 255, 255, 0.4); 27 | } 28 | ` 29 | 30 | const SharePermalink = ({ trackingId, url }) => { 31 | const { translate } = useI18n() 32 | 33 | return ( 34 | 40 | 49 | {translate('share.link')} 50 | 51 | ) 52 | } 53 | 54 | export default SharePermalink 55 | -------------------------------------------------------------------------------- /src/core/i18n/T.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useI18n } from 'core/i18n/i18nContext' 3 | // import ReactMarkdown from 'react-markdown' 4 | // import rehypeRaw from 'rehype-raw' 5 | import { useKeydownContext } from 'core/helpers/keydownContext' 6 | 7 | const getGitHubSearchUrl = (k, localeId) => 8 | `https://github.com/search?q=${k}+repo%3AStateOfJS%2Fstate-of-js-graphql-results-api+path%3A%2Fsrc%2Fi18n%2F${localeId}%2F+path%3A%2Fsrc%2Fi18n%2Fen-US%2F&type=Code&ref=advsearch&l=&l=` 9 | 10 | const T = ({ t: override, k, values, md = false, html = false, fallback }) => { 11 | const { getString } = useI18n() 12 | const { modKeyDown } = useKeydownContext() 13 | 14 | // accept override to just use provided string as translation result 15 | let t = override 16 | const props = { 17 | 'data-key': k, 18 | } 19 | const classNames = ['t'] 20 | 21 | if (t) { 22 | classNames.push('t-override') 23 | } else { 24 | const tString = getString(k, { values }, fallback) 25 | 26 | const handleClick = (e) => { 27 | // note: `fallback` here denotes whether a string is itself a fallback for a missing string 28 | if (modKeyDown) { 29 | e.preventDefault() 30 | e.stopPropagation() 31 | window.open(getGitHubSearchUrl(k, tString.locale.id)) 32 | } 33 | } 34 | 35 | if (!tString.t || tString.fallback) { 36 | props.onClick = handleClick 37 | props.title = 'Cmd/ctrl-click to add missing translation' 38 | classNames.push(modKeyDown ? 't-modkeydown' : 't-modkeyup') 39 | if (!tString.t) { 40 | // no translation was found 41 | t = `[${tString.locale.id}] ${k}` 42 | classNames.push('t-missing') 43 | } else if (tString.fallback) { 44 | // a translation was found, but it's a fallback placeholder 45 | t = tString.t 46 | classNames.push('t-fallback') 47 | } 48 | } else { 49 | t = md ? tString.tHtml : tString.t 50 | } 51 | } 52 | 53 | props.className = classNames.join(' ') 54 | 55 | //{t} 56 | 57 | return md ? ( 58 |
59 | ) : html ? ( 60 | 61 | ) : ( 62 | {t} 63 | ) 64 | } 65 | 66 | export default T 67 | -------------------------------------------------------------------------------- /src/core/charts/demographics/ParticipationByCountryChart.js: -------------------------------------------------------------------------------- 1 | import React, { memo, useMemo } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { useTheme } from 'styled-components' 4 | import { ResponsiveChoroplethCanvas } from '@nivo/geo' 5 | import countries from 'data/geo/world_countries' 6 | import ParticipationByCountryTooltip from './ParticipationByCountryTooltip' 7 | 8 | const features = countries.features.map((feature) => { 9 | return { 10 | ...feature, 11 | id: feature.id, 12 | } 13 | }) 14 | 15 | const chartLegends = [ 16 | { 17 | anchor: 'bottom-left', 18 | direction: 'column', 19 | translateX: 30, 20 | translateY: -30, 21 | itemsSpacing: 0, 22 | itemWidth: 100, 23 | itemHeight: 18, 24 | itemDirection: 'left-to-right', 25 | symbolSize: 18, 26 | justify: true, 27 | }, 28 | ] 29 | 30 | const ParticipationByCountryChart = ({ units, data }) => { 31 | const theme = useTheme() 32 | 33 | const mergedTheme = { 34 | ...theme.charts, 35 | // background: theme.colors.backgroundAlt, 36 | } 37 | 38 | const colorRange = theme.colors.countries 39 | 40 | const formatValue = useMemo(() => { 41 | if (units === 'percentage') return (v) => `${v.toFixed(1)}%` 42 | return (v) => Math.round(v) 43 | }, [units]) 44 | 45 | return ( 46 | 64 | ) 65 | } 66 | 67 | ParticipationByCountryChart.propTypes = { 68 | units: PropTypes.oneOf(['count', 'percentage']).isRequired, 69 | data: PropTypes.arrayOf( 70 | PropTypes.shape({ 71 | id: PropTypes.string.isRequired, 72 | count: PropTypes.number.isRequired, 73 | percentage: PropTypes.number.isRequired, 74 | }) 75 | ).isRequired, 76 | } 77 | 78 | export default memo(ParticipationByCountryChart) 79 | --------------------------------------------------------------------------------
{heading.label}
{row.find(r => r.id === cell.id)?.label}{row.find(r => r.id === cell.id).label}