├── src ├── table-object │ ├── index.scss │ └── table-object.scss ├── header │ ├── index.scss │ ├── README.md │ └── header.scss ├── toasts │ ├── index.scss │ └── README.md ├── box │ ├── index.scss │ ├── README.md │ └── box-overlay.scss ├── loaders │ ├── index.scss │ ├── loaders.scss │ └── README.md ├── marketing │ ├── utilities │ │ ├── filters.scss │ │ ├── borders.scss │ │ ├── index.scss │ │ ├── animations.scss │ │ └── layout.scss │ ├── support │ │ └── index.scss │ ├── links │ │ ├── index.scss │ │ └── link.scss │ ├── buttons │ │ └── index.scss │ ├── type │ │ └── index.scss │ ├── index.scss │ └── README.md ├── tooltips │ ├── index.scss │ └── README.md ├── color-modes │ ├── themes │ │ ├── dark.scss │ │ ├── light.scss │ │ ├── dark_dimmed.scss │ │ ├── dark_colorblind.scss │ │ ├── dark_tritanopia.scss │ │ ├── light_colorblind.scss │ │ ├── light_tritanopia.scss │ │ ├── dark_high_contrast.scss │ │ └── light_high_contrast.scss │ └── index.scss ├── autocomplete │ ├── index.scss │ ├── README.md │ └── suggester.scss ├── branch-name │ ├── index.scss │ ├── README.md │ └── branch-name.scss ├── pagination │ ├── index.scss │ └── README.md ├── select-menu │ ├── index.scss │ └── README.md ├── labels │ ├── index.scss │ ├── issue-labels.scss │ ├── README.md │ └── mixins.scss ├── buttons │ ├── index.scss │ ├── README.md │ └── button-group.scss ├── avatars │ ├── index.scss │ ├── avatar-parent-child.scss │ ├── README.md │ └── circle-badge.scss ├── base │ ├── octicons.scss │ ├── index.scss │ ├── README.md │ ├── native-colors.scss │ ├── kbd.scss │ └── typography-base.scss ├── navigation │ ├── index.scss │ ├── README.md │ ├── filter-list.scss │ └── sidenav.scss ├── forms │ ├── index.scss │ ├── README.md │ ├── form-select.scss │ ├── input-group.scss │ └── radio-group.scss ├── layout │ ├── index.scss │ ├── container.scss │ ├── README.md │ └── grid-offset.scss ├── markdown │ ├── index.scss │ ├── README.md │ ├── blob-csv.scss │ ├── tables.scss │ ├── footnotes.scss │ ├── code.scss │ ├── lists.scss │ ├── headings.scss │ └── markdown-body.scss ├── support │ ├── index.scss │ ├── variables │ │ ├── misc.scss │ │ └── typography.scss │ ├── README.md │ └── mixins │ │ ├── layout.scss │ │ └── misc.scss ├── docs.scss ├── primitives │ ├── index.scss │ └── temp-typography-tokens.scss ├── utilities │ ├── index.scss │ ├── README.md │ ├── box-shadow.scss │ ├── padding.scss │ ├── margin.scss │ ├── details.scss │ ├── flexbox.scss │ └── visibility-display.scss ├── index.scss ├── product │ ├── index.scss │ └── README.md └── core │ ├── README.md │ └── index.scss ├── prettier.config.cjs ├── .vercelignore ├── script ├── .eslintrc.json ├── pretest ├── prepublish ├── build-docs ├── primer-css-compiler.js ├── selector-diff-report ├── build-css.js ├── check-for-changeset ├── stylelint-add-disables.js ├── stylelint-remove-disables.js └── analyze-variables.js ├── .npmrc ├── docs ├── stories │ ├── static │ │ ├── typography-image.png │ │ ├── objects-image.svg │ │ ├── utilities-image.svg │ │ ├── spacing-image.svg │ │ ├── color-image.svg │ │ ├── components-image.svg │ │ └── components-image copy.svg │ ├── utilities │ │ ├── MarketingFilters.stories.jsx │ │ ├── MarketingFilters.mdx │ │ ├── MarketingLayout.stories.jsx │ │ ├── MarketingLayout.mdx │ │ ├── MarketingTypography.mdx │ │ ├── Details.stories.jsx │ │ ├── Padding.stories.jsx │ │ ├── Border.stories.jsx │ │ └── Shadow.stories.jsx │ ├── deprecated-components │ │ ├── BranchName │ │ │ ├── BranchName.mdx │ │ │ └── BranchName.stories.tsx │ │ ├── FilterList │ │ │ ├── FilterList.mdx │ │ │ └── FilterList.stories.tsx │ │ ├── Loaders │ │ │ ├── Loaders.mdx │ │ │ └── Loaders.stories.tsx │ │ ├── IssueLabel │ │ │ ├── IssueLabel.mdx │ │ │ └── IssueLabel.stories.tsx │ │ ├── SubNav │ │ │ └── SubNav.mdx │ │ ├── BoxOverlay │ │ │ └── BoxOverlay.mdx │ │ ├── Marketing │ │ │ └── Marketing.mdx │ │ ├── SideNav │ │ │ └── SideNav.mdx │ │ ├── Pagination │ │ │ ├── Pagination.mdx │ │ │ └── Pagination.stories.tsx │ │ ├── Toast │ │ │ └── Toast.mdx │ │ ├── Header │ │ │ └── Header.mdx │ │ └── Tooltip │ │ │ └── Tooltip.mdx │ ├── support │ │ ├── Prototyping.mdx │ │ └── Deprecations.mdx │ ├── helpers │ │ └── pageLayoutBehavior.jsx │ ├── principles │ │ └── SCSS.mdx │ └── components │ │ └── Layout │ │ └── StackExamples.stories.jsx ├── .storybook │ ├── preview-head.html │ ├── manager.js │ ├── main.js │ ├── preview.css │ ├── storybook.css │ └── theme.js ├── .babelrc ├── postcss.config.js ├── .eslintrc.json ├── script │ └── build-storybook └── package.json ├── .changeset ├── wet-moose-beg.md ├── config.json └── README.md ├── eslint.config.cjs ├── stylelint.config.cjs ├── __tests__ ├── .eslintrc.json ├── docs.test.js ├── css.test.js ├── utils │ ├── docs.js │ └── css.js └── build.test.js ├── .github ├── CODEOWNERS ├── release_template.md ├── workflows │ ├── release_tracking.yml │ ├── stale.yml │ ├── ci.yml │ ├── deploy_production.yml │ ├── axe.yml │ ├── deploy_preview.yml │ └── codeql.yml ├── ISSUE_TEMPLATE │ ├── primer-feature-request.md │ ├── style-guide-bug-report.md │ └── primer-bug-report.md ├── dependabot.yml └── pull_request_template.md ├── .npmignore ├── .gitignore ├── postcss.config.cjs ├── deprecations.js ├── LICENSE ├── RELEASING.md └── README.md /src/table-object/index.scss: -------------------------------------------------------------------------------- 1 | @import './table-object.scss'; 2 | -------------------------------------------------------------------------------- /prettier.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require('@github/prettier-config') 2 | -------------------------------------------------------------------------------- /.vercelignore: -------------------------------------------------------------------------------- 1 | .*.sw? 2 | .changelog/ 3 | dist/ 4 | docs/dist 5 | docs/public/ 6 | -------------------------------------------------------------------------------- /script/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": 0 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/header/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './header.scss'; 3 | -------------------------------------------------------------------------------- /src/toasts/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './toasts.scss'; 3 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | git-tag-version=false 2 | legacy-peer-deps=true 3 | no-audit=true 4 | no-fund=true 5 | -------------------------------------------------------------------------------- /src/box/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './box-overlay.scss'; 3 | -------------------------------------------------------------------------------- /src/loaders/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './loaders.scss'; 3 | -------------------------------------------------------------------------------- /src/marketing/utilities/filters.scss: -------------------------------------------------------------------------------- 1 | .grayscale { 2 | filter: grayscale(100%); 3 | } 4 | -------------------------------------------------------------------------------- /src/tooltips/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './tooltips.scss'; 3 | -------------------------------------------------------------------------------- /src/color-modes/themes/dark.scss: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/functional/themes/dark'; 2 | -------------------------------------------------------------------------------- /src/color-modes/themes/light.scss: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/functional/themes/light'; 2 | -------------------------------------------------------------------------------- /src/marketing/support/index.scss: -------------------------------------------------------------------------------- 1 | @import '../../support/index.scss'; 2 | @import './variables.scss'; 3 | -------------------------------------------------------------------------------- /src/color-modes/themes/dark_dimmed.scss: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/functional/themes/dark-dimmed'; 2 | -------------------------------------------------------------------------------- /src/autocomplete/index.scss: -------------------------------------------------------------------------------- 1 | // support files 2 | @import '../support/index.scss'; 3 | @import './suggester.scss'; 4 | -------------------------------------------------------------------------------- /src/branch-name/index.scss: -------------------------------------------------------------------------------- 1 | // support files 2 | @import '../support/index.scss'; 3 | @import './branch-name.scss'; 4 | -------------------------------------------------------------------------------- /src/marketing/links/index.scss: -------------------------------------------------------------------------------- 1 | // support files 2 | @import '../support/index.scss'; 3 | @import './link.scss'; 4 | -------------------------------------------------------------------------------- /src/pagination/index.scss: -------------------------------------------------------------------------------- 1 | // support files 2 | @import '../support/index.scss'; 3 | @import './pagination.scss'; 4 | -------------------------------------------------------------------------------- /src/select-menu/index.scss: -------------------------------------------------------------------------------- 1 | // support files 2 | @import '../support/index.scss'; 3 | @import './select-menu.scss'; 4 | -------------------------------------------------------------------------------- /src/color-modes/themes/dark_colorblind.scss: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/functional/themes/dark-colorblind'; 2 | -------------------------------------------------------------------------------- /src/color-modes/themes/dark_tritanopia.scss: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/functional/themes/dark-tritanopia'; 2 | -------------------------------------------------------------------------------- /src/color-modes/themes/light_colorblind.scss: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/functional/themes/light-colorblind'; 2 | -------------------------------------------------------------------------------- /src/color-modes/themes/light_tritanopia.scss: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/functional/themes/light-tritanopia'; 2 | -------------------------------------------------------------------------------- /src/labels/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './mixins.scss'; 3 | @import './issue-labels.scss'; 4 | -------------------------------------------------------------------------------- /src/marketing/buttons/index.scss: -------------------------------------------------------------------------------- 1 | // support files 2 | @import '../support/index.scss'; 3 | @import './button.scss'; 4 | -------------------------------------------------------------------------------- /src/marketing/type/index.scss: -------------------------------------------------------------------------------- 1 | // support files 2 | @import '../support/index.scss'; 3 | @import './typography.scss'; 4 | -------------------------------------------------------------------------------- /docs/stories/static/typography-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/primer/css/HEAD/docs/stories/static/typography-image.png -------------------------------------------------------------------------------- /src/color-modes/themes/dark_high_contrast.scss: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/functional/themes/dark-high-contrast'; 2 | -------------------------------------------------------------------------------- /src/color-modes/themes/light_high_contrast.scss: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/functional/themes/light-high-contrast'; 2 | -------------------------------------------------------------------------------- /.changeset/wet-moose-beg.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@primer/css": minor 3 | --- 4 | 5 | Change `contrast` input background to `bgColor-default` 6 | -------------------------------------------------------------------------------- /docs/.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /eslint.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = [{ 2 | "languageOptions": { 3 | "sourceType": "module", 4 | "ecmaVersion": "latest" 5 | } 6 | }] 7 | -------------------------------------------------------------------------------- /src/buttons/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './button.scss'; 3 | @import './button-group.scss'; 4 | @import './misc.scss'; 5 | -------------------------------------------------------------------------------- /src/avatars/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | 3 | // Avatars 4 | @import './avatar-parent-child.scss'; 5 | @import './circle-badge.scss'; 6 | -------------------------------------------------------------------------------- /stylelint.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@primer/stylelint-config'], 3 | ignoreFiles: ['**/*.js', '**/*.cjs'], 4 | rules: {} 5 | } 6 | -------------------------------------------------------------------------------- /__tests__/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:jest/recommended" 4 | ], 5 | "rules": { 6 | "eslint-comments/no-use": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import {addons} from '@storybook/manager-api' 2 | import theme from './theme' 3 | 4 | addons.setConfig({ 5 | theme: theme, 6 | }) 7 | -------------------------------------------------------------------------------- /src/base/octicons.scss: -------------------------------------------------------------------------------- 1 | .octicon { 2 | display: inline-block; 3 | overflow: visible !important; 4 | vertical-align: text-bottom; 5 | fill: currentColor; 6 | } 7 | -------------------------------------------------------------------------------- /src/navigation/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | // Navigation 3 | @import './filter-list.scss'; 4 | @import './sidenav.scss'; 5 | @import './subnav.scss'; 6 | -------------------------------------------------------------------------------- /script/pretest: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | mkdir -p tmp 5 | cd tmp 6 | echo "{\"package\":\"tmp\",\"version\":\"0.0.0\"}" > package.json 7 | npm i @primer/css@latest --force 8 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @primer/design-reviewers 2 | package.json @primer/design-reviewers @primer/engineer-reviewers 3 | package-lock.json @primer/design-reviewers @primer/engineer-reviewers 4 | -------------------------------------------------------------------------------- /src/marketing/utilities/borders.scss: -------------------------------------------------------------------------------- 1 | // Marketing border utilities 2 | 3 | // XXX If you're looking for responsive border utilities, they've moved to 4 | // ../../utilities/borders.scss 5 | -------------------------------------------------------------------------------- /src/marketing/utilities/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | // utilities 3 | @import './animations.scss'; 4 | @import './borders.scss'; 5 | @import './filters.scss'; 6 | @import './layout.scss'; 7 | -------------------------------------------------------------------------------- /src/forms/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './form-control.scss'; 3 | @import './form-select.scss'; 4 | @import './form-group.scss'; 5 | @import './input-group.scss'; 6 | @import './radio-group.scss'; 7 | -------------------------------------------------------------------------------- /src/base/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './native-colors.scss'; 3 | @import './normalize.scss'; 4 | @import './base.scss'; 5 | @import './kbd.scss'; 6 | @import './typography-base.scss'; 7 | @import './octicons.scss'; 8 | -------------------------------------------------------------------------------- /src/layout/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './app-frame.scss'; 3 | @import './container.scss'; 4 | @import './grid.scss'; 5 | @import './grid-offset.scss'; 6 | @import './page-layout.scss'; 7 | @import './stack.scss'; 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | __tests__/ 2 | .changeset/ 3 | .github/ 4 | docs/ 5 | script/ 6 | # we ignore this because everything in src/ is copied out in script/prepublish 7 | src/ 8 | tmp/ 9 | docs.scss 10 | .* 11 | *.log 12 | vercel.json 13 | *.config.js 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.tgz 3 | .DS_Store 4 | .cache/ 5 | .changelog/ 6 | .next/ 7 | .sass-cache 8 | .storybuild/ 9 | .stylelintcache 10 | _site 11 | dist/ 12 | docs/dist 13 | node_modules/ 14 | public/ 15 | searchIndex.js 16 | tmp 17 | yarn.lock 18 | -------------------------------------------------------------------------------- /docs/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "sourceType": "unambiguous", 3 | "presets": [ 4 | "@babel/preset-react", 5 | [ 6 | "@babel/preset-env", 7 | { 8 | "targets": { 9 | "chrome": 100 10 | } 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /script/prepublish: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # generate the build directory 5 | npm run dist > /dev/null 6 | 7 | files=$(git ls-files src | sed -e 's#^src/##' | sed -e 's#/.*$##' | sort -u) 8 | echo $files > publish-files.log 9 | cd src 10 | cp -rv $files .. 11 | cd - 12 | -------------------------------------------------------------------------------- /docs/stories/utilities/MarketingFilters.stories.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Utilities/Marketing/Filters', 3 | } 4 | 5 | export const Grayscale = ({}) => ( 6 | <> 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /src/markdown/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | @import './markdown-body.scss'; 3 | @import './headings.scss'; 4 | @import './lists.scss'; 5 | @import './tables.scss'; 6 | @import './images.scss'; 7 | @import './code.scss'; 8 | @import './blob-csv.scss'; 9 | @import './footnotes.scss'; 10 | -------------------------------------------------------------------------------- /src/support/index.scss: -------------------------------------------------------------------------------- 1 | // variables 2 | @import './variables/typography.scss'; 3 | @import './variables/layout.scss'; 4 | @import './variables/misc.scss'; 5 | 6 | // mixins 7 | @import './mixins/color-modes.scss'; 8 | @import './mixins/typography.scss'; 9 | @import './mixins/layout.scss'; 10 | @import './mixins/misc.scss'; 11 | -------------------------------------------------------------------------------- /script/build-docs: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Build the base project so we can pull out the JSON data 4 | npm run dist 5 | cp -rf dist docs 6 | 7 | # Now build the docs site using that data 8 | cd docs 9 | 10 | if [ -n "$1" ]; then 11 | CI=true npm run build:storybook preview 12 | else 13 | CI=true npm run build:storybook 14 | fi 15 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@1.5.0/schema.json", 3 | "changelog": ["@changesets/changelog-github", {"repo": "primer/css"}], 4 | "commit": false, 5 | "linked": [], 6 | "access": "public", 7 | "baseBranch": "main", 8 | "updateInternalDependencies": "patch", 9 | "ignore": [] 10 | } 11 | -------------------------------------------------------------------------------- /.github/release_template.md: -------------------------------------------------------------------------------- 1 | Preparing for a release. 2 | 3 | ### Checklist 4 | 5 | Make sure these items are checked before merging. 6 | 7 | - [ ] Preview the docs change. 8 | - [ ] Preview npm release candidate. 9 | - [ ] CI passes on the release PR. 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /script/primer-css-compiler.js: -------------------------------------------------------------------------------- 1 | import postcss from 'postcss' 2 | import postCssConfig from '../postcss.config.cjs' 3 | 4 | export default async function compiler(css, options) { 5 | const { plugins, ...config } = postCssConfig 6 | 7 | const result = await postcss(plugins).process(css, { 8 | ...config, 9 | ...options 10 | }) 11 | return result 12 | } 13 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/BranchName/BranchName.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as BranchNameStories from './BranchName.stories' 4 | 5 | 6 | 7 | # BranchName 8 | 9 | Branch names can be a link, span, and include an octicon before the branch name. 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/docs.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Primer CSS 3 | * https://primer.style 4 | * 5 | * Released under MIT license. 6 | */ 7 | 8 | // Docs styles 9 | // Only meant for the docs at https://primer.style/css 10 | 11 | // CSS color variables 12 | @import './color-modes/index.scss'; 13 | 14 | // Global requirements 15 | @import './core/index.scss'; 16 | @import './product/index.scss'; 17 | @import './marketing/index.scss'; 18 | -------------------------------------------------------------------------------- /src/marketing/index.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * @primer/css/marketing 3 | * http://primer.style/css 4 | * 5 | * Released under MIT license. Copyright (c) 2019 GitHub Inc. 6 | */ 7 | 8 | // Global requirements 9 | @import './support/index.scss'; 10 | 11 | // marketing specific css modules 12 | @import './type/index.scss'; 13 | @import './buttons/index.scss'; 14 | @import './links/index.scss'; 15 | @import './utilities/index.scss'; 16 | -------------------------------------------------------------------------------- /src/color-modes/index.scss: -------------------------------------------------------------------------------- 1 | // All themes 2 | 3 | @import './themes/light.scss'; 4 | @import './themes/light_colorblind.scss'; 5 | @import './themes/light_high_contrast.scss'; 6 | @import './themes/light_tritanopia.scss'; 7 | @import './themes/dark.scss'; 8 | @import './themes/dark_dimmed.scss'; 9 | @import './themes/dark_high_contrast.scss'; 10 | @import './themes/dark_colorblind.scss'; 11 | @import './themes/dark_tritanopia.scss'; 12 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/FilterList/FilterList.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as FilterListStories from './FilterList.stories' 4 | 5 | 6 | 7 | # FilterList 8 | 9 | A vertical list of filters. Grey text on white background. Selecting a filter from the list will fill its background with blue and make the text white. 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/loaders/loaders.scss: -------------------------------------------------------------------------------- 1 | // Loaders 2 | 3 | // Animated Ellipsis 4 | 5 | .AnimatedEllipsis { 6 | display: inline-block; 7 | overflow: hidden; 8 | vertical-align: bottom; 9 | 10 | &::after { 11 | display: inline-block; 12 | content: '...'; 13 | animation: AnimatedEllipsis-keyframes 1.2s steps(4, jump-none) infinite; 14 | } 15 | 16 | @keyframes AnimatedEllipsis-keyframes { 17 | 0% { transform: translateX(-100%); } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/postcss.config.js: -------------------------------------------------------------------------------- 1 | // This config file is necessary to get postcss-loader to work in 2 | // ./src/@primer/gatsby-theme-doctocat/components/live-preview-wrapper.js. 3 | const path = require('path') 4 | 5 | module.exports = { 6 | parser: 'postcss-scss', 7 | plugins: [ 8 | require('postcss-node-sass')({ 9 | includePaths: [path.join(__dirname, 'node_modules')], 10 | outputStyle: 'compressed' 11 | }), 12 | require('autoprefixer') 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/Loaders/Loaders.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as LoaderStories from './Loaders.stories' 4 | 5 | 6 | 7 | # Loaders 8 | 9 | Loaders inform users that an action is still in progress and might take a while to complete. 10 | 11 | Add an animated ellipsis at the end of text with ``. 12 | 13 | 14 | -------------------------------------------------------------------------------- /.github/workflows/release_tracking.yml: -------------------------------------------------------------------------------- 1 | name: Release Event Tracking 2 | # Measure a datadog event every time a release occurs 3 | 4 | on: 5 | pull_request: 6 | types: 7 | - closed 8 | - opened 9 | - reopened 10 | 11 | release: 12 | types: [published] 13 | 14 | jobs: 15 | release-tracking: 16 | name: Release Tracking 17 | uses: primer/.github/.github/workflows/release_tracking.yml@v2.4.0 18 | secrets: 19 | datadog_api_key: ${{ secrets.DATADOG_API_KEY }} 20 | -------------------------------------------------------------------------------- /docs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:react/recommended", 4 | "plugin:jsx-a11y/recommended", 5 | "plugin:storybook/recommended" 6 | ], 7 | "rules": { 8 | "import/no-namespace": 0, 9 | "no-unused-vars": [ 10 | "error", 11 | { 12 | "ignoreRestSiblings": true 13 | } 14 | ] 15 | }, 16 | "settings": { 17 | "react": { 18 | "version": "detect" 19 | } 20 | }, 21 | "globals": { 22 | "__DEV__": "readonly" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/script/build-storybook: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | # Add base url to be able to serve static files 4 | if [ -n "$1" ]; then 5 | echo '' >> .storybook/manager-head.html 6 | else 7 | echo '' >> .storybook/manager-head.html 8 | fi 9 | 10 | # Build storybook inside docs 11 | npx storybook build -o public/storybook public/static 12 | 13 | # Remove manager-head after build to not interfere with dev builds 14 | rm .storybook/manager-head.html 15 | -------------------------------------------------------------------------------- /src/primitives/index.scss: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/base/size/size'; 2 | @import '@primer/primitives/dist/css/base/typography/typography'; 3 | @import '@primer/primitives/dist/css/functional/size/border'; 4 | @import '@primer/primitives/dist/css/functional/size/breakpoints'; 5 | @import '@primer/primitives/dist/css/functional/size/size'; 6 | @import '@primer/primitives/dist/css/functional/size/viewport'; 7 | @import '@primer/primitives/dist/css/functional/typography/typography'; 8 | @import './temp-typography-tokens.scss'; 9 | -------------------------------------------------------------------------------- /docs/stories/utilities/MarketingFilters.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta} from '@storybook/blocks' 2 | 3 | import * as MarketingFiltersStories from './MarketingFilters.stories' 4 | 5 | 6 | 7 | # Marketing filters 8 | 9 | Filter utility classes can be applied to divs or images to apply visual effects. 10 | 11 | ## Grayscale 12 | 13 | Applying `.grayscale` to an element will remove all of its colors, and make it render in black and white. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/IssueLabel/IssueLabel.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as IssueLabelStories from './IssueLabel.stories' 4 | 5 | 6 | 7 | # IssueLabel 8 | 9 | Issue labels are used for adding labels to issues and pull requests. They also come with emoji support. 10 | 11 | 12 | 13 | If an issue label needs to be bigger, add the `.IssueLabel--big` modifier. 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/utilities/index.scss: -------------------------------------------------------------------------------- 1 | @import '../support/index.scss'; 2 | // utilities 3 | @import './animations.scss'; 4 | @import './borders.scss'; 5 | @import './box-shadow.scss'; 6 | @import './colors.scss'; 7 | @import './details.scss'; 8 | @import './flexbox.scss'; 9 | @import './layout.scss'; 10 | @import './margin.scss'; 11 | @import './padding.scss'; 12 | @import './typography.scss'; 13 | // Visibility and display should always come last in the imports so that they override other utilities with !important 14 | @import './visibility-display.scss'; 15 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /script/selector-diff-report: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | function log() { 5 | echo "$@" 1>&2 6 | } 7 | 8 | function warn() { 9 | echo "$@" 1>&2 10 | } 11 | 12 | pkg="@primer/css" 13 | path="dist/stats/primer.json" 14 | cp "tmp/node_modules/@primer/css/dist/stats/primer.json" before.json 15 | 16 | cp $path after.json 17 | 18 | key=".selectors.values[]" 19 | jq -r $key before.json | sort | uniq > before.txt 20 | jq -r $key after.json | sort | uniq > after.txt 21 | 22 | diff -U 1 {before,after}.txt 23 | 24 | rm {before,after}.{json,txt} 25 | -------------------------------------------------------------------------------- /src/index.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Primer CSS 3 | * https://primer.style 4 | * 5 | * Released under MIT license. 6 | */ 7 | 8 | // Primer main file 9 | // 10 | // Imports all Primer files in their intended order for easy mass-inclusion. 11 | // Should you need specific files, you can easily use separate `@import`s. 12 | 13 | // CSS color variables 14 | @import './color-modes/index.scss'; 15 | @import './primitives/index.scss'; 16 | 17 | // Global requirements 18 | @import './core/index.scss'; 19 | @import './product/index.scss'; 20 | @import './marketing/index.scss'; 21 | -------------------------------------------------------------------------------- /src/avatars/avatar-parent-child.scss: -------------------------------------------------------------------------------- 1 | // .avatar-parent-child is when you see a small avatar at the bottom right 2 | // corner of a larger avatar. 3 | // 4 | // No Styleguide version 5 | .avatar-parent-child { 6 | position: relative; 7 | } 8 | 9 | .avatar-child { 10 | position: absolute; 11 | right: -15%; 12 | bottom: -9%; 13 | background-color: var(--bgColor-default, var(--color-canvas-default)); // For transparent backgrounds 14 | // stylelint-disable-next-line primer/borders 15 | border-radius: $border-radius-1; 16 | box-shadow: var(--avatar-shadow, var(--color-avatar-child-shadow)); 17 | } 18 | -------------------------------------------------------------------------------- /src/product/index.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * @primer/css/product 3 | * http://primer.style/css 4 | * 5 | * Released under MIT license. Copyright (c) 2019 GitHub Inc. 6 | */ 7 | 8 | // Global requirements 9 | @import '../support/index.scss'; 10 | 11 | // Product specific css modules 12 | @import '../autocomplete/index.scss'; 13 | @import '../avatars/index.scss'; 14 | @import '../branch-name/index.scss'; 15 | @import '../header/index.scss'; 16 | @import '../labels/index.scss'; 17 | @import '../loaders/index.scss'; 18 | @import '../markdown/index.scss'; 19 | @import '../select-menu/index.scss'; 20 | @import '../toasts/index.scss'; 21 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const autoprefixer = require('autoprefixer') 2 | const sass = require('@csstools/postcss-sass') 3 | const scss = require('postcss-scss') 4 | const scssImport = require('postcss-import') 5 | const { join } = require('path') 6 | 7 | module.exports = { 8 | map: { 9 | sourcesContent: false, 10 | annotation: true 11 | }, 12 | customSyntax: scss, 13 | parser: scss, 14 | plugins: [ 15 | scssImport, 16 | sass({ 17 | includePaths: [join(__dirname, 'node_modules')], 18 | outputStyle: process.env.CSS_MINIFY === '0' ? 'expanded' : 'compressed' 19 | }), 20 | autoprefixer 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src/labels/issue-labels.scss: -------------------------------------------------------------------------------- 1 | // Issue Labels 2 | 3 | // TODO: Replace with .Label once solid backgrounds are supported 4 | 5 | .IssueLabel { 6 | @include labels-base; 7 | 8 | .g-emoji { 9 | position: relative; 10 | // stylelint-disable-next-line primer/spacing 11 | top: -0.05em; 12 | display: inline-block; 13 | // stylelint-disable-next-line primer/typography 14 | font-size: 1em; 15 | // stylelint-disable-next-line primer/typography 16 | line-height: $lh-condensed-ultra; 17 | } 18 | 19 | &:hover { 20 | text-decoration: none; 21 | } 22 | } 23 | 24 | .IssueLabel--big { 25 | @include labels-large; 26 | } 27 | -------------------------------------------------------------------------------- /src/primitives/temp-typography-tokens.scss: -------------------------------------------------------------------------------- 1 | // Temporary typography vars in rem units variables 2 | :root { 3 | // Heading sizes - mobile 4 | // h4-h6 remain the same size on both mobile & desktop 5 | --h00-size-mobile: 2.5rem; 6 | --h0-size-mobile: 2rem; 7 | --h1-size-mobile: 1.625rem; 8 | --h2-size-mobile: 1.375rem; 9 | --h3-size-mobile: 1.125rem; 10 | 11 | // Heading sizes - desktop 12 | --h00-size: 3rem; 13 | --h0-size: 2.5rem; 14 | --h1-size: 2rem; 15 | --h2-size: 1.5rem; 16 | --h3-size: 1.25rem; 17 | --h4-size: 1rem; 18 | --h5-size: 0.875rem; 19 | --h6-size: 0.75rem; 20 | --body-font-size: 0.875rem; 21 | --font-size-small: 0.75rem; 22 | } 23 | -------------------------------------------------------------------------------- /src/table-object/table-object.scss: -------------------------------------------------------------------------------- 1 | // Deprecated 2 | // TODO: Replace TableObject with flexbox or a new Table component 3 | 4 | // TableObject is a module for creating dynamically resizable elements that 5 | // always sit on the same horizontal line (e.g., they never wrap). Using 6 | // tables means it's cross browser friendly. 7 | 8 | .TableObject { 9 | display: table; 10 | } 11 | 12 | // Place this on every "cell" 13 | .TableObject-item { 14 | display: table-cell; 15 | width: 1%; 16 | white-space: nowrap; 17 | vertical-align: middle; 18 | } 19 | 20 | // Place this on the largest or most important "cell" 21 | .TableObject-item--primary { 22 | width: 99%; 23 | } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/primer-feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Primer CSS feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /src/support/variables/misc.scss: -------------------------------------------------------------------------------- 1 | // Miscellaneous variables 2 | 3 | // Border 4 | $border-width: 1px !default; 5 | $border-style: solid !default; 6 | $border: $border-width $border-style var(--borderColor-default, var(--color-border-default)) !default; 7 | $border-rem: var(--borderWidth-thin, 1px) solid var(--borderColor-default, var(--color-border-default)) !default; 8 | 9 | // Border Radius 10 | $border-radius-1: 4px !default; 11 | $border-radius-2: 6px !default; 12 | $border-radius-3: 8px !default; 13 | $border-radius: $border-radius-2 !default; 14 | 15 | // Tooltips 16 | $tooltip-max-width: 250px !default; 17 | $tooltip-delay: 0.4s !default; 18 | $tooltip-duration: 0.1s !default; 19 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/SubNav/SubNav.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as SubNavStories from './SubNav.stories' 4 | 5 | 6 | 7 | # SubNav 8 | 9 | `.subnav` is navigation that is typically used when on a dashboard type interface with another set of navigation above it. This helps distinguish navigation hierarchy. 10 | 11 | 12 | 13 | You can have `subnav-search` in the subnav bar. 14 | 15 | 16 | 17 | You can also use a `subnav-search-context` to display search help in a select menu. 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/layout/container.scss: -------------------------------------------------------------------------------- 1 | // Fixed-width, centered column for site content. 2 | // Handy container styles that match our breakpoints 3 | 4 | // 544px 5 | .container-sm { 6 | max-width: $width-sm; 7 | margin-right: auto; 8 | margin-left: auto; 9 | } 10 | 11 | // 768px 12 | .container-md { 13 | max-width: $container-md; 14 | margin-right: auto; 15 | margin-left: auto; 16 | } 17 | 18 | // 1004px - this matches the current fixed width: 980px + padding: px-3 19 | .container-lg { 20 | max-width: $container-lg; 21 | margin-right: auto; 22 | margin-left: auto; 23 | } 24 | 25 | // 1280px 26 | .container-xl { 27 | max-width: $container-xl; 28 | margin-right: auto; 29 | margin-left: auto; 30 | } 31 | -------------------------------------------------------------------------------- /src/box/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "box" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `box` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/box/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/box.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/base/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "base" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `base` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/base/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/base.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/core/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "core" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `core` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/core/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/core.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/forms/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "forms" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `forms` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/forms/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/forms.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/core/index.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * @primer/css/core 3 | * http://primer.style/css 4 | * 5 | * Released under MIT license. Copyright (c) 2019 GitHub Inc. 6 | */ 7 | 8 | // Global requirements 9 | @import '../support/index.scss'; 10 | 11 | // Color modes 12 | 13 | // Core modules 14 | @import '../base/index.scss'; 15 | @import '../box/index.scss'; 16 | @import '../buttons/index.scss'; 17 | @import '../table-object/index.scss'; 18 | @import '../forms/index.scss'; 19 | @import '../layout/index.scss'; 20 | @import '../navigation/index.scss'; 21 | @import '../pagination/index.scss'; 22 | @import '../tooltips/index.scss'; 23 | 24 | // Utilities always go last so that they can override components 25 | @import '../utilities/index.scss'; 26 | -------------------------------------------------------------------------------- /src/header/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "header" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `header` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/header/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/header.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/labels/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "labels" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `labels` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/labels/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/labels.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/layout/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "layout" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `layout` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/layout/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/layout.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/toasts/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "toasts" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `toasts` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/toasts/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/toasts.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/avatars/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "avatars" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `avatars` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/avatars/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/avatars.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/buttons/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "buttons" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `buttons` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/buttons/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/buttons.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/loaders/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "loaders" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `loaders` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/loaders/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/loaders.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/product/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "product" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `product` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/product/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/product.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/support/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "support" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `support` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/support/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/support.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/markdown/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "markdown" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `markdown` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/markdown/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/markdown.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/tooltips/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "tooltips" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `tooltips` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/tooltips/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/tooltips.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/marketing/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "marketing" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `marketing` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/marketing/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/marketing.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/utilities/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "utilities" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `utilities` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/utilities/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/utilities.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/navigation/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "navigation" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `navigation` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/navigation/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/navigation.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/pagination/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "pagination" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `pagination` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/pagination/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/pagination.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/branch-name/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "branch-name" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `branch-name` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/branch-name/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/branch-name.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/select-menu/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "select-menu" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `select-menu` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/select-menu/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/select-menu.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /src/autocomplete/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | bundle: "autocomplete" 3 | generated: true 4 | --- 5 | 6 | # Primer CSS: `autocomplete` bundle 7 | 8 | ## Usage 9 | 10 | Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with: 11 | 12 | ```scss 13 | @import "@primer/css/autocomplete/index.scss"; 14 | ``` 15 | 16 | ## Build 17 | 18 | The `@primer/css` npm package includes a standalone CSS build of this module in `dist/autocomplete.css`. 19 | 20 | ## License 21 | 22 | [MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/) 23 | 24 | 25 | [scss]: https://sass-lang.com/documentation/syntax#scss 26 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/BoxOverlay/BoxOverlay.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as BoxOverlayStories from './BoxOverlay.stories' 4 | 5 | 6 | 7 | # BoxOverlay 8 | 9 | Use the `Box--overlay` with the `
` and [``](https://github.com/github/details-dialog), and add the `details-overlay-dark` utility if you wish to apply a dark transparent background. 10 | 11 | Box overlays come in three widths. The default `Box--overlay` is 440px wide, `Box-overlay--narrow` is 320px wide, and `Box-overlay--wide` is 640px wide. 12 | 13 | See [aria attributes](https://www.w3.org/TR/html-aria/#allowed-aria-roles-states-and-properties) 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/stories/static/objects-image.svg: -------------------------------------------------------------------------------- 1 | objects 2 | -------------------------------------------------------------------------------- /src/base/native-colors.scss: -------------------------------------------------------------------------------- 1 | // color-scheme 2 | // Enables color modes for native elements 3 | 4 | @include color-mode(light) { color-scheme: light; } 5 | 6 | @include color-mode(dark) { color-scheme: dark; } 7 | 8 | [data-color-mode] { 9 | color: var(--fgColor-default, var(--color-fg-default)); 10 | background-color: var(--bgColor-default, var(--color-canvas-default)); 11 | } 12 | 13 | // Windows High Contrast mode 14 | 15 | // Improves focus state for various components when Windows High Contrast mode is enabled 16 | // stylelint-disable selector-max-type 17 | @media (forced-colors: active) { 18 | body { 19 | --color-accent-emphasis: Highlight; 20 | --color-fg-on-emphasis: LinkText; 21 | --fgColor-onEmphasis: LinkText; 22 | --fgColor-accent: Highlight; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/utilities/box-shadow.scss: -------------------------------------------------------------------------------- 1 | // Box shadow utilities 2 | 3 | // Box shadows 4 | 5 | .color-shadow-small { 6 | box-shadow: var(--shadow-resting-small, var(--color-shadow-small)) !important; 7 | } 8 | 9 | .color-shadow-medium { 10 | box-shadow: var(--shadow-resting-medium, var(--color-shadow-medium)) !important; 11 | } 12 | 13 | .color-shadow-large { 14 | box-shadow: var(--shadow-floating-large, var(--color-shadow-large)) !important; 15 | } 16 | 17 | .color-shadow-extra-large { 18 | box-shadow: var(--shadow-floating-xlarge, var(--color-shadow-extra-large)) !important; 19 | } 20 | 21 | .shadow-floating-small { 22 | box-shadow: var(--shadow-floating-small, var(--color-overlay-shadow)) !important; 23 | } 24 | 25 | // Turn off box shadow 26 | 27 | .box-shadow-none { 28 | box-shadow: none !important; 29 | } 30 | -------------------------------------------------------------------------------- /docs/stories/utilities/MarketingLayout.stories.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Utilities/Marketing/Layout', 3 | } 4 | 5 | export const Positions = ({}) => ( 6 | <> 7 |
8 |
.top-2
9 |
.right-md-4
10 |
.left-lg-1
11 |
12 | 13 | ) 14 | 15 | export const Offset = ({}) => ( 16 | <> 17 |
18 |
.offset-n1
19 |
.offset-n2
20 |
21 | 22 | ) 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | # Maintain dependencies for GitHub Actions 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: "daily" 13 | groups: 14 | all: 15 | patterns: 16 | - "*" 17 | 18 | # Maintain dependencies for npm 19 | - package-ecosystem: "npm" 20 | directory: "/" 21 | schedule: 22 | interval: "daily" 23 | groups: 24 | all: 25 | patterns: 26 | - "*" 27 | -------------------------------------------------------------------------------- /docs/.storybook/main.js: -------------------------------------------------------------------------------- 1 | /** @type { import('@storybook/react-webpack5').StorybookConfig } */ 2 | const config = { 3 | stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'], 4 | addons: [ 5 | '@storybook/addon-links', 6 | '@storybook/addon-essentials', 7 | '@storybook/addon-interactions', 8 | 'storybook-addon-pseudo-states', 9 | '@storybook/addon-storysource', 10 | '@geometricpanda/storybook-addon-badges', 11 | { 12 | name: '@storybook/addon-styling', 13 | options: { 14 | sass: { 15 | implementation: require('sass'), 16 | }, 17 | }, 18 | }, 19 | ], 20 | framework: { 21 | name: '@storybook/react-webpack5', 22 | options: {}, 23 | }, 24 | docs: { 25 | autodocs: 'tag', 26 | }, 27 | staticDirs: ['../stories/static'], 28 | } 29 | export default config 30 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### What are you trying to accomplish? 2 | 3 | 4 | 5 | ### What approach did you choose and why? 6 | 7 | 8 | 9 | ### What should reviewers focus on? 10 | 11 | 12 | 13 | ### Can these changes ship as is? 14 | 15 | 16 | 17 | - [ ] Yes, this PR does not depend on additional changes. 🚢 18 | -------------------------------------------------------------------------------- /script/build-css.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import {globby} from 'globby' 3 | import compiler from './primer-css-compiler.js' 4 | import {dirname, join} from 'path' 5 | 6 | import fsExtra from 'fs-extra' 7 | const {mkdirp, readFile, writeFile} = fsExtra 8 | 9 | 10 | const inDir = 'src' 11 | const outDir = 'dist' 12 | const bundleNames = { 13 | 'index.scss': 'primer' 14 | } 15 | 16 | const files = await globby([`${inDir}/**/index.scss`]) 17 | await mkdirp(outDir) 18 | const inPattern = new RegExp(`^${inDir}/`) 19 | const tasks = files.map(async from => { 20 | const path = from.replace(inPattern, '') 21 | const name = bundleNames[path] || dirname(path).replace(/\//g, '-') 22 | 23 | const to = join(outDir, `${name}.css`) 24 | 25 | const result = await compiler(await readFile(from, 'utf8'), {from, to}) 26 | 27 | await Promise.all([ 28 | writeFile(to, result.css, 'utf8'), 29 | ]) 30 | }) 31 | 32 | await Promise.all(tasks) 33 | -------------------------------------------------------------------------------- /__tests__/docs.test.js: -------------------------------------------------------------------------------- 1 | import {getNavigationLinks, getContentFrontmatter} from './utils/docs' 2 | 3 | let navLinks, contentFM 4 | 5 | beforeAll(async () => { 6 | contentFM = await getContentFrontmatter() 7 | navLinks = getNavigationLinks() 8 | }) 9 | 10 | describe('frontmatter', () => { 11 | it('page title matches link title', () => { 12 | for (const link of navLinks) { 13 | const content = contentFM[link['url']] 14 | expect(content).not.toBeNull() 15 | expect(content['title']).toBe(link['title']) 16 | } 17 | }) 18 | 19 | it('contains path attribute', () => { 20 | for (const v of Object.values(contentFM)) { 21 | expect(v['path']).not.toBeNull() 22 | } 23 | }) 24 | }) 25 | 26 | describe('navigation', () => { 27 | it('has a file for each nav item', () => { 28 | for (const link of navLinks) { 29 | const content = contentFM[link['url']] 30 | expect(content).not.toBeNull() 31 | } 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /deprecations.js: -------------------------------------------------------------------------------- 1 | /** 2 | Deprecated Selectors 3 | ------------------------- 4 | These are deprecated selectors and should not be used. They include a replacement value, 5 | which can be an array or null. 6 | 7 | * 'deprecated-selector': 'replacement-selector' <-- Replace with this selector. 8 | * 'deprecated-selector': ['replacement-1', 'replacement-2'] <-- Replace with one of these selectors. 9 | * 'deprecated-selector': null <-- No option available, remove selector. 10 | */ 11 | 12 | import fs from 'fs' 13 | 14 | const deprecations = JSON.parse(fs.readFileSync('./dist/deprecations.json')) 15 | 16 | const deprecatedSelectors = deprecations['selectors'] 17 | const deprecatedSassVariables = deprecations['variables'] 18 | const deprecatedSassMixins = deprecations['mixins'] 19 | 20 | export { 21 | deprecatedSelectors, 22 | deprecatedSassVariables, 23 | deprecatedSassMixins 24 | } 25 | -------------------------------------------------------------------------------- /src/markdown/blob-csv.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable selector-max-type 2 | .markdown-body .csv-data { 3 | td, 4 | th { 5 | // stylelint-disable-next-line primer/spacing 6 | padding: 5px; 7 | overflow: hidden; 8 | // stylelint-disable-next-line primer/typography 9 | font-size: $font-size-small; 10 | // stylelint-disable-next-line primer/typography 11 | line-height: $lh-condensed-ultra; 12 | text-align: left; 13 | white-space: nowrap; 14 | } 15 | 16 | .blob-num { 17 | // stylelint-disable-next-line primer/spacing 18 | padding: 10px var(--base-size-8) 9px; 19 | text-align: right; 20 | background: var(--bgColor-default, var(--color-canvas-default)); 21 | border: 0; 22 | } 23 | 24 | tr { border-top: 0; } 25 | 26 | th { 27 | // stylelint-disable-next-line primer/typography 28 | font-weight: $font-weight-bold; 29 | background: var(--bgColor-muted, var(--color-canvas-subtle)); 30 | border-top: 0; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/Marketing/Marketing.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as MarketingStories from './Marketing.stories' 4 | 5 | 6 | 7 | # Marketing links 8 | 9 | Marketing links can be produced by combining the base class `link-mktg` with a set of modifier classes to control the size and color. 10 | 11 | 12 | 13 | ## Link sizes 14 | 15 | The marketing link size is defined with utility classes and come in large (`.f3-mktg`) and small (`.f4-mktg`): 16 | 17 | ## Link with emphasis 18 | 19 | Add class `link-emphasis-mktg` to the link, to give it a pale underline, that will fill up on hover. 20 | 21 | 22 | 23 | ## Link colors 24 | 25 | The link color is controlled with the [Primer color classes](/utilities/colors), while the symbol and underline styling will follow: 26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/FilterList/FilterList.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {BADGE} from '@geometricpanda/storybook-addon-badges' 3 | 4 | export default { 5 | title: 'Deprecated/FilterList', 6 | parameters: { 7 | storyType: 'banner', 8 | controls: {hideNoControlsWarning: true}, 9 | badges: [BADGE.DEPRECATED], 10 | }, 11 | } 12 | 13 | export const Default = () => { 14 | return ( 15 | 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/style-guide-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Style guide bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/primer-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Primer CSS bug report 3 | about: Create a report to help us improve Primer CSS 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /src/base/kbd.scss: -------------------------------------------------------------------------------- 1 | // Keyboard shortcuts 2 | // stylelint-disable selector-max-type 3 | 4 | kbd { 5 | display: inline-block; 6 | padding: var(--base-size-4); 7 | // stylelint-disable-next-line primer/typography, declaration-property-value-no-unknown 8 | font: 11px $mono-font; 9 | // stylelint-disable-next-line primer/typography 10 | line-height: 10px; 11 | color: var(--fgColor-default, var(--color-fg-default)); 12 | vertical-align: middle; 13 | background-color: var(--bgColor-muted, var(--color-canvas-subtle)); 14 | // stylelint-disable-next-line primer/borders, primer/colors 15 | border: $border-style $border-width var(--borderColor-neutral-muted, var(--color-neutral-muted)); 16 | border-bottom-color: var(--borderColor-neutral-muted, var(--color-neutral-muted)); 17 | // stylelint-disable-next-line primer/borders 18 | border-radius: $border-radius; 19 | // stylelint-disable-next-line primer/box-shadow 20 | box-shadow: inset 0 -1px 0 var(--borderColor-neutral-muted, var(--color-neutral-muted)); 21 | } 22 | -------------------------------------------------------------------------------- /__tests__/css.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | getCurrentVersion, 3 | getPackageStats, 4 | getSelectorDiff, 5 | getVariableDiff, 6 | currentVersionDeprecations 7 | } from './utils/css' 8 | import semver from 'semver' 9 | 10 | let selectorsDiff, variablesDiff, version 11 | 12 | beforeAll(async () => { 13 | selectorsDiff = getSelectorDiff() 14 | variablesDiff = getVariableDiff() 15 | version = getCurrentVersion() 16 | }) 17 | 18 | describe('css', () => { 19 | it('The support.css file contains no compiled css', () => { 20 | const supportStats = getPackageStats('support') 21 | expect(supportStats.size).toEqual(0) 22 | }) 23 | }) 24 | 25 | describe('deprecations', () => { 26 | it('expects deprecations and their replacement to not be equal.', () => { 27 | const deprecations = currentVersionDeprecations() 28 | Object.keys(deprecations["selectors"]).forEach(deprecation => { 29 | const replacement = deprecations["selectors"][deprecation] 30 | expect(deprecation).not.toEqual(replacement) 31 | }) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/Loaders/Loaders.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {BADGE} from '@geometricpanda/storybook-addon-badges' 3 | 4 | export default { 5 | title: 'Deprecated/Loaders', 6 | parameters: { 7 | storyType: 'banner', 8 | controls: {hideNoControlsWarning: true}, 9 | badges: [BADGE.DEPRECATED], 10 | }, 11 | } 12 | 13 | export const Default = () => { 14 | return ( 15 | <> 16 |

17 | Loading 18 | 19 |

20 | 21 | Loading 22 | 23 | 24 | 25 | Loading 26 | 27 | 28 | 32 | 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /docs/stories/static/utilities-image.svg: -------------------------------------------------------------------------------- 1 | utilities 2 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "docs", 4 | "repository": "primer/css", 5 | "scripts": { 6 | "storybook": "NODE_ENV=test storybook dev -p 6006", 7 | "build:storybook": "script/build-storybook" 8 | }, 9 | "engines": { 10 | "node": ">= 16.x" 11 | }, 12 | "dependencies": { 13 | "@babel/core": "^7.16.7", 14 | "@geometricpanda/storybook-addon-badges": "^2.0.0", 15 | "@storybook/addon-essentials": "^7.0.26", 16 | "@storybook/addon-interactions": "^7.0.26", 17 | "@storybook/addon-links": "^7.0.26", 18 | "@storybook/addon-storysource": "^7.6.20", 19 | "@storybook/addon-styling": "^1.3.2", 20 | "@storybook/blocks": "^7.0.26", 21 | "@storybook/react": "^7.0.26", 22 | "@storybook/react-webpack5": "^7.0.26", 23 | "@storybook/testing-library": "^0.0.14-next.2", 24 | "babel-loader": "^8.2.5", 25 | "clsx": "^1.2.1", 26 | "eslint-plugin-storybook": "^0.6.12", 27 | "react": "^18.2.0", 28 | "react-dom": "^18.2.0", 29 | "storybook": "^7.0.26", 30 | "storybook-addon-pseudo-states": "^2.1.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/stories/utilities/MarketingLayout.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta} from '@storybook/blocks' 2 | 3 | import * as MarketingLayoutStories from './MarketingLayout.stories' 4 | 5 | 6 | 7 | # Marketing layout 8 | 9 | ## Position elements with spacing utilities 10 | 11 | Position elements from all four element edges (`top`, `right`, `bottom`, and `left`) using any spacing utility from the global spacing scale and the marketing spacing scale (from `$spacer-1` to `$spacer-12`), including negative and 0 values. 12 | 13 | Use these with `.position-absolute` to position decorative assets and shapes on marketing sites. 14 | 15 | In an effort to reduce the size of our CSS, responsive breakpoints are only supported for `md` and `lg` breakpoints. **There is no support for `sm` and `xl` breakpoints.** 16 | 17 | 18 | 19 | ## Negative offset columns 20 | 21 | Using column offset classes can pull a div over X number of columns to the left. Negative offsets are available in spacings from 1 to 7. 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/stories/utilities/MarketingTypography.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as MarketingTypographyStories from './MarketingTypography.stories' 4 | 5 | 6 | 7 | # Marketing typography 8 | 9 | The typography for our marketing pages differs from Primer CSS's core. It is responsive, on a slightly different scale, and headlines are set in the [defined marketing font](https://github.com/primer/css/blob/main/src/marketing/support/variables.scss). 10 | 11 | ## Heading utilities 12 | 13 | 14 | 15 | Use `.h0-mktg` to `.h6-mktg` to change an element's font, size, and weight on marketing pages. 16 | 17 | ## Body content utilities 18 | 19 | 20 | 21 | Use `.f0-mktg` to `.f6-mktg` to change an element's body font, size, and weight on marketing pages. 22 | 23 | ## Typographic utilities 24 | 25 | These utilities are meant to be used in addition to Primer CSS's core utilities. 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/layout/grid-offset.scss: -------------------------------------------------------------------------------- 1 | // Optional offset options to work with grid.scss 2 | 3 | // Offset Columns 4 | 5 | @each $breakpoint, $variant in $responsive-variants { 6 | @include breakpoint($breakpoint) { 7 | .offset#{$variant}-1 { margin-left: (1 * 0.0833333333 * 100%) !important; } 8 | .offset#{$variant}-2 { margin-left: (2 * 0.0833333333 * 100%) !important; } 9 | .offset#{$variant}-3 { margin-left: (3 * 0.0833333333 * 100%) !important; } 10 | .offset#{$variant}-4 { margin-left: (4 * 0.0833333333 * 100%) !important; } 11 | .offset#{$variant}-5 { margin-left: (5 * 0.0833333333 * 100%) !important; } 12 | .offset#{$variant}-6 { margin-left: (6 * 0.0833333333 * 100%) !important; } 13 | .offset#{$variant}-7 { margin-left: (7 * 0.0833333333 * 100%) !important; } 14 | .offset#{$variant}-8 { margin-left: (8 * 0.0833333333 * 100%) !important; } 15 | .offset#{$variant}-9 { margin-left: (9 * 0.0833333333 * 100%) !important; } 16 | .offset#{$variant}-10 { margin-left: (10 * 0.0833333333 * 100%) !important; } 17 | .offset#{$variant}-11 { margin-left: (11 * 0.0833333333 * 100%) !important; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 GitHub Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/marketing/links/link.scss: -------------------------------------------------------------------------------- 1 | .link-mktg { 2 | position: relative; 3 | display: inline-block; 4 | 5 | &:hover { 6 | text-decoration: none; 7 | } 8 | 9 | &::after, 10 | &.link-emphasis-mktg::before { 11 | position: absolute; 12 | // stylelint-disable-next-line primer/spacing 13 | bottom: -0.15em; 14 | left: 0; 15 | width: calc(100% - 1em); 16 | height: 2px; 17 | pointer-events: none; 18 | content: ''; 19 | background-color: currentColor; 20 | transform: scaleX(0); 21 | transform-origin: 0 0; 22 | 23 | @media screen and (prefers-reduced-motion: no-preference) { 24 | transition: transform 0.3s ease; 25 | } 26 | } 27 | 28 | &.link-emphasis-mktg::before { 29 | opacity: 0.2; 30 | transform: scaleX(1); 31 | } 32 | 33 | &:hover, 34 | &:active { 35 | &::after { 36 | transform: scaleX(1); 37 | } 38 | } 39 | 40 | &:focus, 41 | &:focus-visible { 42 | outline-offset: 2px; 43 | } 44 | 45 | &.arrow-target-mktg { 46 | .arrow-symbol-mktg { 47 | // stylelint-disable-next-line primer/spacing 48 | margin-left: -$em-spacer-3; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/branch-name/branch-name.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable selector-max-type, selector-no-qualifying-type 2 | 3 | // A nice way to display branch names inside the UI. Can be a link or not. 4 | 5 | .branch-name { 6 | display: inline-block; 7 | // stylelint-disable-next-line primer/spacing 8 | padding: 2px 6px; 9 | // stylelint-disable-next-line primer/typography, declaration-property-value-no-unknown 10 | font: 12px $mono-font; 11 | color: var(--fgColor-muted, var(--color-fg-muted)); 12 | word-break: break-all; 13 | background-color: var(--bgColor-accent-muted, var(--color-accent-subtle)); 14 | // stylelint-disable-next-line primer/borders 15 | border-radius: $border-radius; 16 | 17 | .octicon { 18 | // stylelint-disable-next-line primer/spacing 19 | margin: 1px -2px 0 0; 20 | color: var(--fgColor-muted, var(--color-fg-muted)); 21 | } 22 | } 23 | 24 | // When a branch name is a link 25 | 26 | a.branch-name { 27 | color: var(--fgColor-accent, var(--color-accent-fg)); 28 | background-color: var(--bgColor-accent-muted, var(--color-accent-subtle)); 29 | 30 | .octicon { 31 | color: var(--fgColor-accent, var(--color-accent-fg)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Stale 2 | on: 3 | schedule: 4 | - cron: '0 * * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v10 11 | with: 12 | 13 | # General settings 14 | days-before-stale: 60 15 | days-before-close: 7 16 | enable-statistics: true 17 | operations-per-run: 100 18 | remove-stale-when-updated: true 19 | 20 | # PR specific settings 21 | delete-branch: true 22 | stale-pr-message: "Hi! This pull request has been marked as stale because it has been open with no activity for 60 days. You can comment on the pull request or remove the stale label to keep it open. If you do nothing, this pull request will be closed in 7 days." 23 | 24 | # Issue specific settings 25 | days-before-issue-stale: 180 26 | stale-issue-message: "Hi! This issue has been marked as stale because it has been open with no activity for 180 days. You can comment on the issue or remove the stale label to keep it open. If you do nothing, this issue will be closed in 7 days." 27 | 28 | -------------------------------------------------------------------------------- /docs/stories/static/spacing-image.svg: -------------------------------------------------------------------------------- 1 | 2 | Group 3 | Created using Figma 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /script/check-for-changeset: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | DEEPEN_LENGTH=${DEEPEN_LENGTH:-10} 4 | MAX_DEPTH=${MAX_DEPTH:-300} 5 | 6 | depth=0 7 | 8 | # Fetch the base ref, i.e. main 9 | git fetch --no-tags --progress --depth=1 origin "+refs/heads/$GITHUB_BASE_REF:refs/heads/$GITHUB_BASE_REF" 10 | 11 | # Keep fetching more commits until a merge base can be found 12 | while [ -z "$(git merge-base "$GITHUB_BASE_REF" "origin/$GITHUB_HEAD_REF")" ]; do 13 | git fetch --no-tags -q --deepen="$DEEPEN_LENGTH" origin "$GITHUB_BASE_REF" "$GITHUB_HEAD_REF" > /dev/null 14 | depth=$(( depth + $DEEPEN_LENGTH )) 15 | 16 | # Make sure we don't end up in an infinite loop 17 | if [[ "$depth" -ge "$MAX_DEPTH" ]]; then 18 | echo "Could not find merge base, max depth exceeded." 19 | exit 1 20 | fi 21 | done 22 | 23 | # Check for added .md files in the .changeset directory 24 | git diff --name-only origin/${GITHUB_BASE_REF}...origin/${GITHUB_HEAD_REF} | grep '.changeset/.*.md' > /dev/null || ( 25 | exit_code=$? 26 | echo "No changeset found. If these changes should not result in a new version, apply the ${SKIP_LABEL} label to this pull request. If these changes should result in a version bump, please add a changeset https://git.io/J6QvQ" 27 | exit "${exit_code}" 28 | ) 29 | -------------------------------------------------------------------------------- /docs/stories/utilities/Details.stories.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Utilities/Details' 3 | } 4 | 5 | export const Overlay = ({}) => ( 6 |
7 | More 8 |
Hidden text
9 |
10 | ) 11 | 12 | export const OverlayDark = ({}) => ( 13 |
14 | More 15 |
16 | Hidden text 17 |
18 |
19 | ) 20 | 21 | export const Caret = ({}) => ( 22 |
23 | More 24 |
Hidden text
25 |
26 | ) 27 | 28 | export const Summary = ({}) => ( 29 |
30 |
31 | More 32 |
Hidden text
33 |
34 | 35 |
36 | More 37 |
Hidden text
38 |
39 |
40 | ) 41 | -------------------------------------------------------------------------------- /src/avatars/circle-badge.scss: -------------------------------------------------------------------------------- 1 | // Circle badge icon with drop shadow for icons and logos 2 | 3 | .CircleBadge { 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | background-color: var(--bgColor-default, var(--color-canvas-default)); 8 | border-radius: 50%; 9 | box-shadow: var(--shadow-resting-medium, var(--color-shadow-medium)); 10 | } 11 | 12 | .CircleBadge-icon { 13 | max-width: 60% !important; 14 | height: auto !important; 15 | max-height: 55% !important; 16 | } 17 | 18 | // Small badge 19 | .CircleBadge--small { 20 | width: 56px; 21 | height: 56px; 22 | } 23 | 24 | // Medium badge 25 | .CircleBadge--medium { 26 | width: 96px; 27 | height: 96px; 28 | } 29 | 30 | // Large badge 31 | .CircleBadge--large { 32 | width: 128px; 33 | height: 128px; 34 | } 35 | 36 | // Dashed line that connects badges.. 37 | // Wrap around 2 or more badges to create a horizonal line: 38 | 39 | .DashedConnection { 40 | position: relative; 41 | 42 | &::before { 43 | position: absolute; 44 | top: 50%; 45 | left: 0; 46 | width: 100%; 47 | content: ''; 48 | // stylelint-disable-next-line primer/borders 49 | border-bottom: 2px dashed var(--borderColor-default, var(--color-border-default)); 50 | } 51 | 52 | .CircleBadge { 53 | position: relative; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/box/box-overlay.scss: -------------------------------------------------------------------------------- 1 | .Box--overlay { 2 | // stylelint-disable-next-line primer/responsive-widths 3 | width: 448px; 4 | margin-right: auto; 5 | margin-left: auto; 6 | background-color: var(--bgColor-default, var(--color-canvas-default)); 7 | background-clip: padding-box; 8 | border-color: var(--borderColor-default, var(--color-border-default)); 9 | // stylelint-disable-next-line primer/box-shadow 10 | box-shadow: 0 0 18px rgb(0, 0, 0, 0.4); 11 | 12 | .Box-header { 13 | margin: 0; 14 | border-width: 0; 15 | // stylelint-disable-next-line primer/borders 16 | border-bottom-width: $border-width; 17 | // stylelint-disable-next-line primer/borders 18 | border-top-left-radius: $border-radius; 19 | // stylelint-disable-next-line primer/borders 20 | border-top-right-radius: $border-radius; 21 | } 22 | } 23 | 24 | .Box-overlay--narrow { 25 | width: 320px; 26 | } 27 | 28 | .Box-overlay--wide { 29 | // stylelint-disable-next-line primer/responsive-widths 30 | width: 640px; 31 | } 32 | 33 | .Box-body { 34 | &.scrollable-overlay { 35 | max-height: 400px; 36 | overflow-y: scroll; 37 | } 38 | 39 | .help { 40 | padding-top: var(--base-size-8); 41 | margin: 0; 42 | color: var(--fgColor-muted, var(--color-fg-muted)); 43 | text-align: center; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /__tests__/utils/docs.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import frontMatter from 'front-matter' 3 | import yaml from 'js-yaml' 4 | import {globby} from 'globby' 5 | import { fileURLToPath } from 'url' 6 | import {join, dirname} from 'path' 7 | 8 | const __dirname = dirname(fileURLToPath(import.meta.url)) 9 | 10 | const docsPath = join(__dirname, '../../docs') 11 | 12 | function collectNavLinks(links) { 13 | let foundLinks = [] 14 | for (const link of links) { 15 | foundLinks.push({ 16 | title: link['title'], 17 | url: link['url'] 18 | }) 19 | if (link['children']) { 20 | foundLinks = foundLinks.concat(collectNavLinks(link['children'])) 21 | } 22 | } 23 | return foundLinks 24 | } 25 | 26 | export function getNavigationLinks() { 27 | const nav = yaml.load(fs.readFileSync(join(docsPath, './src/@primer/gatsby-theme-doctocat/nav.yml'), 'utf8')) 28 | return collectNavLinks(nav) 29 | } 30 | 31 | export async function getContentFrontmatter() { 32 | const fm = {} 33 | 34 | const paths = await globby(join(docsPath, './content/**/*.md*')) 35 | for (const path of paths) { 36 | const contents = fs.readFileSync(path, 'utf8') 37 | const fmContents = frontMatter(contents) 38 | fm[path.replace(join(docsPath, './content'), '').replace(/(\/index)?\.mdx?/, '')] = fmContents['attributes'] 39 | } 40 | return fm 41 | } 42 | -------------------------------------------------------------------------------- /src/marketing/utilities/animations.scss: -------------------------------------------------------------------------------- 1 | // Animation utilities for marketing 2 | 3 | .hover-grow-mktg { 4 | // stylelint-disable-next-line declaration-property-value-no-unknown 5 | transition: transform 0.4s $ease-mktg; 6 | 7 | &:hover { 8 | transform: scale3d(1.025, 1.025, 1.025); 9 | } 10 | } 11 | 12 | // Animated arrow symbol, used in marketing links, buttons, etc. 13 | .btn-mktg, 14 | .link-mktg, 15 | .arrow-target-mktg { 16 | .octicon { 17 | width: 1em; 18 | height: 1em; 19 | } 20 | 21 | .arrow-symbol-mktg { 22 | transition: transform 0.2s; 23 | transform: translateX(0); 24 | 25 | // stylelint-disable-next-line selector-max-type 26 | path:last-child { 27 | stroke-dasharray: 10; 28 | stroke-dashoffset: 10; 29 | transition: stroke-dashoffset 0.2s; 30 | } 31 | } 32 | 33 | @media screen and (prefers-reduced-motion: no-preference) { 34 | &:hover, 35 | &:focus { 36 | .arrow-symbol-mktg { 37 | transform: translateX(4px); 38 | 39 | // stylelint-disable-next-line selector-max-type, selector-max-specificity, max-nesting-depth 40 | path:last-child { 41 | stroke-dashoffset: 20; 42 | } 43 | } 44 | } 45 | 46 | &:active { 47 | .arrow-symbol-mktg { 48 | transform: translateX(6px); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/labels/mixins.scss: -------------------------------------------------------------------------------- 1 | // Label mixins 2 | 3 | // Default: 20px 4 | // Large: 24px 5 | // Inline: em based 6 | 7 | @mixin labels-base { 8 | display: inline-block; 9 | // stylelint-disable-next-line primer/spacing 10 | padding: 0 7px; 11 | // stylelint-disable-next-line primer/typography 12 | font-size: $font-size-small; 13 | // stylelint-disable-next-line primer/typography 14 | font-weight: $font-weight-semibold; 15 | // stylelint-disable-next-line primer/typography 16 | line-height: 18px; 17 | white-space: nowrap; 18 | // stylelint-disable-next-line primer/borders, primer/colors 19 | border: $border-width $border-style transparent; 20 | // stylelint-disable-next-line primer/borders 21 | border-radius: 2em; 22 | } 23 | 24 | @mixin labels-large { 25 | // stylelint-disable-next-line primer/spacing 26 | padding-right: 10px; 27 | // stylelint-disable-next-line primer/spacing 28 | padding-left: 10px; 29 | // stylelint-disable-next-line primer/typography 30 | line-height: 22px; 31 | } 32 | 33 | // Inline 34 | // 35 | // Doesn't increase height of parent element 36 | // Can be used with different font-sizes 37 | 38 | @mixin labels--inline { 39 | display: inline; 40 | // stylelint-disable-next-line primer/spacing 41 | padding: 0.12em $em-spacer-5; 42 | // stylelint-disable-next-line primer/typography 43 | font-size: 85%; 44 | } 45 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/SideNav/SideNav.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as SideNavStories from './SideNav.stories' 4 | 5 | 6 | 7 | # SideNav 8 | 9 | The Side Nav is a vertical list of navigational links, typically used on the left side of a page. For maximum flexibility, **Side Nav elements have no default width or positioning**. We suggest using [column grid](../utilities/grid) classes or an inline `width` style for sizing, and [flexbox utilities](../utilities/flexbox) for positioning alongside content. 10 | 11 | - You can use a **border** if the parent element doesn't have it already. 12 | - Add `aria-current="page"` to show a link as selected. Selected button elements in tab-like UIs should instead have `aria-selected="true"`. 13 | 14 | 15 | 16 | Different kind of content can be added inside a Side Nav item. Use utility classes to further style them if needed. 17 | 18 | 19 | 20 | The `.SideNav-subItem` is an alternative, more lightweight version without borders and more condensed. It can be used stand-alone. 21 | 22 | 23 | 24 | Or also appear nested, as a sub navigation. Use margin/padding utility classes to add indentation. 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/.storybook/preview.css: -------------------------------------------------------------------------------- 1 | @import '@primer/primitives/dist/css/base/size/size.css'; 2 | @import '@primer/primitives/dist/css/base/typography/typography.css'; 3 | @import '@primer/primitives/dist/css/functional/size/border.css'; 4 | @import '@primer/primitives/dist/css/functional/size/breakpoints.css'; 5 | @import '@primer/primitives/dist/css/functional/size/size-coarse.css'; 6 | @import '@primer/primitives/dist/css/functional/size/size-fine.css'; 7 | @import '@primer/primitives/dist/css/functional/size/size.css'; 8 | @import '@primer/primitives/dist/css/functional/size/viewport.css'; 9 | @import '@primer/primitives/dist/css/functional/typography/typography.css'; 10 | 11 | /* color */ 12 | @import '@primer/primitives/dist/css/functional/themes/light.css'; 13 | @import '@primer/primitives/dist/css/functional/themes/light-tritanopia.css'; 14 | @import '@primer/primitives/dist/css/functional/themes/light-high-contrast.css'; 15 | @import '@primer/primitives/dist/css/functional/themes/light-colorblind.css'; 16 | @import '@primer/primitives/dist/css/functional/themes/dark.css'; 17 | @import '@primer/primitives/dist/css/functional/themes/dark-colorblind.css'; 18 | @import '@primer/primitives/dist/css/functional/themes/dark-dimmed.css'; 19 | @import '@primer/primitives/dist/css/functional/themes/dark-high-contrast.css'; 20 | @import '@primer/primitives/dist/css/functional/themes/dark-tritanopia.css'; 21 | -------------------------------------------------------------------------------- /src/markdown/tables.scss: -------------------------------------------------------------------------------- 1 | // Needs refactoring 2 | // stylelint-disable selector-max-type, selector-max-compound-selectors 3 | .markdown-body { 4 | // Tables 5 | table { 6 | display: block; 7 | width: 100%; // keep for backwards compatibility 8 | width: max-content; 9 | max-width: 100%; 10 | overflow: auto; 11 | font-variant: tabular-nums; 12 | 13 | th { 14 | // stylelint-disable-next-line primer/typography 15 | font-weight: $font-weight-bold; 16 | } 17 | 18 | th, 19 | td { 20 | // stylelint-disable-next-line primer/spacing 21 | padding: 6px 13px; 22 | // stylelint-disable-next-line primer/borders, primer/colors 23 | border: $border-width $border-style var(--borderColor-default, var(--color-border-default)); 24 | } 25 | 26 | td { 27 | > :last-child { 28 | margin-bottom: 0; 29 | } 30 | } 31 | 32 | tr { 33 | background-color: var(--bgColor-default, var(--color-canvas-default)); 34 | // stylelint-disable-next-line primer/borders, primer/colors 35 | border-top: $border-width $border-style var(--borderColor-muted, var(--color-border-muted)); 36 | 37 | &:nth-child(2n) { 38 | background-color: var(--bgColor-muted, var(--color-canvas-subtle)); 39 | } 40 | } 41 | 42 | img { 43 | background-color: transparent; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/BranchName/BranchName.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {BADGE} from '@geometricpanda/storybook-addon-badges' 3 | 4 | export default { 5 | title: 'Deprecated/BranchName', 6 | parameters: { 7 | storyType: 'banner', 8 | controls: {hideNoControlsWarning: true}, 9 | badges: [BADGE.DEPRECATED], 10 | }, 11 | } 12 | 13 | export const Default = () => { 14 | return ( 15 |
16 | a_new_feature_branch 17 | 18 | a_new_feature_branch 19 | 20 | 21 | 28 | 32 | 33 | a_new_feature_branch 34 | 35 |
36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /docs/stories/static/color-image.svg: -------------------------------------------------------------------------------- 1 | 2 | Group 3 | Created using Figma 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/Pagination/Pagination.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as PaginationStories from './Pagination.stories' 4 | 5 | 6 | 7 | # Pagination 8 | 9 | Use the pagination component to apply button styles to a connected set of links that go to related pages (for example, previous, next, or page numbers). 10 | 11 | ## Previous/next pagination 12 | 13 | You can make a very simple pagination container with just the Previous and Next buttons. Add the `aria-disabled="true"` attribute to the `Previous` button if there isn't a preceding page, or to the `Next` button if there isn't a succeeding page. 14 | 15 | 16 | 17 | ## Numbered pagination 18 | 19 | For pagination across multiple pages, make sure it's clear to the user where they are in a set of pages. 20 | 21 | To do this, add anchor links to the `pagination` element. Previous and Next buttons should always be present. Add the `aria-disabled="true"` attribute to the Previous button if you're on the first page. Apply the `aria-current="page"` attribute to the current numbered page. 22 | 23 | As always, make sure to include the appropriate `aria` attributes to make the element accessible. 24 | 25 | - Add `aria-label="Pagination"` to the `paginate-container` element. 26 | - Add `aria-label="Page X"` to each anchor link. 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/stories/static/components-image.svg: -------------------------------------------------------------------------------- 1 | styles 2 | -------------------------------------------------------------------------------- /docs/stories/static/components-image copy.svg: -------------------------------------------------------------------------------- 1 | styles 2 | -------------------------------------------------------------------------------- /src/support/mixins/layout.scss: -------------------------------------------------------------------------------- 1 | // Responsive media queries 2 | 3 | @mixin breakpoint($breakpoint) { 4 | @if $breakpoint == '' { 5 | @content; 6 | } 7 | 8 | @else { 9 | // Retrieves the value from the key 10 | $value: map-get($breakpoints, $breakpoint); 11 | 12 | // If the key exists in the map 13 | @if $value != null { 14 | // Prints a media query based on the value 15 | @media (min-width: $value) { 16 | @content; 17 | } 18 | } 19 | 20 | // If the key doesn't exist in the map 21 | @else { 22 | @warn 'Unfortunately, no value could be retrieved from `#{$breakpoint}`. ' 23 | + 'Please make sure it is defined in `$breakpoints` map.'; 24 | } 25 | } 26 | } 27 | 28 | // Retina media query 29 | 30 | @mixin retina-media-query { 31 | @media 32 | only screen and (-webkit-min-device-pixel-ratio: 2), 33 | only screen and (min--moz-device-pixel-ratio: 2), 34 | only screen and (-moz-min-device-pixel-ratio: 2), 35 | only screen and (-o-min-device-pixel-ratio: 2/1), 36 | only screen and (min-device-pixel-ratio: 2), 37 | only screen and (min-resolution: 192dpi), 38 | only screen and (min-resolution: 2dppx) { 39 | @content; 40 | } 41 | } 42 | 43 | // Clearfix 44 | // 45 | // Clears floats via mixin. 46 | 47 | @mixin clearfix { 48 | &::before { 49 | display: table; 50 | content: ''; 51 | } 52 | 53 | &::after { 54 | display: table; 55 | clear: both; 56 | content: ''; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/IssueLabel/IssueLabel.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {BADGE} from '@geometricpanda/storybook-addon-badges' 3 | 4 | export default { 5 | title: 'Deprecated/IssueLabel', 6 | parameters: { 7 | storyType: 'banner', 8 | controls: {hideNoControlsWarning: true}, 9 | badges: [BADGE.DEPRECATED], 10 | }, 11 | } 12 | 13 | export const Default = () => { 14 | return ( 15 | <> 16 | Primer 17 | bug 🐛 18 | help wanted 19 | 🚂 deploy: train 20 | 21 | ) 22 | } 23 | 24 | export const Big = () => { 25 | return ( 26 | <> 27 | Primer 28 | bug 🐛 29 | 30 | help wanted 31 | 32 | 33 | 🚂 deploy: train 34 | 35 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /__tests__/build.test.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | 3 | let distDir 4 | 5 | beforeAll(() => { 6 | distDir = fs.readdirSync('./dist') 7 | }) 8 | 9 | describe('./dist/ folder', () => { 10 | let distCSS, distMap, distJS 11 | 12 | beforeAll(() => { 13 | distCSS = distDir.filter(i => i.match(/\.css$/)) 14 | distMap = distDir.filter(i => i.match(/\.map$/)) 15 | distJS = distDir.filter(i => i.match(/\.js$/)) 16 | }) 17 | 18 | it('is not empty', () => { 19 | expect(distDir).not.toBeNull() 20 | expect(distDir.length).not.toBe(0) 21 | }) 22 | 23 | it('contains source maps', () => { 24 | for (const file of distCSS) { 25 | if (file.includes('support')) { continue } 26 | expect(distMap).toContain(`${file}.map`) 27 | } 28 | }) 29 | 30 | it('contains stats export files', () => { 31 | for (const file of distCSS) { 32 | expect(distJS).toContain(file.replace('.css', '.js')) 33 | } 34 | }) 35 | 36 | it('contains stats/ folder', () => { 37 | expect(distDir).toContain('stats') 38 | }) 39 | }) 40 | 41 | describe('./dist/stats/ folder', () => { 42 | let statsDir 43 | 44 | beforeAll(() => { 45 | statsDir = fs.readdirSync('./dist/stats') 46 | }) 47 | 48 | it('is not empty', () => { 49 | expect(statsDir).not.toBeNull() 50 | expect(statsDir.length).not.toBe(0) 51 | }) 52 | 53 | it('contains a css file for each stat file', () => { 54 | for (const file of statsDir) { 55 | expect(distDir).toContain(file.replace('.json', '.css')) 56 | } 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | jobs: 8 | stylelint: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v5 12 | - uses: actions/setup-node@v6 13 | with: 14 | node-version: 20 15 | cache: 'npm' 16 | - run: npm ci 17 | - run: npm run dist 18 | - name: Lint source files 19 | run: npm run stylelint:fix 20 | - name: Push up any fixes 21 | if: ${{ github.event_name == 'pull_request' }} 22 | uses: stefanzweifel/git-auto-commit-action@v7 23 | with: 24 | commit_message: Fixing stylelint issues 25 | commit_user_name: GitHub Design Engineering Bot 26 | commit_user_email: primer-css@users.noreply.github.com 27 | commit_author: primer-css 28 | file_pattern: src/**/*.scss 29 | 30 | eslint: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v5 34 | - uses: actions/setup-node@v6 35 | with: 36 | node-version: 20 37 | cache: 'npm' 38 | - run: npm ci 39 | - name: Lint workflow files 40 | run: npm run eslint 41 | 42 | test: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v5 46 | - uses: actions/setup-node@v6 47 | with: 48 | node-version: 20 49 | cache: 'npm' 50 | - run: npm ci 51 | - name: Jest 52 | run: npm test 53 | -------------------------------------------------------------------------------- /docs/stories/support/Prototyping.mdx: -------------------------------------------------------------------------------- 1 | # Prototyping 2 | 3 | You're welcome to use whatever prototyping tool suits your needs, however we've set up some options that will help you get started quickly. 4 | 5 | The power of prototyping in code is that you can create clickable mocks that can be shared via a URL. This can be useful for exploring designs and interactions or for user research sessions. Prototypes can be throw-away, or part of your process for building out new features since you can work with the same CSS we use in production. 6 | 7 | ## Simple HTML prototype with Primer CSS 8 | 9 | Copy the code below and paste it in a HTML file. The CDN link is always linked to the most up to date version of Primer CSS and includes all of the modules in the core, product, and marketing packages. 10 | 11 | This method requires no dev environment set up and is useful for when you want to create simple prototypes using Primer CSS. 12 | 13 | ```html 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ``` 24 | 25 | Note that the above loads a specific version of Primer CSS. You can also load the "latest" version with `https://unpkg.com/@primer/css/dist/primer.css`. Just be aware that it could include breaking changes. So for prototypes meant to last for a long time it's better to lock Primer CSS to a specific version. 26 | -------------------------------------------------------------------------------- /.github/workflows/deploy_production.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - 'main' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | contents: write 10 | pages: write 11 | id-token: write 12 | deployments: write 13 | issues: write 14 | statuses: write 15 | checks: write 16 | 17 | jobs: 18 | guard: 19 | name: Guard 20 | runs-on: ubuntu-latest 21 | outputs: 22 | # To avoid deploying documentation for unrelease changes, we check the number of changeset files. 23 | # If it's 0, we deploy. 24 | should_deploy: ${{ steps.changeset-count.outputs.change_count == 0 }} 25 | steps: 26 | - uses: actions/checkout@v5 27 | 28 | - id: changeset-count 29 | run: echo "::set-output name=change_count::$(ls .changeset/*.md | grep -v README | wc -l | xargs)" 30 | 31 | # Log changeset count for debugging purposes 32 | - name: Log changeset count 33 | run: echo ${{ steps.changeset-count.outputs.change_count }} 34 | 35 | # Log guard output for debugging purposes 36 | - name: Log guard output 37 | run: echo ${{ needs.guard.outputs.should_deploy }} 38 | deploy: 39 | if: ${{ github.repository == 'primer/css' }} 40 | name: Production 41 | needs: [guard] 42 | uses: primer/.github/.github/workflows/deploy.yml@v2.4.0 43 | secrets: 44 | gh_token: ${{ secrets.GITHUB_TOKEN }} 45 | with: 46 | node_version: 20 47 | install: npm i && cd docs && npm i && cd .. 48 | build: npm run build:docs 49 | output_dir: docs/public 50 | -------------------------------------------------------------------------------- /docs/.storybook/storybook.css: -------------------------------------------------------------------------------- 1 | .story-wrap { 2 | font-family: var(--fontStack-system); 3 | color: var(--fgColor-default); 4 | } 5 | 6 | #storybook-preview-wrapper { 7 | background-color: var(--bgColor-default) !important; 8 | width: 100% !important; 9 | height: 100% !important; 10 | } 11 | 12 | .theme-wrap-grid { 13 | display: grid; 14 | grid-template-columns: repeat(4, minmax(var(--breakpoint-xsmall, 20rem), auto)); 15 | grid-gap: 1px; 16 | height: 100vh; 17 | } 18 | 19 | .story-wrap-grid { 20 | outline: 1px solid #d4d4d8; 21 | padding-bottom: 40px; 22 | position: relative; 23 | } 24 | 25 | @media (max-width: calc(20rem * 4)) { 26 | .theme-wrap-grid { 27 | grid-template-columns: repeat(3, minmax(var(--breakpoint-xsmall, 20rem), auto)); 28 | } 29 | } 30 | 31 | @media (max-width: calc(20rem * 3)) { 32 | .theme-wrap-grid { 33 | grid-template-columns: repeat(2, minmax(var(--breakpoint-xsmall, 20rem), auto)); 34 | } 35 | } 36 | 37 | @media (max-width: calc(20rem * 2)) { 38 | .theme-wrap-grid { 39 | display: block; 40 | } 41 | } 42 | 43 | .theme-name { 44 | position: absolute; 45 | bottom: 0; 46 | right: 0; 47 | background-color: var(--bgColor-muted); 48 | padding: var(--base-size-4) var(--base-size-8); 49 | font: var(--text-caption-shorthand); 50 | margin: 0; 51 | } 52 | 53 | code { 54 | padding: 0.2em 0.4em; 55 | font-family: var(--fontStack-monospace); 56 | font-size: var(--text-codeInline-size); 57 | background-color: var(--bgColor-muted); 58 | border-radius: var(--borderRadius-small); 59 | font-weight: var(--base-text-weight-normal); 60 | } 61 | -------------------------------------------------------------------------------- /src/header/header.scss: -------------------------------------------------------------------------------- 1 | .Header { 2 | z-index: 32; // TODO: Figure out z-index system 3 | display: flex; 4 | padding: var(--base-size-16); 5 | // stylelint-disable-next-line primer/typography 6 | font-size: $h5-size; 7 | // stylelint-disable-next-line primer/typography 8 | line-height: $lh-default; 9 | color: var(--header-fgColor-default, var(--color-header-text)); 10 | background-color: var(--header-bgColor, var(--color-header-bg)); 11 | align-items: center; 12 | flex-wrap: nowrap; 13 | } 14 | 15 | .Header-item { 16 | display: flex; 17 | margin-right: var(--base-size-16); 18 | align-self: stretch; 19 | align-items: center; 20 | flex-wrap: nowrap; 21 | } 22 | 23 | .Header-item--full { 24 | flex: auto; 25 | } 26 | 27 | .Header-link { 28 | // stylelint-disable-next-line primer/typography 29 | font-weight: $font-weight-bold; 30 | color: var(--header-fgColor-logo, var(--color-header-logo)); 31 | white-space: nowrap; 32 | 33 | &:hover, 34 | &:focus { 35 | color: var(--header-fgColor-default, var(--color-header-text)); 36 | text-decoration: none; 37 | } 38 | } 39 | 40 | .Header-input { 41 | color: var(--header-fgColor-default, var(--color-header-text)); 42 | background-color: var(--headerSearch-bgColor, var(--color-header-search-bg)); 43 | // stylelint-disable-next-line primer/borders, primer/colors 44 | border: $border-width $border-style var(--headerSearch-borderColor, var(--color-header-search-border)); 45 | box-shadow: none; 46 | 47 | &::placeholder { 48 | // stylelint-disable-next-line primer/colors 49 | color: rgb(255, 255, 255, 0.75); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/axe.yml: -------------------------------------------------------------------------------- 1 | name: axe 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | axe: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v5 14 | with: 15 | fetch-depth: 0 16 | - name: Get changed files 17 | id: changed-files 18 | uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 19 | with: 20 | files: | 21 | docs/content/components/**/*.md 22 | docs/content/utilities/**/*.md 23 | files_ignore: | 24 | docs/content/components/index.md 25 | docs/content/utilities/index.md 26 | - name: Save changed files 27 | run: | 28 | echo "STRING_OF_PATHS_WE_CARE_ABOUT=${{ steps.changed-files.outputs.all_changed_files }}" >> $GITHUB_ENV 29 | - name: Use Node.js 30 | if: steps.changed-files.outputs.any_changed == 'true' 31 | uses: actions/setup-node@v6 32 | with: 33 | node-version: 20 34 | cache: 'npm' 35 | - run: npm ci 36 | if: steps.changed-files.outputs.any_changed == 'true' 37 | - run: npm run dist 38 | if: steps.changed-files.outputs.any_changed == 'true' 39 | - name: Run docs site 40 | if: steps.changed-files.outputs.any_changed == 'true' 41 | run: | 42 | npm run dev & npx wait-on http://localhost:8000 43 | - name: Run axe script 44 | if: steps.changed-files.outputs.any_changed == 'true' 45 | run: | 46 | script/axe-docs $STRING_OF_PATHS_WE_CARE_ABOUT 47 | -------------------------------------------------------------------------------- /docs/stories/helpers/pageLayoutBehavior.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | export default function PageLayoutBehavior() { 3 | 4 | const pageLayoutSelector = '.PageLayout.PageLayout--responsive-separateRegions'; 5 | const primaryRegionSelector = 'PageLayout--responsive-primary'; 6 | 7 | const detectPageLayoutHash = () => { 8 | 9 | const pageLayout = document.querySelector(pageLayoutSelector); 10 | 11 | let dest; 12 | if (location.hash === '') { 13 | dest = pageLayout.getAttribute('data-primary-region'); 14 | } else if (location.hash === '#pane') { 15 | dest = 'pane'; 16 | } else if (location.hash === '#content') { 17 | dest = 'content'; 18 | } else { 19 | return; 20 | } 21 | 22 | pageLayout.setAttribute('data-current-region', dest); 23 | 24 | if (dest === 'pane') { 25 | pageLayout.classList.replace(primaryRegionSelector + '-content', primaryRegionSelector + '-pane'); 26 | } else { 27 | pageLayout.classList.replace(primaryRegionSelector + '-pane', primaryRegionSelector + '-content'); 28 | } 29 | }; 30 | 31 | window.addEventListener("hashchange", () => { 32 | detectPageLayoutHash(); 33 | }); 34 | 35 | document.addEventListener('DOMContentLoaded', (event) => { 36 | const pageLayout = document.querySelector(pageLayoutSelector); 37 | const primaryRegion = pageLayout.classList.contains(primaryRegionSelector + '-pane') ? 'pane' : 'content'; 38 | 39 | if (pageLayout.getAttribute('data-primary-region') === null) { 40 | pageLayout.setAttribute('data-primary-region', primaryRegion); 41 | } 42 | 43 | detectPageLayoutHash(); 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /src/support/variables/typography.scss: -------------------------------------------------------------------------------- 1 | // Typography variables 2 | 3 | // Heading sizes - mobile 4 | // h4-h6 remain the same size on both mobile & desktop 5 | $h00-size-mobile: 40px !default; 6 | $h0-size-mobile: 32px !default; 7 | $h1-size-mobile: 26px !default; 8 | $h2-size-mobile: 22px !default; 9 | $h3-size-mobile: 18px !default; 10 | 11 | // Heading sizes - desktop 12 | $h00-size: 48px !default; 13 | $h0-size: 40px !default; 14 | $h1-size: 32px !default; 15 | $h2-size: 24px !default; 16 | $h3-size: 20px !default; 17 | $h4-size: 16px !default; 18 | $h5-size: 14px !default; 19 | $h6-size: 12px !default; 20 | 21 | $font-size-small: 12px !default; 22 | 23 | // Font weights 24 | $font-weight-bold: var(--base-text-weight-semibold, 600) !default; 25 | $font-weight-semibold: var(--base-text-weight-medium, 500) !default; 26 | $font-weight-normal: var(--base-text-weight-normal, 400) !default; 27 | $font-weight-light: var(--base-text-weight-light, 300) !default; 28 | 29 | // Line heights 30 | $lh-condensed-ultra: 1 !default; 31 | $lh-condensed: 1.25 !default; 32 | $lh-default: 1.5 !default; 33 | 34 | // Font stacks 35 | $body-font: var(--fontStack-sansSerif, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji') !default; 36 | 37 | // Monospace font stack 38 | // Note: SFMono-Regular needs to come before SF Mono to fix an older version of the font in Chrome 39 | $mono-font: var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace) !default; 40 | 41 | // The base body size 42 | $body-font-size: 14px !default; 43 | $body-line-height: $lh-default !default; 44 | -------------------------------------------------------------------------------- /docs/.storybook/theme.js: -------------------------------------------------------------------------------- 1 | import {create} from '@storybook/theming' 2 | import packageJson from '../../package.json' 3 | 4 | export default create({ 5 | brandTitle: ` 6 |
7 | 10 | ${packageJson.name}@${packageJson.version} 11 |
12 | `, 13 | }) 14 | -------------------------------------------------------------------------------- /src/markdown/footnotes.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable selector-max-type 2 | // stylelint-disable selector-max-compound-selectors 3 | 4 | .markdown-body { 5 | [data-footnote-ref] { 6 | &::before { 7 | content: '['; 8 | } 9 | 10 | &::after { 11 | content: ']'; 12 | } 13 | } 14 | 15 | .footnotes { 16 | // stylelint-disable-next-line primer/typography 17 | font-size: $h6-size; 18 | color: var(--fgColor-muted, var(--color-fg-muted)); 19 | // stylelint-disable-next-line primer/borders, primer/colors 20 | border-top: $border; 21 | 22 | ol { 23 | padding-left: var(--base-size-16); 24 | 25 | ul { 26 | display: inline-block; 27 | padding-left: var(--base-size-16); 28 | margin-top: var(--base-size-16); 29 | } 30 | } 31 | 32 | li { 33 | position: relative; 34 | } 35 | 36 | li:target::before { 37 | position: absolute; 38 | top: calc(var(--base-size-8) * -1); 39 | right: calc(var(--base-size-8) * -1); 40 | bottom: calc(var(--base-size-8) * -1); 41 | left: calc(var(--base-size-24) * -1); 42 | pointer-events: none; 43 | content: ''; 44 | // stylelint-disable-next-line primer/borders, primer/colors, declaration-property-value-no-unknown 45 | border: 2px $border-style var(--borderColor-accent-emphasis, var(--color-accent-emphasis)); 46 | // stylelint-disable-next-line primer/borders 47 | border-radius: $border-radius; 48 | } 49 | 50 | li:target { 51 | color: var(--fgColor-default, var(--color-fg-default)); 52 | } 53 | 54 | .data-footnote-backref g-emoji { 55 | // stylelint-disable-next-line primer/typography 56 | font-family: monospace; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/Pagination/Pagination.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {BADGE} from '@geometricpanda/storybook-addon-badges' 3 | 4 | export default { 5 | title: 'Deprecated/Pagination', 6 | parameters: { 7 | storyType: 'banner', 8 | controls: {hideNoControlsWarning: true}, 9 | badges: [BADGE.DEPRECATED], 10 | }, 11 | } 12 | 13 | export const Default = () => { 14 | return ( 15 | <> 16 | 26 | 27 | ) 28 | } 29 | 30 | export const Numbered = () => { 31 | return ( 32 | 59 | ) 60 | } 61 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/Toast/Toast.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as ToastStories from './Toast.stories' 4 | 5 | 6 | 7 | # Toast 8 | 9 | Toasts are used to show live, time-sensitive feedback to users. 10 | 11 | ## Default 12 | 13 | To create a default toast, use `.Toast`. Always use the `info` icon for default messages. 14 | 15 | 16 | 17 | The Toast content is formattable. We recommend keeping your message under 140 characters. 18 | 19 | ## Variations 20 | 21 | Use the success, warning, and error modifiers to communicate different states. 22 | 23 | Always use the `check` octicon for success states. 24 | 25 | 26 | 27 | Always use the `alert` octicon for warning states. 28 | 29 | 30 | 31 | Always use the `stop` octicon for error states. 32 | 33 | 34 | 35 | ## Toast with dismiss 36 | 37 | Use `.Toast-dismissButton` to allow a user to explicitly dismiss a Toast. 38 | 39 | 40 | 41 | ## Toast with link 42 | 43 | Include a link to allow users to take actions within a Toast. 44 | 45 | 46 | 47 | ## Toast animation in 48 | 49 | The `Toast--animateIn` and `Toast--animateOut` modifier classes can be used to animate the toast in and out from the bottom. 50 | 51 | 52 | 53 | ## Toast with loading animation 54 | 55 | Add the `Toast--spinner` modifier class on the `Toast-icon` `svg` to communicate a loading state. 56 | 57 | 58 | 59 | ## Toast position 60 | 61 | Use the `position-fixed bottom-0 left-0` utility classes on a wrapper element to position Toasts at the **bottom left** of the viewport. 62 | -------------------------------------------------------------------------------- /docs/stories/deprecated-components/Header/Header.mdx: -------------------------------------------------------------------------------- 1 | import {Canvas, Meta, Story} from '@storybook/blocks' 2 | 3 | import * as HeaderStories from './Header.stories' 4 | 5 | 6 | 7 | # Header 8 | 9 | Use the Header component to create a header that has all of its items aligned vertically with consistent horizontal spacing. 10 | 11 | ## Header 12 | 13 | The `.Header` class is the wrapping class that aligns all the items properly and gives the header its dark background. Each direct child of the `.Header` component is expected to be a `.Header-item` component. The component utilizes flexbox CSS to align all these items properly and applies spacing scale margin. 14 | 15 | 16 | 17 | ## Header-item 18 | 19 | All items directly under the `.Header` component should be a `.Header-item` component. Inside these components can be anything (text, forms, images...), and the `.Header-item` component will make sure these elements vertically align with each other. 20 | 21 | `.Header-item` elements have a built-in margin that will need to be overridden with the `mr-0` utility class for the last element in the container. We relied on the utility classes here instead of `:last-child` because the last child isn't always the item visible. On responsive pages, there's a mobile menu that gets presented to the user at smaller breakpoints. 22 | 23 | 24 | 25 | ### Header-item--full 26 | 27 | The `.Header-item` element has a modifier class, `.Header-item--full`, that stretches it to fill the available space and push any remaining items to the right. 28 | 29 | 30 | 31 | ## Header-link 32 | 33 | Add the `.Header-link` class to any anchor tags in the header to give them consistent styling and hover opacity. This class makes the links white, bold and 70% fade on hover. 34 | 35 | 36 | -------------------------------------------------------------------------------- /.github/workflows/deploy_preview.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | pull_request: 4 | 5 | permissions: 6 | contents: write 7 | pages: write 8 | id-token: write 9 | deployments: write 10 | issues: write 11 | statuses: write 12 | checks: write 13 | 14 | jobs: 15 | deploy: 16 | if: ${{ github.repository == 'primer/css' }} 17 | uses: primer/.github/.github/workflows/deploy_preview.yml@v2.4.0 18 | name: Deploy preview 19 | secrets: 20 | gh_token: ${{ secrets.GITHUB_TOKEN }} 21 | with: 22 | node_version: 20 23 | install: npm i && cd docs && npm i && cd .. 24 | build: npm run build:docs:preview 25 | output_dir: docs/public 26 | 27 | deploy-storybook: 28 | if: ${{ github.repository == 'primer/css' }} 29 | name: Preview Storybook 30 | runs-on: ubuntu-latest 31 | needs: deploy 32 | steps: 33 | - uses: actions/checkout@v5 34 | 35 | - uses: chrnorm/deployment-action@v2.0.7 36 | name: Create GitHub deployment for storybook 37 | id: storybook 38 | with: 39 | token: ${{ secrets.GITHUB_TOKEN }} 40 | environment: Storybook Preview 41 | environment_url: '${{ needs.deploy.outputs.deployment_url }}/storybook' 42 | 43 | - name: Update storybook deployment status (success) 44 | if: success() 45 | uses: chrnorm/deployment-status@v2.0.3 46 | with: 47 | token: ${{ secrets.GITHUB_TOKEN }} 48 | environment-url: '${{ needs.deploy.outputs.deployment_url }}/storybook' 49 | state: 'success' 50 | deployment-id: ${{ steps.storybook.outputs.deployment_id }} 51 | 52 | - name: Update storybook deployment status (failure) 53 | if: failure() 54 | uses: chrnorm/deployment-status@v2.0.3 55 | with: 56 | token: ${{ secrets.GITHUB_TOKEN }} 57 | state: 'failure' 58 | deployment-id: ${{ steps.storybook.outputs.deployment_id }} 59 | -------------------------------------------------------------------------------- /src/base/typography-base.scss: -------------------------------------------------------------------------------- 1 | // Headings 2 | // -------------------------------------------------- 3 | // stylelint-disable selector-max-type 4 | h1, 5 | h2, 6 | h3, 7 | h4, 8 | h5, 9 | h6 { 10 | margin-top: 0; 11 | margin-bottom: 0; 12 | } 13 | 14 | h1 { @include h1; } 15 | h2 { @include h2; } 16 | h3 { @include h3; } 17 | h4 { @include h4; } 18 | h5 { @include h5; } 19 | h6 { @include h6; } 20 | 21 | // Body text 22 | // -------------------------------------------------- 23 | 24 | p { 25 | margin-top: 0; 26 | // stylelint-disable-next-line primer/spacing 27 | margin-bottom: 10px; 28 | } 29 | 30 | small { 31 | // stylelint-disable-next-line primer/typography 32 | font-size: 90%; 33 | } 34 | 35 | blockquote { 36 | margin: 0; 37 | } 38 | 39 | // Lists 40 | // -------------------------------------------------- 41 | 42 | ul, 43 | ol { 44 | padding-left: 0; 45 | margin-top: 0; 46 | margin-bottom: 0; 47 | } 48 | 49 | ol ol, 50 | ul ol { 51 | list-style-type: lower-roman; 52 | } 53 | 54 | ul ul ol, 55 | ul ol ol, 56 | ol ul ol, 57 | ol ol ol { 58 | list-style-type: lower-alpha; 59 | } 60 | 61 | dd { 62 | margin-left: 0; 63 | } 64 | 65 | // Monospaced 66 | // -------------------------------------------------- 67 | 68 | tt, 69 | code, 70 | samp { 71 | // stylelint-disable-next-line primer/typography 72 | font-family: $mono-font; 73 | // stylelint-disable-next-line primer/typography 74 | font-size: $font-size-small; 75 | } 76 | 77 | pre { 78 | margin-top: 0; 79 | margin-bottom: 0; 80 | // stylelint-disable-next-line primer/typography 81 | font-family: $mono-font; 82 | // stylelint-disable-next-line primer/typography 83 | font-size: $font-size-small; 84 | } 85 | 86 | // Octicons 87 | // -------------------------------------------------- 88 | 89 | // Move this over here as a temporary override to the octicons source repo 90 | // instead of updating that upstream. 91 | .octicon { 92 | vertical-align: text-bottom; 93 | } 94 | -------------------------------------------------------------------------------- /src/utilities/padding.scss: -------------------------------------------------------------------------------- 1 | // Padding spacer utilities 2 | // stylelint-disable primer/spacing 3 | 4 | // Responsive padding spacer utilities 5 | @each $breakpoint, $variant in $responsive-variants { 6 | @include breakpoint($breakpoint) { 7 | // Loop through the spacer values 8 | @each $scale, $size in $spacer-map-rem-extended { 9 | @if ($scale < length($spacer-map-rem)) { 10 | /* Set a $size padding to all sides at $breakpoint */ 11 | .p#{$variant}-#{$scale} { padding: $size !important; } 12 | } 13 | 14 | /* Set a $size padding to the top at $breakpoint */ 15 | .pt#{$variant}-#{$scale} { padding-top: $size !important; } 16 | /* Set a $size padding to the right at $breakpoint */ 17 | .pr#{$variant}-#{$scale} { padding-right: $size !important; } 18 | /* Set a $size padding to the bottom at $breakpoint */ 19 | .pb#{$variant}-#{$scale} { padding-bottom: $size !important; } 20 | /* Set a $size padding to the left at $breakpoint */ 21 | .pl#{$variant}-#{$scale} { padding-left: $size !important; } 22 | 23 | @if ($scale < length($spacer-map-rem)) { 24 | /* Set a $size padding to the left & right at $breakpoint */ 25 | .px#{$variant}-#{$scale} { 26 | padding-right: $size !important; 27 | padding-left: $size !important; 28 | } 29 | } 30 | 31 | /* Set a $size padding to the top & bottom at $breakpoint */ 32 | .py#{$variant}-#{$scale} { 33 | padding-top: $size !important; 34 | padding-bottom: $size !important; 35 | } 36 | } 37 | } 38 | } 39 | 40 | // responsive padding for containers 41 | .p-responsive { 42 | padding-right: var(--base-size-16) !important; 43 | padding-left: var(--base-size-16) !important; 44 | 45 | @include breakpoint(sm) { 46 | padding-right: var(--base-size-40) !important; 47 | padding-left: var(--base-size-40) !important; 48 | } 49 | 50 | @include breakpoint(lg) { 51 | padding-right: var(--base-size-16) !important; 52 | padding-left: var(--base-size-16) !important; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /docs/stories/support/Deprecations.mdx: -------------------------------------------------------------------------------- 1 | # Deprecations 2 | 3 | As of version 12.7.0, we publish CSS selector and SCSS variable deprecation data (as of 14.0.0) with `@primer/css`. You can access the data via the [Node API](#node) or as [JSON](#json). 4 | 5 | To deprecate a class, variable, or mixin, add the element to the [deprecations.js](https://github.com/primer/css/blob/main/deprecations.js) file with it's replacement value. 6 | 7 | The replacement can be: 8 | 9 | - A `String` for a direct replacement. 10 | - An `Array` for multiple replacement options. 11 | - `null` to indicate there is no replacement. 12 | 13 | This could look something like this: 14 | 15 | ```js 16 | const deprecations = { 17 | 'deprecated-1': 'replacement', 18 | 'deprecated-2': ['replacement-1', 'replacement-2'], 19 | 'deprecated-3': null, 20 | } 21 | ``` 22 | 23 | ## JSON 24 | 25 | The JSON data is available in the unpacked node module's `dist/deprecations.json`, and is an object with the following structure: 26 | 27 | ```json 28 | { 29 | "selectors" {...}, 30 | "variables": {...}, 31 | "mixins": {...} 32 | } 33 | ``` 34 | 35 | `selectors` is an object mapping CSS selectors to their replacements. If the replacement is an Array, then there's multiple options. If the replacement is null then there are no replacements. 36 | 37 | ```json 38 | { 39 | "selectors": { 40 | "deprecated-class": "replacement-class" 41 | } 42 | } 43 | ``` 44 | 45 | `variables` is an object mapping SCSS variables to their replacement SCSS variable. 46 | 47 | ```json 48 | { 49 | "variables": { 50 | "$deprecated-variable": "$replacement-variable" 51 | } 52 | } 53 | ``` 54 | 55 | `mixins` is an object mapping SCSS mixins to their replacement SCSS mixins. 56 | 57 | ```json 58 | { 59 | "mixins": { 60 | "deprecated-mixin": "replacement-mixin" 61 | } 62 | } 63 | ``` 64 | 65 | ## Node 66 | 67 | The Node API for selector deprecations is available at 68 | `@primer/css/deprecations`. 69 | 70 | ### Example: 71 | 72 | ```js 73 | const {deprecatedSelectors, deprecatedSassVariables, deprecatedSassMixins} = require('@primer/css/deprecations') 74 | ``` 75 | -------------------------------------------------------------------------------- /src/forms/form-select.scss: -------------------------------------------------------------------------------- 1 | // Custom select 2 | // 3 | // Apply `.select` to any `