├── .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 |
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 |
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) => | {heading.label} | )}
19 |
20 |
21 |
22 | {table.rows.map((row) => {
23 | return
24 | {table.headings.map((cell, index) => {
25 | return index > 0
26 | ? | {row.find(r => r.id === cell.id)?.label} |
27 | : {row.find(r => r.id === cell.id).label} |
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 |
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 |
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 |
--------------------------------------------------------------------------------