├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── pull_request_template.md └── workflows │ ├── main.yaml │ ├── pr.yaml │ ├── pr_title.yaml │ └── release.yaml ├── .gitignore ├── .husky └── pre-commit ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .storybook ├── main.ts ├── package.json └── preview.jsx ├── .vscode └── settings.json ├── .yarnrc.yml ├── LICENSE ├── README.md ├── babel.config.cjs ├── colors ├── deepBlue.ts ├── elevation.ts ├── grayBlue.ts ├── index.ts ├── neon.ts ├── orange.ts └── red.ts ├── components ├── AccessibleIcon │ ├── AccessibleIcon.stories.tsx │ ├── AccessibleIcon.tsx │ └── index.tsx ├── Accordion │ ├── Accordion.stories.tsx │ ├── Accordion.themes.ts │ ├── Accordion.tsx │ └── index.ts ├── Alert │ ├── Alert.stories.tsx │ ├── Alert.tsx │ └── index.ts ├── AriaTable │ ├── AriaTable.stories.tsx │ ├── AriaTable.test.tsx │ ├── AriaTable.tsx │ └── index.ts ├── Avatar │ ├── Avatar.stories.tsx │ ├── Avatar.tsx │ └── index.ts ├── Badge │ ├── Badge.stories.tsx │ ├── Badge.test.tsx │ ├── Badge.themes.ts │ ├── Badge.tsx │ └── index.ts ├── Blockquote │ ├── Blockquote.stories.tsx │ ├── Blockquote.tsx │ └── index.ts ├── Box │ ├── Box.stories.tsx │ ├── Box.tsx │ └── index.ts ├── Bubble │ ├── Bubble.stories.tsx │ ├── Bubble.tsx │ └── index.ts ├── Button │ ├── Button.stories.tsx │ ├── Button.test.tsx │ ├── Button.themes.ts │ ├── Button.tsx │ └── index.ts ├── ButtonSwitch │ ├── ButtonSwitch.stories.tsx │ ├── ButtonSwitch.themes.ts │ ├── ButtonSwitch.tsx │ └── index.ts ├── Card │ ├── Card.stories.tsx │ ├── Card.themes.ts │ ├── Card.tsx │ └── index.ts ├── Checkbox │ ├── Checkbox.stories.tsx │ ├── Checkbox.themes.ts │ ├── Checkbox.tsx │ └── index.ts ├── Container │ ├── Container.stories.tsx │ ├── Container.tsx │ └── index.ts ├── DateTimePicker │ ├── DateTimePicker.stories.tsx │ ├── DateTimePicker.tsx │ └── index.ts ├── DateTimePickerInput │ ├── DateTimePickerInput.stories.tsx │ ├── DateTimePickerInput.tsx │ └── index.ts ├── Dialog │ ├── Dialog.stories.tsx │ ├── Dialog.themes.ts │ ├── Dialog.tsx │ └── index.ts ├── DropdownMenu │ ├── DropdownMenu.stories.tsx │ ├── DropdownMenu.tsx │ ├── index.ts │ └── styles.ts ├── Elevation │ ├── Elevation.stories.tsx │ ├── Elevation.tsx │ └── index.ts ├── FaencyProvider.tsx ├── Flex │ ├── Flex.stories.tsx │ ├── Flex.tsx │ └── index.ts ├── Grid │ ├── Grid.stories.tsx │ ├── Grid.tsx │ └── index.ts ├── Heading │ ├── Heading.stories.tsx │ ├── Heading.test.tsx │ ├── Heading.themes.ts │ ├── Heading.tsx │ └── index.ts ├── IconButton │ ├── IconButton.stories.tsx │ ├── IconButton.themes.ts │ ├── IconButton.tsx │ └── index.ts ├── Image │ ├── Image.stories.tsx │ ├── Image.tsx │ └── index.ts ├── Input │ ├── Input.stories.tsx │ ├── Input.themes.ts │ ├── Input.tsx │ └── index.ts ├── Label │ ├── Label.stories.tsx │ ├── Label.tsx │ └── index.ts ├── Link │ ├── Link.stories.tsx │ ├── Link.themes.ts │ ├── Link.tsx │ └── index.ts ├── List │ ├── List.stories.tsx │ ├── List.themes.ts │ ├── List.tsx │ └── index.ts ├── Navigation │ ├── Navigation.stories.tsx │ ├── Navigation.themes.ts │ ├── Navigation.tsx │ ├── NavigationItem.stories.tsx │ └── index.ts ├── NavigationMenu │ ├── NavigationMenu.stories.tsx │ ├── NavigationMenu.tsx │ └── index.ts ├── NavigationTree │ ├── NavigationTree.stories.tsx │ ├── NavigationTreeContainer.tsx │ ├── NavigationTreeDrawer.tsx │ ├── NavigationTreeItem.stories.tsx │ ├── NavigationTreeItem.tsx │ └── index.tsx ├── Overlay.tsx ├── Panel.tsx ├── Paragraph.tsx ├── Popover │ ├── Popover.stories.tsx │ ├── Popover.tsx │ └── index.ts ├── Radio │ ├── Radio.stories.tsx │ ├── Radio.themes.ts │ ├── Radio.tsx │ └── index.ts ├── RadioAccordion │ ├── RadioAccordion.stories.tsx │ ├── RadioAccordion.tsx │ └── index.tsx ├── Select │ ├── Select.stories.tsx │ ├── Select.themes.ts │ ├── Select.tsx │ └── index.ts ├── SidePanel │ ├── SidePanel.stories.tsx │ ├── SidePanel.themes.ts │ ├── SidePanel.tsx │ └── index.ts ├── Skeleton │ ├── Skeleton.stories.tsx │ ├── Skeleton.themes.ts │ ├── Skeleton.tsx │ └── index.ts ├── Switch │ ├── Switch.stories.tsx │ ├── Switch.themes.ts │ ├── Switch.tsx │ └── index.tsx ├── Table │ ├── Table.stories.tsx │ ├── Table.themes.ts │ ├── Table.tsx │ └── index.ts ├── Tabs │ ├── Tabs.stories.tsx │ ├── Tabs.tsx │ └── index.ts ├── Text │ ├── Text.stories.tsx │ ├── Text.themes.ts │ ├── Text.tsx │ └── index.ts ├── TextField │ ├── TextField.stories.tsx │ ├── TextField.tsx │ └── index.ts ├── Textarea │ ├── Textarea.stories.tsx │ ├── Textarea.themes.ts │ ├── Textarea.tsx │ └── index.tsx ├── Tooltip │ ├── Tooltip.stories.tsx │ ├── Tooltip.themes.ts │ ├── Tooltip.tsx │ └── index.ts └── VisuallyHidden │ ├── VisuallyHidden.stories.tsx │ ├── VisuallyHidden.tsx │ └── index.tsx ├── eslint.config.js ├── index.ts ├── jest.config.cjs ├── jest.setup.js ├── package.json ├── patches └── @stitches+react+1.2.8.patch ├── rollup.config.js ├── stitches.config.ts ├── stories ├── Introduction.mdx ├── colors.stories.tsx └── examples │ ├── dashboard.stories.tsx │ └── form.stories.tsx ├── tsconfig-rollup.json ├── tsconfig.jest.json ├── tsconfig.json ├── types └── index.d.ts ├── utils ├── getPrimaryColorInfo.ts ├── ignoreArgType.ts └── modifyVariantsForStory.ts ├── vite.config.ts └── yarn.lock /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve. 3 | body: 4 | - type: checkboxes 5 | id: terms 6 | attributes: 7 | label: Welcome! 8 | description: | 9 | The issue tracker is for reporting bugs and feature requests only. 10 | 11 | All new/updated issues are triaged regularly by the maintainers. 12 | All issues closed by a bot are subsequently double-checked by the maintainers. 13 | 14 | DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS. 15 | 16 | options: 17 | - label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/faency/issues) and didn't find any. 18 | required: true 19 | 20 | - type: textarea 21 | attributes: 22 | label: What did you do? 23 | description: | 24 | How to write a good bug report? 25 | 26 | - Respect the issue template as much as possible. 27 | - The title should be short and descriptive. 28 | - Explain the conditions which led you to report this issue: the context. 29 | - The context should lead to something, an idea or a problem that you’re facing. 30 | - Remain clear and concise. 31 | - Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) 32 | placeholder: What did you do? 33 | validations: 34 | required: true 35 | 36 | - type: textarea 37 | attributes: 38 | label: What were you expecting? 39 | placeholder: What were you expecting? 40 | validations: 41 | required: true 42 | 43 | - type: textarea 44 | attributes: 45 | label: What version are you using? 46 | description: | 47 | `latest` is not considered as a valid version. 48 | placeholder: Paste your output here. 49 | validations: 50 | required: true 51 | 52 | - type: textarea 53 | attributes: 54 | label: What is your environment & configuration? 55 | description: arguments, toml, provider, platform, ... 56 | placeholder: Add information here. 57 | value: | 58 | ```yaml 59 | # (paste your configuration here) 60 | ``` 61 | 62 | Add more configuration information here. 63 | validations: 64 | required: true 65 | 66 | - type: textarea 67 | attributes: 68 | label: If applicable, please paste the log output in DEBUG level 69 | description: "`--log.level=DEBUG` switch." 70 | placeholder: Paste your output here. 71 | validations: 72 | required: false 73 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Traefik Community Support 4 | url: https://community.traefik.io/ 5 | about: If you have a question, or are looking for advice, please post on our Discuss forum! The community loves to chime in to help. Happy Coding! 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for this project. 3 | body: 4 | - type: checkboxes 5 | id: terms 6 | attributes: 7 | label: Welcome! 8 | description: | 9 | The issue tracker is for reporting bugs and feature requests only. 10 | 11 | DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS. 12 | options: 13 | - label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/faency/issues) and didn't find any. 14 | required: true 15 | 16 | - type: textarea 17 | attributes: 18 | label: What did you expect to see? 19 | description: | 20 | How to write a good issue? 21 | 22 | - Respect the issue template as much as possible. 23 | - The title should be short and descriptive. 24 | - Explain the conditions which led you to report this issue: the context. 25 | - The context should lead to something, an idea or a problem that you’re facing. 26 | - Remain clear and concise. 27 | - Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) 28 | placeholder: What did you expect to see? 29 | validations: 30 | required: true 31 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## Description 8 | 9 | 13 | 14 | ## Preview 15 | 16 | 19 | 20 | ## Dependency changes 21 | 22 | 26 | 27 | | Dependency | Version | State | 28 | | ---------- | -------------- | ------- | 29 | | lib1 | 0.1.1 | Added | 30 | | lib2 | 0.0.1 -> 1.0.1 | Updated | 31 | | lib3 | 2.0.1 | Removed | 32 | 33 | - lib1 is responsible for... 34 | 35 | ## Breaking changes 36 | 37 | 41 | 42 | ## How to test? 43 | 44 | 47 | 48 | 1. Go to the page ... 49 | 2. Click on ... 50 | 51 | ## Good PR checkboxes 52 | 53 | - [ ] Change has been tested 54 | - [ ] Added/Updated tests 55 | - [ ] Added/Updated stories 56 | - [ ] PR follows [conventions](https://github.com/traefik/faency#how-to-contribute) 57 | - [ ] Labels are set 58 | - [ ] Project is linked 59 | 60 | ## Good Review checkboxes 61 | 62 |
63 | ℹ️ Copy the snippet and paste in the review field to fill it 64 | 65 | ```markdown 66 | - [ ] I've tested the changes 67 | - [ ] I've agreed on the unit tests (soon to come) 68 | - [ ] I've checked the stories 69 | - [ ] I've read the code and understood it 70 | - [ ] I don't have any more questions 71 | - [ ] I've described any optional improvements 72 | - [ ] I checked PR follows [conventions](https://github.com/traefik/faency#how-to-contribute) 73 | ``` 74 | 75 |
76 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: Main 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | permissions: 9 | contents: read 10 | pages: write 11 | id-token: write 12 | 13 | jobs: 14 | faency: 15 | name: Deploy 16 | if: github.repository == 'traefik/faency' 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v4 22 | 23 | - name: enable corepack 24 | run: corepack enable 25 | 26 | - name: Setup node 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version-file: .nvmrc 30 | cache: yarn 31 | 32 | - name: Config git 33 | run: | 34 | git config --local user.email "30906710+traefiker@users.noreply.github.com" 35 | git config --local user.name "traefiker" 36 | 37 | - name: Deploy 38 | uses: bitovi/github-actions-storybook-to-github-pages@v1.0.3 39 | env: 40 | NODE_ENV: production 41 | with: 42 | install_command: yarn workspaces focus --all --production && yarn patch-package 43 | build_command: yarn build-storybook 44 | path: storybook-static 45 | checkout: false 46 | -------------------------------------------------------------------------------- /.github/workflows/pr.yaml: -------------------------------------------------------------------------------- 1 | name: Pull Request Lint 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | faency: 8 | name: Test, lint and build 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v4 14 | 15 | - name: enable corepack 16 | run: corepack enable 17 | 18 | - name: Setup node 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version-file: .nvmrc 22 | cache: yarn 23 | 24 | - name: Install 25 | run: yarn install 26 | 27 | - name: Patch 28 | run: yarn patch-package 29 | 30 | - name: Lint 31 | run: yarn lint 32 | 33 | - name: Test 34 | run: yarn test:ci 35 | 36 | - name: Build 37 | run: yarn build 38 | -------------------------------------------------------------------------------- /.github/workflows/pr_title.yaml: -------------------------------------------------------------------------------- 1 | name: Pull Request Title 2 | 3 | on: 4 | pull_request_target: 5 | types: ['opened', 'reopened', 'edited', 'synchronize'] 6 | 7 | permissions: 8 | pull-requests: read 9 | 10 | jobs: 11 | faency: 12 | name: Lint PR Title 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Semantic pull request 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | uses: amannn/action-semantic-pull-request@v5 20 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | faency: 10 | name: Release 11 | if: github.repository == 'traefik/faency' 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: enable corepack 18 | run: corepack enable 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version-file: .nvmrc 24 | cache: yarn 25 | 26 | - name: Install dependencies 27 | run: yarn workspaces focus --all --production 28 | 29 | - name: Patch 30 | run: yarn patch-package 31 | 32 | - name: Build 33 | run: yarn build 34 | 35 | - name: Release 36 | run: yarn semantic-release 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 39 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # https://github.com/hashicorp/next-mdx-enhanced#installation 38 | .mdx-data 39 | 40 | /dist 41 | 42 | /storybook-static 43 | 44 | .parcel-cache 45 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn pre-commit 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | out 4 | .mdx-data 5 | .parcel-cache 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true, 4 | "printWidth": 100 5 | } 6 | -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/react-vite'; 2 | 3 | const config: StorybookConfig = { 4 | stories: [ 5 | '../stories/**/*.mdx', 6 | '../stories/**/*.stories.@(js|jsx|ts|tsx)', 7 | '../components/**/*.stories.@(js|jsx|ts|tsx)', 8 | ], 9 | 10 | addons: [ 11 | '@storybook/addon-links', 12 | { 13 | name: '@storybook/addon-essentials', 14 | options: { 15 | backgrounds: false, 16 | }, 17 | }, 18 | 'storybook-dark-mode', 19 | ], 20 | 21 | core: { 22 | builder: '@storybook/builder-vite', 23 | }, 24 | 25 | framework: { 26 | name: '@storybook/react-vite', 27 | options: {}, 28 | }, 29 | 30 | typescript: { 31 | reactDocgen: 'react-docgen-typescript', 32 | }, 33 | }; 34 | 35 | export default config; 36 | -------------------------------------------------------------------------------- /.storybook/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.storybook/preview.jsx: -------------------------------------------------------------------------------- 1 | import { DocsContainer } from '@storybook/addon-docs'; 2 | import { addons } from '@storybook/preview-api'; 3 | import { themes } from '@storybook/theming'; 4 | import React from 'react'; 5 | import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode'; 6 | 7 | import { globalCss } from '../'; 8 | import { FaencyProvider } from '../components/FaencyProvider'; 9 | import { darkTheme, lightTheme } from '../stitches.config'; 10 | 11 | const channel = addons.getChannel(); 12 | 13 | export const parameters = { 14 | controls: { 15 | matchers: { 16 | color: /(background|color)$/i, 17 | date: /Date$/, 18 | }, 19 | }, 20 | darkMode: { 21 | stylePreview: true, 22 | }, 23 | docs: { 24 | container: (context) => { 25 | // eslint-disable-next-line react-hooks/rules-of-hooks 26 | const [isDark, setDark] = React.useState(); 27 | 28 | // eslint-disable-next-line react-hooks/rules-of-hooks 29 | React.useEffect(() => { 30 | channel.on(DARK_MODE_EVENT_NAME, setDark); 31 | return () => channel.removeListener(DARK_MODE_EVENT_NAME, setDark); 32 | }, [setDark]); 33 | 34 | const props = { 35 | ...context, 36 | theme: isDark ? themes.dark : themes.light, 37 | }; 38 | 39 | return React.createElement(DocsContainer, props); 40 | }, 41 | }, 42 | }; 43 | 44 | const globalStyle = globalCss({ 45 | body: { 46 | bc: '$contentBg', 47 | }, 48 | }); 49 | 50 | export const decorators = [ 51 | (renderStory) => { 52 | React.useEffect(() => { 53 | darkTheme('neon').toString(); 54 | lightTheme('neon').toString(); 55 | }, []); 56 | 57 | return ( 58 | 59 | {globalStyle()} 60 | {renderStory()} 61 | 62 | ); 63 | }, 64 | ]; 65 | 66 | export const tags = ['autodocs']; 67 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "cSpell.words": ["faency"], 4 | "typescript.tsdk": "node_modules/typescript/lib", 5 | "editor.tabSize": 2 6 | } 7 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['@babel/plugin-transform-react-pure-annotations'], 3 | presets: [ 4 | [ 5 | '@babel/preset-env', 6 | { 7 | targets: { 8 | node: 'current', 9 | }, 10 | }, 11 | ], 12 | [ 13 | '@babel/preset-react', 14 | { 15 | runtime: 'automatic', 16 | }, 17 | ], 18 | '@babel/preset-typescript', 19 | ], 20 | }; 21 | -------------------------------------------------------------------------------- /colors/deepBlue.ts: -------------------------------------------------------------------------------- 1 | export const deepBlue = { 2 | deepBlue1: 'hsl(240, 14.0%, 99.0%)', 3 | deepBlue2: 'hsl(210, 90.0%, 96.1%)', 4 | deepBlue3: 'hsl(210, 67.8%, 94.6%)', 5 | deepBlue4: 'hsl(210, 49.1%, 92.2%)', 6 | deepBlue5: 'hsl(210, 34.8%, 88.1%)', 7 | deepBlue6: 'hsl(209, 24.6%, 81.0%)', 8 | deepBlue7: 'hsl(209, 17.6%, 67.9%)', 9 | deepBlue8: 'hsl(208, 16.0%, 36.0%)', 10 | deepBlue9: 'hsl(208, 24.0%, 27.0%)', 11 | deepBlue10: 'hsl(208, 40.0%, 18.0%)', 12 | deepBlue11: 'hsl(209, 88.0%, 9.0%)', 13 | deepBlue12: 'hsl(208, 89.0%, 7.0%)', 14 | }; 15 | 16 | export const deepBlueA = { 17 | deepBlueA1: 'hsl(200, 94.9%, 38.7%, 0.016)', 18 | deepBlueA2: 'hsl(210, 98.6%, 47.7%, 0.075)', 19 | deepBlueA3: 'hsl(212, 97.9%, 41.8%, 0.091)', 20 | deepBlueA4: 'hsl(210, 99.1%, 33.5%, 0.118)', 21 | deepBlueA5: 'hsl(209, 99.5%, 25.7%, 0.161)', 22 | deepBlueA6: 'hsl(209, 98.5%, 19.4%, 0.236)', 23 | deepBlueA7: 'hsl(209, 99.1%, 15.2%, 0.377)', 24 | deepBlueA8: 'hsl(207 99.7% 8.3% / 0.682)', 25 | deepBlueA9: 'hsl(207, 98.6%, 8.2%, 0.797)', 26 | deepBlueA10: 'hsl(208, 98.9%, 8.0%, 0.891)', 27 | deepBlueA11: 'hsl(210, 100%, 7.6%, 0.980)', 28 | deepBlueA12: 'hsl(211, 100%, 5.8%, 0.980)', 29 | }; 30 | 31 | export const deepBlueDark = { 32 | deepBlue1: 'hsl(208, 53.0%, 4.0%)', 33 | deepBlue2: 'hsl(209, 88.0%, 7.0%)', 34 | deepBlue3: 'hsl(209, 88.0%, 9.0%)', 35 | deepBlue4: 'hsl(209, 45.6%, 16.0%)', 36 | deepBlue5: 'hsl(209, 44.5%, 17.7%)', 37 | deepBlue6: 'hsl(208, 43.4%, 19.6%)', 38 | deepBlue7: 'hsl(208, 42.2%, 22.6%)', 39 | deepBlue8: 'hsl(208, 40.5%, 29.0%)', 40 | deepBlue9: 'hsl(208, 40.0%, 58.0%)', 41 | deepBlue10: 'hsl(209, 40.0%, 68.0%)', 42 | deepBlue11: 'hsl(209, 40.0%, 78.0%)', 43 | deepBlue12: 'hsl(209, 60.0%, 94.0%)', 44 | }; 45 | 46 | export const deepBlueDarkA = { 47 | deepBlueA1: 'hsl(0, 0%, 0%, 0)', 48 | deepBlueA2: 'hsl(216 100% 49.6% / 0.076)', 49 | deepBlueA3: 'hsl(210 100% 50% / 0.113)', 50 | deepBlueA4: 'hsl(210, 99.9%, 69.5%, 0.180)', 51 | deepBlueA5: 'hsl(209, 98.5%, 69.8%, 0.206)', 52 | deepBlueA6: 'hsl(209, 99.1%, 70.0%, 0.235)', 53 | deepBlueA7: 'hsl(208, 99.0%, 70.7%, 0.277)', 54 | deepBlueA8: 'hsl(208, 99.3%, 71.6%, 0.369)', 55 | deepBlueA9: 'hsl(208, 99.6%, 77.7%, 0.733)', 56 | deepBlueA10: 'hsl(210, 100%, 84.5%, 0.795)', 57 | deepBlueA11: 'hsl(208, 99.7%, 90.0%, 0.858)', 58 | deepBlueA12: 'hsl(210, 99.7%, 96.4%, 0.975)', 59 | }; 60 | -------------------------------------------------------------------------------- /colors/elevation.ts: -------------------------------------------------------------------------------- 1 | export const elevation = { 2 | '00dp': 'hsl(240, 4%, 95%)', 3 | '01dp': 'hsl(0, 0%, 99%)', 4 | '02dp': 'hsl(0, 0%, 99%)', 5 | '03dp': 'hsl(0, 0%, 99%)', 6 | '04dp': 'hsl(0, 0%, 99%)', 7 | '05dp': 'hsl(0, 0%, 99%)', 8 | }; 9 | 10 | export const elevationDark = { 11 | '00dp': 'hsl(210, 68%, 9%)', 12 | '01dp': 'hsl(208, 37%, 15%)', 13 | '02dp': 'hsl(207, 32%, 17%)', 14 | '03dp': 'hsl(209, 28%, 19%)', 15 | '04dp': 'hsl(209, 23%, 21%)', 16 | '05dp': 'hsl(209, 21%, 23%)', 17 | }; 18 | -------------------------------------------------------------------------------- /colors/grayBlue.ts: -------------------------------------------------------------------------------- 1 | export const grayBlue = { 2 | grayBlue1: 'hsl(208 9.0% 99.0%)', 3 | grayBlue2: 'hsl(210 10.0% 96.1%)', 4 | grayBlue3: 'hsl(210 9.9% 95.7%)', 5 | grayBlue4: 'hsl(210 9.8% 94.9%)', 6 | grayBlue5: 'hsl(210 9.7% 93.1%)', 7 | grayBlue6: 'hsl(210 9.6% 89.8%)', 8 | grayBlue7: 'hsl(210 9.4% 83.2%)', 9 | grayBlue8: 'hsl(208, 9.0%, 80.0%)', 10 | grayBlue9: 'hsl(208, 9.0%, 73.0%)', 11 | grayBlue10: 'hsl(208 9.0% 53.0%)', 12 | grayBlue11: 'hsl(208 9.0% 33.0%)', 13 | grayBlue12: 'hsl(208 9.0% 13.0%)', 14 | }; 15 | 16 | export const grayBlueA = { 17 | grayBlueA1: 'hsl(240 89.3% 18.3% / 0.012)', 18 | grayBlueA2: 'hsl(182 89% 7.1% / 0.032)', 19 | grayBlueA3: 'hsl(210 80.6% 10.1% / 0.048)', 20 | grayBlueA4: 'hsl(210 97.6% 7.3% / 0.055)', 21 | grayBlueA5: 'hsl(220 92.3% 8.5% / 0.075)', 22 | grayBlueA6: 'hsl(204 97.3% 8.8% / 0.114)', 23 | grayBlueA7: 'hsl(210 95.8% 8.9% / 0.185)', 24 | grayBlueA8: 'hsl(207 96.4% 8.9% / 0.211)', 25 | grayBlueA9: 'hsl(205 96.4% 8.3% / 0.295)', 26 | grayBlueA10: 'hsl(207 99.4% 8.4% / 0.514)', 27 | grayBlueA11: 'hsl(208 96.8% 4.3% / 0.699)', 28 | grayBlueA12: 'hsl(210 94.8% 1.4% / 0.883)', 29 | }; 30 | 31 | export const grayBlueDark = { 32 | grayBlue1: 'hsl(209 40.0% 2.0%)', 33 | grayBlue2: 'hsl(210 40.0% 3.9%)', 34 | grayBlue3: 'hsl(209 38.1% 4.8%)', 35 | grayBlue4: 'hsl(208 35.4% 5.8%)', 36 | grayBlue5: 'hsl(208 32.4% 6.9%)', 37 | grayBlue6: 'hsl(209 31.0% 8.2%)', 38 | grayBlue7: 'hsl(209, 38.0%, 12.0%)', 39 | grayBlue8: 'hsl(209, 38.0%, 25.0%)', 40 | grayBlue9: 'hsl(208, 11.0%, 45.0%)', 41 | grayBlue10: 'hsl(208 11.0% 68.0%)', 42 | grayBlue11: 'hsl(208 11.0% 78.0%)', 43 | grayBlue12: 'hsl(208 11.0% 88.0%)', 44 | }; 45 | 46 | export const grayBlueDarkA = { 47 | grayBlueA1: 'hsl(0 0% 0% / 0)', 48 | grayBlueA2: 'hsl(210 91.5% 69.6% / 0.029)', 49 | grayBlueA3: 'hsl(216 93.9% 73.7% / 0.041)', 50 | grayBlueA4: 'hsl(210 95.6% 76.0% / 0.053)', 51 | grayBlueA5: 'hsl(206 96.8% 77.4% / 0.065)', 52 | grayBlueA6: 'hsl(207 98.1% 77% / 0.081)', 53 | grayBlueA7: 'hsl(208 97.8% 72.4% / 0.142)', 54 | grayBlueA8: 'hsl(209 99.6% 72.7% / 0.327)', 55 | grayBlueA9: 'hsl(206 99.7% 90.7% / 0.484)', 56 | grayBlueA10: 'hsl(206 99.0% 95.3% / 0.706)', 57 | grayBlueA11: 'hsl(210 97.4% 97.2% / 0.799)', 58 | grayBlueA12: 'hsl(205 93.8% 98.5% / 0.892)', 59 | }; 60 | -------------------------------------------------------------------------------- /colors/index.ts: -------------------------------------------------------------------------------- 1 | import * as elevation from './elevation'; 2 | import * as deepBlue from './deepBlue'; 3 | import * as grayBlue from './grayBlue'; 4 | import * as neon from './neon'; 5 | import * as orange from './orange'; 6 | import * as red from './red'; 7 | 8 | import { 9 | blackA, 10 | blue, 11 | blueA, 12 | blueDark, 13 | blueDarkA, 14 | gray, 15 | grayA, 16 | grayDark, 17 | grayDarkA, 18 | green, 19 | greenA, 20 | greenDark, 21 | greenDarkA, 22 | purple, 23 | purpleA, 24 | purpleDark, 25 | purpleDarkA, 26 | slate, 27 | slateA, 28 | slateDark, 29 | slateDarkA, 30 | whiteA, 31 | } from '@radix-ui/colors'; 32 | 33 | const customColors = { 34 | ...elevation, 35 | ...deepBlue, 36 | ...grayBlue, 37 | ...neon, 38 | ...orange, 39 | ...red, 40 | }; 41 | 42 | export type ColorMap = { 43 | [key: string]: string; 44 | }; 45 | 46 | export const lightColors: ColorMap = Object.entries(customColors) 47 | .filter(([colorName]) => !colorName.includes('Dark')) 48 | .reduce( 49 | (acc, [_, colors]) => ({ 50 | ...acc, 51 | ...colors, 52 | }), 53 | { 54 | // radix colors 55 | ...gray, 56 | ...grayA, 57 | ...blue, 58 | ...blueA, 59 | ...green, 60 | ...greenA, 61 | ...slate, 62 | ...slateA, 63 | ...purple, 64 | ...purpleA, 65 | ...whiteA, 66 | ...blackA, 67 | } 68 | ); 69 | 70 | export const darkColors: ColorMap = Object.entries(customColors) 71 | .filter(([colorName]) => colorName.includes('Dark')) 72 | .reduce( 73 | (acc, [_, colors]) => ({ 74 | ...acc, 75 | ...colors, 76 | }), 77 | { 78 | // radix colors 79 | ...grayDark, 80 | ...grayDarkA, 81 | ...blueDark, 82 | ...blueDarkA, 83 | ...greenDark, 84 | ...greenDarkA, 85 | ...slateDark, 86 | ...slateDarkA, 87 | ...purpleDark, 88 | ...purpleDarkA, 89 | ...whiteA, 90 | ...blackA, 91 | } 92 | ); 93 | 94 | export * as elevation from './elevation'; 95 | export * as deepBlue from './deepBlue'; 96 | export * as grayBlue from './grayBlue'; 97 | export * as neon from './neon'; 98 | export * as orange from './orange'; 99 | export * as red from './red'; 100 | 101 | export default customColors; 102 | -------------------------------------------------------------------------------- /colors/neon.ts: -------------------------------------------------------------------------------- 1 | export const neon = { 2 | neon1: 'hsl(68, 60.0%, 99.0%)', 3 | neon2: 'hsl(67, 90.0%, 96.1%)', 4 | neon3: 'hsl(67, 86.3%, 94.4%)', 5 | neon4: 'hsl(67, 82.5%, 90.5%)', 6 | neon5: 'hsl(67, 79.8%, 82.3%)', 7 | neon6: 'hsl(68, 78.5%, 69.8%)', 8 | neon7: 'hsl(68, 78.5%, 60.6%)', 9 | neon8: 'hsl(68, 53.0%, 48.0%)', 10 | neon9: 'hsl(68, 53.0%, 36.0%)', 11 | neon10: 'hsl(68, 53.0%, 24.0%)', 12 | neon11: 'hsl(68, 53.0%, 18.0%)', 13 | neon12: 'hsl(68, 53.0%, 10.0%)', 14 | }; 15 | 16 | export const neonA = { 17 | neonA1: 'hsl(60, 94.9%, 38.7%, 0.016)', 18 | neonA2: 'hsl(67, 98.6%, 47.7%, 0.075)', 19 | neonA3: 'hsl(67, 99.8%, 46.4%, 0.106)', 20 | neonA4: 'hsl(68, 99.4%, 45.6%, 0.173)', 21 | neonA5: 'hsl(68, 99.8%, 44.5%, 0.318)', 22 | neonA6: 'hsl(68, 99.7%, 43.9%, 0.538)', 23 | neonA7: 'hsl(68, 100%, 43.9%, 0.702)', 24 | neonA8: 'hsl(68, 99.7%, 33.3%, 0.762)', 25 | neonA9: 'hsl(68, 99.7%, 22.9%, 0.832)', 26 | neonA10: 'hsl(68, 99.4%, 14.5%, 0.887)', 27 | neonA11: 'hsl(68, 99.7%, 10.3%, 0.914)', 28 | neonA12: 'hsl(69, 99.9%, 5.6%, 0.953)', 29 | }; 30 | 31 | export const neonDark = { 32 | neon1: 'hsl(68, 53.0%, 4.0%)', 33 | neon2: 'hsl(68, 53.3%, 5.9%)', 34 | neon3: 'hsl(72, 47.3%, 10.0%)', 35 | neon4: 'hsl(73, 52.0%, 13.2%)', 36 | neon5: 'hsl(73, 58.3%, 16.3%)', 37 | neon6: 'hsl(72, 65.6%, 20.0%)', 38 | neon7: 'hsl(70, 76.7%, 24.7%)', 39 | neon8: 'hsl(68, 100%, 29.0%)', 40 | neon9: 'hsl(68, 79.0%, 60.0%)', 41 | neon10: 'hsl(68, 79.0%, 68.0%)', 42 | neon11: 'hsl(68, 79.0%, 78.0%)', 43 | neon12: 'hsl(68, 79.0%, 88.0%)', 44 | }; 45 | 46 | export const neonDarkA = { 47 | neonA1: 'hsl(0, 0%, 0%, 0)', 48 | neonA2: 'hsl(61, 94.0%, 62.9%, 0.030)', 49 | neonA3: 'hsl(73, 97.1%, 67.4%, 0.093)', 50 | neonA4: 'hsl(72, 99.0%, 65.5%, 0.147)', 51 | neonA5: 'hsl(74, 99.1%, 62.0%, 0.210)', 52 | neonA6: 'hsl(72, 99.6%, 59.8%, 0.285)', 53 | neonA7: 'hsl(70, 99.7%, 55.8%, 0.398)', 54 | neonA8: 'hsl(68, 100%, 49.9%, 0.553)', 55 | neonA9: 'hsl(68, 99.7%, 65.3%, 0.913)', 56 | neonA10: 'hsl(68, 100%, 72.9%, 0.929)', 57 | neonA11: 'hsl(68, 99.9%, 81.9%, 0.950)', 58 | neonA12: 'hsl(69, 99.9%, 90.2%, 0.975)', 59 | }; 60 | -------------------------------------------------------------------------------- /colors/orange.ts: -------------------------------------------------------------------------------- 1 | export const orange = { 2 | orange1: 'hsl(24 70.0% 99.0%)', 3 | orange2: 'hsl(27 81.8% 97.8%)', 4 | orange3: 'hsl(27 98.5% 95.4%)', 5 | orange4: 'hsl(27 100% 92.2%)', 6 | orange5: 'hsl(27 100% 88.1%)', 7 | orange6: 'hsl(26 100% 82.5%)', 8 | orange7: 'hsl(25 99.2% 74.8%)', 9 | orange8: 'hsl(24 94.6% 63.9%)', 10 | orange9: 'hsl(24 94.0% 50.0%)', 11 | orange10: 'hsl(24 100% 46.0%)', 12 | orange11: 'hsl(24 100% 37.0%)', 13 | orange12: 'hsl(24 60.0% 17.0%)', 14 | }; 15 | 16 | export const orangeA = { 17 | orangeA1: 'hsl(20 94.9% 38.7% / 0.016)', 18 | orangeA2: 'hsl(27 95.7% 46.1% / 0.040)', 19 | orangeA3: 'hsl(26 100% 50.4% / 0.091)', 20 | orangeA4: 'hsl(27 100% 50.0% / 0.157)', 21 | orangeA5: 'hsl(28 100% 50.2% / 0.240)', 22 | orangeA6: 'hsl(26 100% 50.1% / 0.350)', 23 | orangeA7: 'hsl(25 100% 49.6% / 0.502)', 24 | orangeA8: 'hsl(24 100% 48.6% / 0.702)', 25 | orangeA9: 'hsl(24 99.9% 48.4% / 0.969)', 26 | orangeA10: 'hsl(23 100% 46.4% / 0.980)', 27 | orangeA11: 'hsl(23 100% 36.8% / 0.980)', 28 | orangeA12: 'hsl(15 99.4% 11.0% / 0.934)', 29 | }; 30 | 31 | export const orangeDark = { 32 | orange1: 'hsl(35 100% 7.2%)', 33 | orange2: 'hsl(35 100% 8.0%)', 34 | orange3: 'hsl(34 87.5% 11.4%)', 35 | orange4: 'hsl(33 84.1% 14.0%)', 36 | orange5: 'hsl(33 83.6% 16.6%)', 37 | orange6: 'hsl(33 85.7% 19.8%)', 38 | orange7: 'hsl(34 91.5% 23.9%)', 39 | orange8: 'hsl(35 100% 29.0%)', 40 | orange9: 'hsl(24 94.0% 50.0%)', 41 | orange10: 'hsl(35, 100%, 50.0%)', 42 | orange11: 'hsl(35 100% 62.2%)', 43 | orange12: 'hsl(24 97.0% 93.2%)', 44 | }; 45 | 46 | export const orangeDarkA = { 47 | orangeA1: 'hsl(0 0% 100% / 0)', 48 | orangeA2: 'hsl(30 100% 50.2% / 0.064)', 49 | orangeA3: 'hsl(33 100% 50.2% / 0.116)', 50 | orangeA4: 'hsl(33 100% 50.2% / 0.176)', 51 | orangeA5: 'hsl(33 99.4% 14.4% / 0.973)', 52 | orangeA6: 'hsl(33 99.5% 17.6% / 0.973)', 53 | orangeA7: 'hsl(34 100% 22.4% / 0.98)', 54 | orangeA8: 'hsl(34 100% 28.6% / 0.98)', 55 | orangeA9: 'hsl(24 99.8% 48.4% / 0.969)', 56 | orangeA10: 'hsl(35 100% 50% / 0.98)', 57 | orangeA11: 'hsl(36 100% 62.4% / 0.98)', 58 | orangeA12: 'hsl(26 100% 94.2% / 0.98)', 59 | }; 60 | -------------------------------------------------------------------------------- /colors/red.ts: -------------------------------------------------------------------------------- 1 | export const red = { 2 | red1: 'hsl(347 100% 99.0%)', 3 | red2: 'hsl(348 100% 99.0%)', 4 | red3: 'hsl(348 99.7% 97.2%)', 5 | red4: 'hsl(348 92.2% 94.5%)', 6 | red5: 'hsl(348 83.3% 90.1%)', 7 | red6: 'hsl(348 75.7% 84.0%)', 8 | red7: 'hsl(347 71.9% 78.7%)', 9 | red8: 'hsl(347 100% 67.0%)', 10 | red9: 'hsl(347, 100%, 60.0%)', 11 | red10: 'hsl(347 77.4% 52.4%)', 12 | red11: 'hsl(347, 80.0%, 40.0%)', 13 | red12: 'hsl(347 60.0% 15.0%)', 14 | }; 15 | 16 | export const redA = { 17 | redA1: 'hsl(348 100% 51.0% / 0.020)', 18 | redA2: 'hsl(348 100% 51.0% / 0.020)', 19 | redA3: 'hsl(347 100% 50.1% / 0.055)', 20 | redA4: 'hsl(348 99.8% 48.2% / 0.106)', 21 | redA5: 'hsl(349 99.3% 45.8% / 0.181)', 22 | redA6: 'hsl(347 99.5% 43.2% / 0.283)', 23 | redA7: 'hsl(347 99.8% 42.0% / 0.365)', 24 | redA8: 'hsl(347 100% 50% / 0.641)', 25 | redA9: 'hsl(347 100% 50.0% / 0.800)', 26 | redA10: 'hsl(347 99.8% 43.8% / 0.844)', 27 | redA11: 'hsl(347 99.9% 34.9% / 0.922)', 28 | redA12: 'hsl(347 99.1% 9.7% / 0.942)', 29 | }; 30 | 31 | export const redDark = { 32 | red1: 'hsl(347 23.0% 10.0%)', 33 | red2: 'hsl(346 34.4% 12.0%)', 34 | red3: 'hsl(345 42.6% 16.5%)', 35 | red4: 'hsl(345 46.5% 19.3%)', 36 | red5: 'hsl(346 49.9% 22.1%)', 37 | red6: 'hsl(346 53.9% 26.2%)', 38 | red7: 'hsl(346 59.0% 32.3%)', 39 | red8: 'hsl(347 64.8% 41.2%)', 40 | red9: 'hsl(347, 100%, 60.0%)', 41 | red10: 'hsl(347, 100%, 67.0%)', 42 | red11: 'hsl(347 100% 72.0%)', 43 | red12: 'hsl(347 100% 98.0%)', 44 | }; 45 | 46 | export const redDarkA = { 47 | redA1: 'hsl(0 0% 0% / 0)', 48 | redA2: 'hsl(342 98.5% 53.6% / 0.045)', 49 | redA3: 'hsl(344 99.1% 59.8% / 0.130)', 50 | redA4: 'hsl(343 98.8% 60.1% / 0.184)', 51 | redA5: 'hsl(346 99.6% 60.5% / 0.237)', 52 | redA6: 'hsl(345 99.6% 60.5% / 0.322)', 53 | redA7: 'hsl(346 99.7% 60.0% / 0.447)', 54 | redA8: 'hsl(347 100% 59.2% / 0.634)', 55 | redA9: 'hsl(347 100% 60.1% / 0.980)', 56 | redA10: 'hsl(347 100% 67.3% / 0.980)', 57 | redA11: 'hsl(347 100% 72.3% / 0.980)', 58 | redA12: 'hsl(338 100% 98.9% / 0.980)', 59 | }; 60 | -------------------------------------------------------------------------------- /components/AccessibleIcon/AccessibleIcon.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as Icons from '@radix-ui/react-icons'; 2 | import { Meta, StoryFn } from '@storybook/react'; 3 | import React from 'react'; 4 | 5 | import { Flex } from '../Flex'; 6 | import { IconButton } from '../IconButton'; 7 | import { AccessibleIcon } from './AccessibleIcon'; 8 | 9 | const Component: Meta = { 10 | title: 'Components/AccessibleIcon', 11 | component: AccessibleIcon, 12 | tags: ['autodocs'], 13 | }; 14 | 15 | export const Basic: StoryFn = () => ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | 31 | export default Component; 32 | -------------------------------------------------------------------------------- /components/AccessibleIcon/AccessibleIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as AccessibleIconPrimitive from '@radix-ui/react-accessible-icon'; 2 | 3 | export const AccessibleIcon = AccessibleIconPrimitive.Root; 4 | -------------------------------------------------------------------------------- /components/AccessibleIcon/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './AccessibleIcon'; 2 | -------------------------------------------------------------------------------- /components/Accordion/Accordion.themes.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '@stitches/react/types/css'; 2 | import tinycolor from 'tinycolor2'; 3 | 4 | import { ColorInfo } from '../../utils/getPrimaryColorInfo'; 5 | 6 | export namespace Theme { 7 | type Colors = { 8 | accordionBackground: Property.Color; 9 | accordionHoverShadow: Property.Color; 10 | accordionActiveShadow: Property.Color; 11 | accordionText: Property.Color; 12 | accordionLabel: Property.Color; 13 | }; 14 | 15 | type Factory = (primaryColor: ColorInfo) => Colors; 16 | 17 | export const getLight: Factory = () => ({ 18 | accordionBackground: 'white', 19 | accordionHoverShadow: tinycolor('black').setAlpha(0.05).toHslString(), 20 | accordionActiveShadow: tinycolor('black').setAlpha(0.2).toHslString(), 21 | accordionText: tinycolor('black').setAlpha(0.74).toHslString(), 22 | accordionLabel: tinycolor('black').setAlpha(0.51).toHslString(), 23 | }); 24 | 25 | export const getDark: Factory = () => ({ 26 | accordionBackground: '$deepBlue2', 27 | accordionHoverShadow: tinycolor('white').setAlpha(0.05).toHslString(), 28 | accordionActiveShadow: tinycolor('white').setAlpha(0.2).toHslString(), 29 | accordionText: tinycolor('white').setAlpha(0.74).toHslString(), 30 | accordionLabel: tinycolor('white').setAlpha(0.51).toHslString(), 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /components/Accordion/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Accordion'; 2 | -------------------------------------------------------------------------------- /components/Alert/Alert.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory'; 5 | import { H2 } from '../Heading'; 6 | import { Text } from '../Text'; 7 | import { Alert, AlertProps, AlertVariants } from './Alert'; 8 | 9 | const AlertWrapper = (props: AlertProps): JSX.Element => ; 10 | 11 | const AlertForStory = modifyVariantsForStory(AlertWrapper); 12 | 13 | const Component: Meta = { 14 | title: 'Components/Alert', 15 | component: AlertForStory, 16 | }; 17 | 18 | export const Variants: StoryFn = (args) => ( 19 | 20 |

Alert

21 | 22 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut 23 | labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco 24 | laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in 25 | voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat 26 | non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 27 | 28 |
29 | ); 30 | 31 | Variants.args = { 32 | variant: 'info', 33 | }; 34 | 35 | Variants.argTypes = { 36 | variant: { 37 | control: 'inline-radio', 38 | options: ['gray', 'info', 'success', 'warning', 'error'], 39 | }, 40 | }; 41 | 42 | export default Component; 43 | -------------------------------------------------------------------------------- /components/Alert/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { styled, VariantProps } from '../../stitches.config'; 2 | import { Card } from '../Card'; 3 | 4 | export const Alert = styled(Card, { 5 | variants: { 6 | variant: { 7 | gray: { 8 | '&::before': { 9 | boxShadow: 'inset 0 0 0 1px $colors$slate9', 10 | }, 11 | }, 12 | info: { 13 | '&::before': { 14 | boxShadow: 'inset 0 0 0 1px $colors$blue9', 15 | }, 16 | }, 17 | success: { 18 | '&::before': { 19 | boxShadow: 'inset 0 0 0 1px $colors$green9', 20 | }, 21 | }, 22 | warning: { 23 | '&::before': { 24 | boxShadow: 'inset 0 0 0 1px $colors$orange9', 25 | }, 26 | }, 27 | error: { 28 | '&::before': { 29 | boxShadow: 'inset 0 0 0 1px $colors$red9', 30 | }, 31 | }, 32 | }, 33 | }, 34 | defaultVariants: { 35 | variant: 'gray', 36 | }, 37 | }); 38 | 39 | export type AlertVariants = VariantProps; 40 | export type AlertProps = AlertVariants & NonNullable; 41 | -------------------------------------------------------------------------------- /components/Alert/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Alert'; 2 | -------------------------------------------------------------------------------- /components/AriaTable/AriaTable.test.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | import { axe } from 'jest-axe'; 3 | 4 | import { Table, Tbody, Td, Tfoot, Th, Thead, Tr } from './AriaTable'; 5 | 6 | describe('AriaTable', () => { 7 | it('should render table with no a11y issue', async () => { 8 | const { container, getByRole, getAllByRole } = render( 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
Col1
Cell1
FooterCell1
26 | ); 27 | 28 | expect(getByRole('table')).toBeInTheDocument(); 29 | expect(getByRole('columnheader')).toBeInTheDocument(); 30 | expect(getAllByRole('rowgroup')).toHaveLength(3); 31 | expect(getAllByRole('row')).toHaveLength(3); 32 | expect(getAllByRole('cell')).toHaveLength(2); 33 | 34 | const results = await axe(container); 35 | 36 | expect(results).toHaveNoViolations(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /components/AriaTable/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AriaTable'; 2 | -------------------------------------------------------------------------------- /components/Avatar/Avatar.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { Avatar } from './Avatar'; 5 | 6 | const Component: Meta = { 7 | title: 'Components/Avatar', 8 | component: Avatar, 9 | }; 10 | 11 | export const Template: StoryFn = (args) => ; 12 | 13 | Template.args = { 14 | src: 'https://picsum.photos/100', 15 | size: '4', 16 | }; 17 | 18 | Template.argTypes = { 19 | size: { 20 | control: 'inline-radio', 21 | options: ['1', '2', '3', '4', '5', '6'], 22 | }, 23 | }; 24 | 25 | export const Shape: StoryFn = Template.bind({}); 26 | 27 | Shape.args = { 28 | src: 'https://picsum.photos/100', 29 | shape: 'square', 30 | size: '4', 31 | }; 32 | 33 | Shape.argTypes = { 34 | size: { 35 | control: 'inline-radio', 36 | options: ['1', '2', '3', '4', '5', '6'], 37 | }, 38 | shape: { 39 | control: 'inline-radio', 40 | options: ['square', 'circle'], 41 | }, 42 | }; 43 | 44 | export const Fallback: StoryFn = Template.bind({}); 45 | 46 | Fallback.args = { 47 | fallback: 'M', 48 | shape: 'circle', 49 | size: '4', 50 | variant: 'red', 51 | }; 52 | 53 | Fallback.argTypes = { 54 | size: { 55 | control: 'inline-radio', 56 | options: ['1', '2', '3', '4', '5', '6'], 57 | }, 58 | shape: { 59 | control: 'inline-radio', 60 | options: ['square', 'circle'], 61 | }, 62 | variant: { 63 | control: 'inline-radio', 64 | options: ['gray', 'red', 'purple', 'blue', 'green', 'orange'], 65 | }, 66 | fallback: { 67 | control: 'text', 68 | }, 69 | }; 70 | 71 | export const Variants: StoryFn = Template.bind({}); 72 | 73 | Variants.args = { 74 | fallback: 'M', 75 | shape: 'circle', 76 | size: '4', 77 | variant: 'blue', 78 | }; 79 | 80 | Variants.argTypes = { 81 | size: { 82 | control: 'inline-radio', 83 | options: ['1', '2', '3', '4', '5', '6'], 84 | }, 85 | shape: { 86 | control: 'inline-radio', 87 | options: ['square', 'circle'], 88 | }, 89 | variant: { 90 | control: 'inline-radio', 91 | options: ['gray', 'red', 'purple', 'blue', 'green', 'orange'], 92 | }, 93 | }; 94 | 95 | export default Component; 96 | -------------------------------------------------------------------------------- /components/Avatar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Avatar'; 2 | -------------------------------------------------------------------------------- /components/Badge/Badge.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { VariantProps } from '../../stitches.config'; 5 | import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory'; 6 | import { Flex } from '../Flex'; 7 | import { UnstyledLink } from '../Link'; 8 | import { Badge, COLORS } from './Badge'; 9 | 10 | type BadgeVariants = VariantProps; 11 | type BadgeProps = BadgeVariants & NonNullable; 12 | 13 | const BaseBadge = (props: BadgeProps): JSX.Element => ; 14 | const BadgeForStory = modifyVariantsForStory(BaseBadge); 15 | 16 | const Component: Meta = { 17 | title: 'Components/Badge', 18 | component: BadgeForStory, 19 | argTypes: { 20 | size: { control: 'inline-radio' }, 21 | variant: { control: 'select' }, 22 | borderless: { control: 'boolean' }, 23 | }, 24 | }; 25 | 26 | export const Colors: StoryFn = (args) => ( 27 | 28 | Default 29 | {COLORS.map((color) => ( 30 | 31 | {color} 32 | 33 | ))} 34 | 35 | ); 36 | 37 | Colors.args = { 38 | interactive: false, 39 | size: 'small', 40 | variant: 'gray', 41 | borderless: false, 42 | }; 43 | 44 | export const AlphaBackground: StoryFn = Colors.bind({}); 45 | 46 | AlphaBackground.args = { 47 | alphaBg: true, 48 | }; 49 | 50 | export const Small: StoryFn = (args) => Small badge; 51 | 52 | Small.args = { 53 | interactive: false, 54 | size: 'small', 55 | variant: 'blue', 56 | borderless: false, 57 | }; 58 | 59 | export const Large: StoryFn = (args) => Large badge; 60 | 61 | Large.args = { 62 | interactive: false, 63 | size: 'large', 64 | variant: 'green', 65 | borderless: false, 66 | }; 67 | 68 | export const Interactive: StoryFn = (args) => ( 69 | 70 | Default 71 | {COLORS.map((color) => ( 72 | 73 | {color} 74 | 75 | ))} 76 | 77 | ); 78 | 79 | Interactive.args = { 80 | interactive: true, 81 | size: 'small', 82 | variant: 'gray', 83 | borderless: false, 84 | }; 85 | 86 | export const BadgeLink: StoryFn = (args) => ( 87 | 88 | Link 89 | 90 | ); 91 | 92 | export const Borderless: StoryFn = (args) => ( 93 | Borderless badge 94 | ); 95 | 96 | Borderless.args = { 97 | interactive: true, 98 | size: 'small', 99 | variant: 'neon', 100 | borderless: true, 101 | }; 102 | 103 | export default Component; 104 | -------------------------------------------------------------------------------- /components/Badge/Badge.test.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | 3 | import { UnstyledLink } from '../Link'; 4 | import { Badge } from './Badge'; 5 | 6 | describe('Badge', () => { 7 | it('should render Badge as link', () => { 8 | const { getByRole } = render( 9 | 10 | Link 11 | 12 | ); 13 | 14 | expect(getByRole('link')).toBeInTheDocument(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /components/Badge/Badge.themes.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '@stitches/react/types/css'; 2 | import tinycolor from 'tinycolor2'; 3 | 4 | import { ColorInfo } from '../../utils/getPrimaryColorInfo'; 5 | 6 | export namespace Theme { 7 | type Colors = { 8 | badgeInteractiveBackgroundActive: Property.Color; 9 | badgeInteractiveBackgroundHover: Property.Color; 10 | }; 11 | 12 | type Factory = (primaryColor: ColorInfo) => Colors; 13 | 14 | export const getLight: Factory = () => ({ 15 | badgeInteractiveBackgroundActive: tinycolor('black').setAlpha(0.1).toHslString(), 16 | badgeInteractiveBackgroundHover: tinycolor('black').setAlpha(0.05).toHslString(), 17 | }); 18 | 19 | export const getDark: Factory = () => ({ 20 | badgeInteractiveBackgroundActive: tinycolor('black').setAlpha(0.2).toHslString(), 21 | badgeInteractiveBackgroundHover: tinycolor('black').setAlpha(0.1).toHslString(), 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /components/Badge/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Badge'; 2 | -------------------------------------------------------------------------------- /components/Blockquote/Blockquote.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory'; 5 | import { Blockquote, BlockquoteProps, BlockquoteVariants } from './Blockquote'; 6 | 7 | const BaseBlockquote = (props: BlockquoteProps): JSX.Element =>
; 8 | 9 | const BlockquoteForStory = modifyVariantsForStory( 10 | BaseBlockquote, 11 | ); 12 | 13 | const Component: Meta = { 14 | title: 'Components/Blockquote', 15 | component: BlockquoteForStory, 16 | }; 17 | 18 | const Template: StoryFn = (args) => ( 19 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
20 | ); 21 | 22 | export const Basic: StoryFn = Template.bind({}); 23 | 24 | export default Component; 25 | -------------------------------------------------------------------------------- /components/Blockquote/Blockquote.tsx: -------------------------------------------------------------------------------- 1 | import { CSS, styled, VariantProps } from '../../stitches.config'; 2 | import { Text } from '../Text'; 3 | 4 | export const Blockquote = styled(Text, { 5 | borderLeft: '2px solid $textDefault', 6 | p: '$2 $3', 7 | }); 8 | 9 | export type BlockquoteVariants = VariantProps; 10 | export type BlockquoteProps = BlockquoteVariants & { css?: CSS }; 11 | -------------------------------------------------------------------------------- /components/Blockquote/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Blockquote'; 2 | -------------------------------------------------------------------------------- /components/Box/Box.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { VariantProps } from '../../stitches.config'; 5 | import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory'; 6 | import { Text } from '../Text'; 7 | import { Box } from './Box'; 8 | 9 | type BoxVariants = VariantProps; 10 | type BoxProps = BoxVariants & NonNullable; 11 | 12 | const BaseBox = (props: BoxProps): JSX.Element => ; 13 | const BoxForStory = modifyVariantsForStory>( 14 | BaseBox 15 | ); 16 | 17 | const Component: Meta = { 18 | title: 'Components/Box', 19 | component: BoxForStory, 20 | }; 21 | 22 | export const Basic: StoryFn = (args) => ( 23 | 24 | 25 | Traefik Labs develops the world's most popular cloud-native application networking software. 26 | It helps developers and operations teams of all sizes build, deploy and run modern 27 | microservices applications quickly and easily. 28 | 29 | 30 | ); 31 | 32 | export default Component; 33 | -------------------------------------------------------------------------------- /components/Box/Box.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '../../stitches.config'; 2 | 3 | export const Box = styled('div', { 4 | // Reset 5 | boxSizing: 'border-box', 6 | }); 7 | -------------------------------------------------------------------------------- /components/Box/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Box'; 2 | -------------------------------------------------------------------------------- /components/Bubble/Bubble.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { VariantProps } from '../../stitches.config'; 5 | import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory'; 6 | import { Flex } from '../Flex'; 7 | import { Bubble } from './Bubble'; 8 | 9 | type BubbleVariants = VariantProps; 10 | type BubbleProps = BubbleVariants & NonNullable; 11 | 12 | const BaseBubble = (props: BubbleProps): JSX.Element => ; 13 | const BubbleForStory = modifyVariantsForStory(BaseBubble); 14 | 15 | const Component: Meta = { 16 | title: 'Components/Bubble', 17 | component: BubbleForStory, 18 | }; 19 | 20 | export const Colors: StoryFn = (args) => ( 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | ); 31 | 32 | Colors.args = { 33 | size: 'small', 34 | noAnimation: false, 35 | }; 36 | 37 | Colors.argTypes = { 38 | size: { 39 | control: 'inline-radio', 40 | options: ['x-small', 'small', 'medium', 'large', 'x-large'], 41 | }, 42 | }; 43 | 44 | export const Sizes: StoryFn = (args) => ( 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ); 53 | 54 | Sizes.args = { 55 | variant: 'purple', 56 | }; 57 | 58 | Sizes.argTypes = { 59 | size: { 60 | control: false, 61 | noAnimation: false, 62 | }, 63 | variant: { 64 | control: 'select', 65 | }, 66 | }; 67 | 68 | export default Component; 69 | -------------------------------------------------------------------------------- /components/Bubble/Bubble.tsx: -------------------------------------------------------------------------------- 1 | import { keyframes, styled } from '../../stitches.config'; 2 | 3 | const bip = keyframes({ 4 | '0%': { 5 | top: 0, 6 | right: 0, 7 | bottom: 0, 8 | left: 0, 9 | opacity: 1, 10 | }, 11 | '100%': { 12 | top: -8, 13 | right: -8, 14 | bottom: -8, 15 | left: -8, 16 | opacity: 0, 17 | }, 18 | }); 19 | 20 | export const Bubble = styled('div', { 21 | display: 'inline-block', 22 | size: '$4', 23 | bc: '$red8', 24 | borderRadius: '50%', 25 | position: 'relative', 26 | 27 | '&::before': { 28 | animation: `${bip} 1s ease infinite`, 29 | boxSizing: 'border-box', 30 | content: '""', 31 | position: 'absolute', 32 | top: 0, 33 | right: 0, 34 | bottom: 0, 35 | left: 0, 36 | boxShadow: 'inset 0 0 0 1px rgba(255,255,255,.5)', 37 | borderRadius: '50%', 38 | pointerEvents: 'none', 39 | zIndex: -1, 40 | }, 41 | 42 | '&::after': { 43 | boxSizing: 'border-box', 44 | content: '""', 45 | position: 'absolute', 46 | top: 5, 47 | right: 5, 48 | bottom: 5, 49 | left: 5, 50 | bc: 'rgba(255,255,255,.1)', 51 | borderRadius: '50%', 52 | pointerEvents: 'none', 53 | }, 54 | 55 | variants: { 56 | variant: { 57 | red: { bc: '$red8', '&::before': { bc: '$red8' } }, 58 | green: { bc: '$green8', '&::before': { bc: '$green8' } }, 59 | orange: { bc: '$orange8', '&::before': { bc: '$orange8' } }, 60 | blue: { bc: '$blue8', '&::before': { bc: '$blue8' } }, 61 | yellow: { bc: '$neon8', '&::before': { bc: '$neon8' } }, 62 | purple: { bc: '$purple8', '&::before': { bc: '$purple8' } }, 63 | gray: { bc: '$slate8', '&::before': { bc: '$slate8' } }, 64 | }, 65 | size: { 66 | 'x-small': { size: '$1' }, 67 | small: { size: '$2' }, 68 | medium: { size: '$3' }, 69 | large: { size: '$4' }, 70 | 'x-large': { size: '$5' }, 71 | }, 72 | noAnimation: { 73 | true: { 74 | '&::before': { content: 'none' }, 75 | }, 76 | }, 77 | }, 78 | 79 | defaultVariants: { 80 | size: 'small', 81 | noAnimation: false, 82 | }, 83 | }); 84 | -------------------------------------------------------------------------------- /components/Bubble/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Bubble'; 2 | -------------------------------------------------------------------------------- /components/Button/Button.stories.tsx: -------------------------------------------------------------------------------- 1 | import { InfoCircledIcon } from '@radix-ui/react-icons'; 2 | import { Meta, StoryFn } from '@storybook/react'; 3 | import React from 'react'; 4 | 5 | import { Flex } from '../Flex'; 6 | import { UnstyledLink } from '../Link'; 7 | import { Text } from '../Text'; 8 | import { ButtonForStory } from './Button'; 9 | 10 | const Component: Meta = { 11 | title: 'Components/Button', 12 | component: ButtonForStory, 13 | argTypes: { onClick: { action: 'clicked' } }, 14 | }; 15 | 16 | const Template: StoryFn = (args) => ( 17 | Button 18 | ); 19 | 20 | export const Primary: StoryFn = Template.bind({}); 21 | 22 | Primary.args = {}; 23 | 24 | export const Secondary: StoryFn = Template.bind({}); 25 | 26 | Secondary.args = { 27 | variant: 'secondary', 28 | }; 29 | 30 | export const Red: StoryFn = Template.bind({}); 31 | 32 | Red.args = { 33 | variant: 'red', 34 | }; 35 | 36 | export const Ghost: StoryFn = Template.bind({}); 37 | 38 | Ghost.args = { 39 | ghost: true, 40 | variant: 'secondary', 41 | }; 42 | 43 | export const Disabled: StoryFn = Template.bind({}); 44 | 45 | Disabled.args = { 46 | disabled: true, 47 | }; 48 | 49 | const TemplateWithIcon: StoryFn = (args) => ( 50 | 51 | 52 | 53 | Button 54 | 55 | 56 | ); 57 | 58 | export const WithIcon: StoryFn = TemplateWithIcon.bind({}); 59 | 60 | const TemplateWithActive: StoryFn = ({ ...args }) => { 61 | const [active, setActive] = React.useState(0); 62 | 63 | return ( 64 | <> 65 | Tab {active + 1} is active 66 | 67 | {[...Array(4)].map((_, i) => ( 68 | setActive(i)} 73 | > 74 | Tab {i + 1} 75 | 76 | ))} 77 | 78 | 79 | ); 80 | }; 81 | 82 | export const Active: StoryFn = TemplateWithActive.bind({}); 83 | 84 | Active.args = {}; 85 | 86 | export const Waiting: StoryFn = Template.bind({}); 87 | 88 | Waiting.args = { 89 | state: 'waiting', 90 | }; 91 | 92 | export const ButtonLink: StoryFn = (args) => ( 93 | 94 | Button 95 | 96 | ); 97 | ButtonLink.argTypes = { 98 | state: { 99 | options: ['waiting', undefined], 100 | control: 'inline-radio', 101 | }, 102 | }; 103 | 104 | export default Component; 105 | -------------------------------------------------------------------------------- /components/Button/Button.test.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | 3 | import { UnstyledLink } from '../Link'; 4 | import { Button } from './Button'; 5 | 6 | describe('Button', () => { 7 | it('should render Button as link', () => { 8 | const { getByRole } = render( 9 | 12 | ); 13 | 14 | expect(getByRole('link')).toBeInTheDocument(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /components/Button/Button.themes.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '@stitches/react/types/css'; 2 | import tinycolor from 'tinycolor2'; 3 | 4 | import { ColorInfo } from '../../utils/getPrimaryColorInfo'; 5 | 6 | export namespace Theme { 7 | type Colors = { 8 | buttonPrimaryBg: Property.Color; 9 | buttonPrimaryText: Property.Color; 10 | buttonPrimaryFocusBorder: Property.Color; 11 | buttonPrimaryGhostHoverText: Property.Color; 12 | 13 | buttonSecondaryBg: Property.Color; 14 | buttonSecondaryText: Property.Color; 15 | buttonSecondaryBorder: Property.Color; 16 | buttonSecondaryFocusBorder: Property.Color; 17 | 18 | buttonRedBg: Property.Color; 19 | buttonRedText: Property.Color; 20 | buttonRedHoverText: Property.Color; 21 | buttonRedFocusBg: Property.Color; 22 | }; 23 | 24 | type Factory = (primaryColor: ColorInfo) => Colors; 25 | 26 | export const getLight: Factory = (primaryColor) => ({ 27 | buttonPrimaryBg: '$primary', 28 | buttonPrimaryFocusBg: tinycolor(primaryColor.value).lighten(10).toHslString(), 29 | buttonPrimaryText: 'white', 30 | buttonPrimaryFocusBorder: primaryColor.helpers.pickScale(6, { alpha: true }), 31 | buttonPrimaryGhostHoverText: '$deepBlue9', 32 | 33 | buttonSecondaryBg: 'transparent', 34 | buttonSecondaryText: 'hsla(0, 0%, 0%, 0.54)', 35 | buttonSecondaryBorder: '$grayBlue9', 36 | buttonSecondaryFocusBorder: tinycolor(primaryColor.value).setAlpha(0.19).toHslString(), 37 | 38 | buttonRedBg: '$red9', 39 | buttonRedText: '$loContrast', 40 | buttonRedHoverText: '$red10', 41 | buttonRedFocusBg: '$redA8', 42 | }); 43 | 44 | export const getDark: Factory = (primaryColor) => ({ 45 | buttonPrimaryBg: '$primary', 46 | buttonPrimaryFocusBg: tinycolor(primaryColor.value).lighten(10).toHslString(), 47 | buttonPrimaryText: '$deepBlue2', 48 | buttonPrimaryFocusBorder: primaryColor.helpers.pickScale(12, { alpha: true }), 49 | buttonPrimaryGhostHoverText: tinycolor(primaryColor.value).lighten(10).toHslString(), 50 | 51 | buttonSecondaryBg: 'transparent', 52 | buttonSecondaryText: tinycolor('white').setAlpha(0.74).toHslString(), 53 | buttonSecondaryBorder: '$grayBlue9', 54 | buttonSecondaryFocusBorder: '$primary', 55 | 56 | buttonRedBg: '$red10', 57 | buttonRedText: '$hiContrast', 58 | buttonRedHoverText: '$red10', 59 | buttonRedFocusBg: '$redA11', 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /components/Button/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Button'; 2 | -------------------------------------------------------------------------------- /components/ButtonSwitch/ButtonSwitch.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | import { useState } from 'react'; 4 | 5 | import { ButtonSwitchContainer, ButtonSwitchItem } from './ButtonSwitch'; 6 | 7 | const Component: Meta = { 8 | title: 'Components/ButtonSwitch', 9 | component: ButtonSwitchContainer, 10 | }; 11 | 12 | export const Basic: StoryFn = () => { 13 | const [value, setValue] = useState('left'); 14 | 15 | return ( 16 | 17 | Left 18 | Right 19 | 20 | ); 21 | }; 22 | 23 | export const Multiple: StoryFn = () => { 24 | const [value, setValue] = useState([]); 25 | 26 | return ( 27 | 28 | Option 1 29 | Option 2 30 | Option 3 31 | Option 4 32 | 33 | ); 34 | }; 35 | 36 | export default Component; 37 | -------------------------------------------------------------------------------- /components/ButtonSwitch/ButtonSwitch.themes.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '@stitches/react/types/css'; 2 | 3 | import { ColorInfo } from '../../utils/getPrimaryColorInfo'; 4 | 5 | export namespace Theme { 6 | type Colors = { 7 | buttonSwitchContainerBg: Property.Color; 8 | buttonSwitchActiveBg: Property.Color; 9 | buttonSwitchOffBg: Property.Color; 10 | buttonSwitchOffColor: Property.Color; 11 | buttonSwitchActiveColor: Property.Color; 12 | }; 13 | 14 | type Factory = (primaryColor: ColorInfo) => Colors; 15 | 16 | export const getLight: Factory = () => ({ 17 | buttonSwitchContainerBg: '$02dp', 18 | buttonSwitchActiveBg: '$primary', 19 | buttonSwitchOffBg: 'transparent', 20 | buttonSwitchOffColor: '$hiContrast', 21 | buttonSwitchActiveColor: 'white', 22 | }); 23 | 24 | export const getDark: Factory = () => ({ 25 | buttonSwitchContainerBg: '$02dp', 26 | buttonSwitchActiveBg: '$primary', 27 | buttonSwitchOffBg: 'transparent', 28 | buttonSwitchOffColor: '$hiContrast', 29 | buttonSwitchActiveColor: '$deepBlue2', 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /components/ButtonSwitch/ButtonSwitch.tsx: -------------------------------------------------------------------------------- 1 | import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group'; 2 | 3 | import { styled } from '../../stitches.config'; 4 | 5 | export const ButtonSwitchContainer = styled(ToggleGroupPrimitive.Root, { 6 | display: 'inline-flex', 7 | bc: '$buttonSwitchContainerBg', 8 | borderRadius: '$3', 9 | p: '3px', 10 | gap: '$1', 11 | }); 12 | 13 | export const ButtonSwitchItem = styled(ToggleGroupPrimitive.Item, { 14 | display: 'inline-flex', 15 | bc: '$buttonSwitchOffBg', 16 | c: '$buttonSwitchOffColor', 17 | p: '$1', 18 | width: '$10', 19 | justifyContent: 'center', 20 | fontWeight: 600, 21 | border: 'none', 22 | position: 'relative', 23 | 24 | '&::before': { 25 | boxSizing: 'border-box', 26 | content: '""', 27 | position: 'absolute', 28 | inset: 0, 29 | borderRadius: '$3', 30 | }, 31 | '&::after': { 32 | boxSizing: 'border-box', 33 | content: '""', 34 | position: 'absolute', 35 | inset: 0, 36 | borderRadius: '$3', 37 | }, 38 | 39 | '&:focus-visible': { 40 | borderRadius: '$3', 41 | '&::before': { 42 | backgroundColor: 'rgba(255, 255, 255, 0.15)', 43 | }, 44 | '&::after': { 45 | opacity: 0.15, 46 | }, 47 | }, 48 | 49 | '@hover': { 50 | '&:hover': { 51 | cursor: 'pointer', 52 | '&::before': { 53 | backgroundColor: 'rgba(255, 255, 255, 0.15)', 54 | }, 55 | '&::after': { 56 | opacity: 0.05, 57 | }, 58 | }, 59 | }, 60 | 61 | '&[data-state=on]': { 62 | bc: '$buttonSwitchActiveBg', 63 | c: '$buttonSwitchActiveColor', 64 | borderRadius: '$3', 65 | }, 66 | }); 67 | -------------------------------------------------------------------------------- /components/ButtonSwitch/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ButtonSwitch'; 2 | -------------------------------------------------------------------------------- /components/Card/Card.themes.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '@stitches/react/types/css'; 2 | import tinycolor from 'tinycolor2'; 3 | 4 | import { ColorInfo } from '../../utils/getPrimaryColorInfo'; 5 | 6 | export namespace Theme { 7 | type Colors = { 8 | cardBackground: Property.Color; 9 | cardBorder: Property.Color; 10 | cardShadow: Property.Color; 11 | cardHoverBackground: Property.Color; 12 | cardHoverBorder: Property.Color; 13 | cardActiveBackground: Property.Color; 14 | cardActiveBorder: Property.Color; 15 | cardGhostBackground: Property.Color; 16 | innerCardBgColor: Property.Color; 17 | }; 18 | 19 | type Factory = (primaryColor: ColorInfo) => Colors; 20 | 21 | export const getLight: Factory = (primaryColor) => ({ 22 | cardBackground: 'white', 23 | cardBorder: '$deepBlue3', 24 | cardShadow: 'rgba(0,0,0,.1)', 25 | cardHoverBackground: 'rgba(0,0,0,.05)', 26 | cardHoverBorder: tinycolor(primaryColor.value).setAlpha(0.6).toHslString(), 27 | cardActiveBackground: 'rgba(0,0,0,.03)', 28 | cardActiveBorder: '$primary', 29 | cardGhostBackground: '$deepBlue2', 30 | innerCardBgColor: tinycolor('black').setAlpha(0.04).toHslString(), 31 | }); 32 | 33 | export const getDark: Factory = (primaryColor) => ({ 34 | cardBackground: '$deepBlue2', 35 | cardBorder: '$deepBlue3', 36 | cardShadow: 'transparent', 37 | cardHoverBackground: 'rgba(255,255,255,.12)', 38 | cardHoverBorder: tinycolor(primaryColor.value).setAlpha(0.6).toHslString(), 39 | cardActiveBackground: 'rgba(255,255,255,.07)', 40 | cardActiveBorder: tinycolor(primaryColor.value).setAlpha(0.4).toHslString(), 41 | cardGhostBackground: '$deepBlue1', 42 | innerCardBgColor: tinycolor('white').setAlpha(0.07).toHslString(), 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /components/Card/Card.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentProps, ElementRef } from 'react'; 2 | 3 | import { styled, VariantProps } from '../../stitches.config'; 4 | import { elevationVariants } from '../Elevation/Elevation'; 5 | 6 | const StyledCard = styled('div', { 7 | appearance: 'none', 8 | border: 'none', 9 | boxSizing: 'border-box', 10 | font: 'inherit', 11 | lineHeight: '1', 12 | outline: 'none', 13 | padding: '$3', 14 | textAlign: 'inherit', 15 | verticalAlign: 'middle', 16 | WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)', 17 | 18 | backgroundColor: '$cardBackground', 19 | display: 'block', 20 | textDecoration: 'none', 21 | color: 'inherit', 22 | borderRadius: '$3', 23 | position: 'relative', 24 | 25 | '&::before': { 26 | boxSizing: 'border-box', 27 | content: '""', 28 | position: 'absolute', 29 | top: 0, 30 | right: 0, 31 | bottom: 0, 32 | left: 0, 33 | borderRadius: '$3', 34 | pointerEvents: 'none', 35 | }, 36 | 37 | variants: { 38 | elevation: elevationVariants, 39 | variant: { 40 | inner: { 41 | backgroundColor: '$innerCardBgColor', 42 | }, 43 | ghost: { 44 | backgroundColor: 'transparent', 45 | boxShadow: 'none', 46 | }, 47 | }, 48 | active: { 49 | true: { 50 | '&::before': { 51 | outline: '1px solid $colors$cardActiveBorder', 52 | backgroundColor: '$cardActiveBackground', 53 | }, 54 | }, 55 | }, 56 | }, 57 | defaultVariants: { 58 | elevation: 1, 59 | }, 60 | }); 61 | 62 | const StyledInteractiveCard = styled('button', StyledCard, { 63 | '@hover': { 64 | '&:hover': { 65 | cursor: 'pointer', 66 | '&::before': { 67 | outline: '1px solid $colors$cardHoverBorder', 68 | backgroundColor: '$cardHoverBackground', 69 | }, 70 | }, 71 | }, 72 | '&:focus': { 73 | outline: '2px solid $primary', 74 | }, 75 | '&:active': { 76 | '&::before': { 77 | outline: '1px solid $colors$cardActiveBorder', 78 | backgroundColor: '$cardActiveBackground', 79 | }, 80 | }, 81 | }); 82 | 83 | export type CardVariantProps = ComponentProps & 84 | VariantProps & { 85 | interactive?: false; 86 | }; 87 | export type InteractiveCardProps = VariantProps & 88 | ComponentProps & { 89 | interactive: true; 90 | }; 91 | export type CardProps = CardVariantProps | InteractiveCardProps; 92 | 93 | export const Card = React.forwardRef, CardProps>( 94 | ({ interactive, ...props }, forwardedRef) => { 95 | if (interactive) { 96 | return ( 97 | } 100 | {...(props as InteractiveCardProps)} 101 | /> 102 | ); 103 | } 104 | return ( 105 | } 107 | {...(props as CardVariantProps)} 108 | /> 109 | ); 110 | }, 111 | ); 112 | -------------------------------------------------------------------------------- /components/Card/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Card'; 2 | -------------------------------------------------------------------------------- /components/Checkbox/Checkbox.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory'; 5 | import { Checkbox, CheckboxProps, CheckboxVariants } from './Checkbox'; 6 | 7 | const BaseCheckbox = (props: CheckboxProps): JSX.Element => ; 8 | const CheckboxForStory = modifyVariantsForStory< 9 | CheckboxVariants, 10 | CheckboxProps & React.InputHTMLAttributes 11 | >(BaseCheckbox); 12 | 13 | const Component: Meta = { 14 | title: 'Components/Checkbox', 15 | component: CheckboxForStory, 16 | argTypes: { onCheckedChange: { action: 'checkedChange' } }, 17 | }; 18 | 19 | const Template: StoryFn = (args) => ; 20 | 21 | export const Basic: StoryFn = Template.bind({}); 22 | 23 | Basic.args = {}; 24 | 25 | export const Size: StoryFn = Template.bind({}); 26 | 27 | Size.args = { 28 | size: 'large', 29 | }; 30 | 31 | export const Disabled: StoryFn = Template.bind({}); 32 | 33 | Disabled.args = { 34 | disabled: true, 35 | checked: true, 36 | size: 'large', 37 | }; 38 | 39 | export default Component; 40 | -------------------------------------------------------------------------------- /components/Checkbox/Checkbox.themes.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '@stitches/react/types/css'; 2 | import tinycolor from 'tinycolor2'; 3 | 4 | import { ColorInfo } from '../../utils/getPrimaryColorInfo'; 5 | 6 | export namespace Theme { 7 | type Colors = { 8 | checkboxIcon: Property.Color; 9 | checkboxBg: Property.Color; 10 | checkboxCheckedBg: Property.Color; 11 | checkboxBorder: Property.Color; 12 | checkboxCheckedIcon: Property.Color; 13 | checkboxCheckedHoverBg: Property.Color; 14 | checkboxHoverBg: Property.Color; 15 | checkboxHoverBorder: Property.Color; 16 | checkboxFocusBorder: Property.Color; 17 | checkboxDisabledBg: Property.Color; 18 | checkboxDisabledBorder: Property.Color; 19 | checkboxIndicatorDisabledBg: Property.Color; 20 | }; 21 | 22 | type Factory = (primaryColor: ColorInfo) => Colors; 23 | 24 | export const getLight: Factory = (primaryColor) => ({ 25 | checkboxIcon: '$deepBlue11', 26 | checkboxBg: 'transparent', 27 | checkboxBorder: '$slate8', 28 | checkboxCheckedBg: '$primary', 29 | checkboxCheckedIcon: 'white', 30 | checkboxCheckedHoverBg: tinycolor(primaryColor.value).darken().toHslString(), 31 | checkboxHoverBg: 'transparent', 32 | checkboxHoverBorder: '$primary', 33 | checkboxFocusBorder: '$primary', 34 | checkboxDisabledBg: '$deepBlue3', 35 | checkboxDisabledBorder: '$deepBlue5', 36 | checkboxIndicatorDisabledBg: tinycolor(primaryColor.value).setAlpha(0.6).toHslString(), 37 | }); 38 | 39 | export const getDark: Factory = (primaryColor) => ({ 40 | checkboxIcon: '$deepBlue1', 41 | checkboxBg: 'transparent', 42 | checkboxCheckedBg: '$primary', 43 | checkboxBorder: '$slate9', 44 | checkboxCheckedIcon: '$deepBlue1', 45 | checkboxCheckedHoverBg: tinycolor(primaryColor.value).lighten(10).toHslString(), 46 | checkboxHoverBg: '$deepBlue3', 47 | checkboxHoverBorder: '$primary', 48 | checkboxFocusBorder: '$primary', 49 | checkboxDisabledBg: tinycolor(primaryColor.value).setAlpha(0.6).toHslString(), 50 | checkboxDisabledBorder: 'transparent', 51 | checkboxIndicatorDisabledBg: '$deepBlue1', 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /components/Checkbox/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; 2 | import { CheckIcon } from '@radix-ui/react-icons'; 3 | import React from 'react'; 4 | 5 | import { CSS, styled, VariantProps } from '../../stitches.config'; 6 | 7 | const StyledIndicator = styled(CheckboxPrimitive.Indicator, { 8 | alignItems: 'center', 9 | display: 'flex', 10 | height: '100%', 11 | justifyContent: 'center', 12 | width: '100%', 13 | }); 14 | 15 | const StyledCheckbox = styled(CheckboxPrimitive.Root, { 16 | all: 'unset', 17 | border: 'none', 18 | boxSizing: 'border-box', 19 | userSelect: 'none', 20 | '&::before': { 21 | boxSizing: 'border-box', 22 | }, 23 | '&::after': { 24 | boxSizing: 'border-box', 25 | }, 26 | 27 | alignItems: 'center', 28 | appearance: 'none', 29 | display: 'inline-flex', 30 | justifyContent: 'center', 31 | lineHeight: '1', 32 | margin: '0', 33 | outline: 'none', 34 | padding: '0', 35 | WebkitTapHighlightColor: 'rgba(0,0,0,0)', 36 | overflow: 'hidden', 37 | 38 | '&[data-state=checked]': { 39 | backgroundColor: '$checkboxCheckedBg', 40 | boxShadow: 'inset 0 0 0 1px transparent', 41 | color: '$checkboxCheckedIcon', 42 | 43 | '@hover': { 44 | '&:hover': { 45 | backgroundColor: '$checkboxCheckedHoverBg', 46 | }, 47 | }, 48 | }, 49 | 50 | '&[data-state=unchecked]': { 51 | backgroundColor: '$checkboxBg', 52 | boxShadow: 'inset 0 0 0 1px $colors$checkboxBorder', 53 | color: '$checkboxIcon', 54 | 55 | '@hover': { 56 | '&:hover': { 57 | backgroundColor: '$checkboxHoverBg', 58 | boxShadow: 'inset 0 0 0 1px $colors$checkboxHoverBorder', 59 | }, 60 | }, 61 | }, 62 | 63 | '&:focus': { 64 | outline: 'none', 65 | boxShadow: 'inset 0 0 0 1px $colors$checkboxFocusBorder', 66 | }, 67 | 68 | '&:disabled': { 69 | pointerEvents: 'none', 70 | backgroundColor: '$checkboxDisabledBg', 71 | boxShadow: 'inset 0 0 0 1px $colors$checkboxDisabledBorder', 72 | '&::placeholder': { 73 | color: '$checkboxDisabledText', 74 | }, 75 | 76 | [`& ${StyledIndicator}`]: { 77 | '&::after': { 78 | backgroundColor: '$checkboxIndicatorDisabledBg', 79 | }, 80 | }, 81 | }, 82 | 83 | variants: { 84 | size: { 85 | medium: { 86 | width: 18, 87 | height: 18, 88 | borderRadius: '$2', 89 | }, 90 | large: { 91 | width: '$5', 92 | height: '$5', 93 | borderRadius: '$3', 94 | }, 95 | }, 96 | }, 97 | defaultVariants: { 98 | size: 'medium', 99 | }, 100 | }); 101 | 102 | type CheckboxPrimitiveProps = Omit, 'as'>; 103 | export type CheckboxVariants = VariantProps; 104 | export type CheckboxProps = CheckboxPrimitiveProps & CheckboxVariants & { css?: CSS }; 105 | 106 | export const Checkbox = React.forwardRef, CheckboxProps>( 107 | (props, forwardedRef) => { 108 | return ( 109 | 110 | 111 | 112 | 113 | 114 | ); 115 | } 116 | ); 117 | -------------------------------------------------------------------------------- /components/Checkbox/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Checkbox'; 2 | -------------------------------------------------------------------------------- /components/Container/Container.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { VariantProps } from '../../stitches.config'; 5 | import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory'; 6 | import { Paragraph } from '../Paragraph'; 7 | import { Container } from './Container'; 8 | 9 | type ContainerVariants = VariantProps; 10 | type ContainerProps = ContainerVariants & NonNullable; 11 | 12 | const BaseContainer = (props: ContainerProps): JSX.Element => ; 13 | const ContainerForStory = modifyVariantsForStory< 14 | ContainerVariants, 15 | ContainerProps & React.HTMLAttributes 16 | >(BaseContainer); 17 | 18 | const Component: Meta = { 19 | title: 'Components/Container', 20 | component: ContainerForStory, 21 | }; 22 | 23 | const Template: StoryFn = (args) => ( 24 | 25 | 26 | Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quo, iste. Perferendis saepe aperiam 27 | repudiandae, a ea labore error iure! Doloribus sunt earum, aperiam facilis ex corporis veniam 28 | deleniti voluptatibus laudantium? Lorem ipsum dolor sit amet consectetur, adipisicing elit. 29 | Reiciendis consequatur harum quibusdam! Facilis aspernatur est fugiat laudantium officiis. Aut 30 | labore asperiores qui iure earum culpa, voluptatum explicabo commodi quis dolorem? Lorem ipsum 31 | dolor sit, amet consectetur adipisicing elit. Dolore dolorum deserunt, consectetur numquam 32 | minus error dolorem? Explicabo iure quidem, maxime fugit quos obcaecati, molestiae nemo nobis 33 | aliquid saepe, impedit at. 34 | 35 | 36 | ); 37 | 38 | export const Basic: StoryFn = Template.bind({}); 39 | 40 | Basic.args = {}; 41 | 42 | export const Size: StoryFn = Template.bind({}); 43 | 44 | Size.args = { size: '1' }; 45 | 46 | export const NoGutter: StoryFn = Template.bind({}); 47 | 48 | NoGutter.args = { noGutter: true }; 49 | 50 | export default Component; 51 | -------------------------------------------------------------------------------- /components/Container/Container.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '../../stitches.config'; 2 | 3 | export const Container = styled('div', { 4 | // Reset 5 | boxSizing: 'border-box', 6 | flexShrink: 0, 7 | 8 | // Custom 9 | ml: 'auto', 10 | mr: 'auto', 11 | px: '$5', 12 | 13 | variants: { 14 | size: { 15 | '1': { 16 | maxWidth: '425px', 17 | }, 18 | '2': { 19 | maxWidth: '768px', 20 | }, 21 | '3': { 22 | maxWidth: '1440px', 23 | }, 24 | '4': { 25 | maxWidth: 'none', 26 | }, 27 | }, 28 | noGutter: { 29 | true: { 30 | px: 0, 31 | }, 32 | }, 33 | }, 34 | defaultVariants: { 35 | size: '4', 36 | }, 37 | }); 38 | -------------------------------------------------------------------------------- /components/Container/index.ts: -------------------------------------------------------------------------------- 1 | export { Container } from './Container'; 2 | -------------------------------------------------------------------------------- /components/DateTimePicker/DateTimePicker.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React, { useState } from 'react'; 3 | 4 | import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory'; 5 | import { Flex } from '../Flex'; 6 | import { Label } from '../Label'; 7 | import { Text } from '../Text'; 8 | import { DateTimePicker, DateTimePickerProps, DateTimePickerVariants } from './DateTimePicker'; 9 | 10 | const DateTimePickerWrapper = (props: DateTimePickerProps): JSX.Element => ( 11 | 12 | ); 13 | 14 | const DateTimePickerForStory = modifyVariantsForStory< 15 | DateTimePickerVariants, 16 | DateTimePickerProps & { 17 | calendarMode?: 'fluid' | 'static' | undefined; 18 | maxDate?: Date; 19 | minDate?: Date; 20 | } 21 | >(DateTimePickerWrapper); 22 | 23 | const Component: Meta = { 24 | title: 'Components/DateTimePicker', 25 | component: DateTimePickerForStory, 26 | argTypes: { 27 | calendarMode: { 28 | control: 'inline-radio', 29 | options: ['fluid', 'static'], 30 | }, 31 | minDate: { 32 | control: 'date', 33 | }, 34 | showDatePresets: { 35 | control: 'boolean', 36 | }, 37 | showTimePicker: { 38 | control: 'boolean', 39 | }, 40 | }, 41 | }; 42 | 43 | const DateTimePickerTemplate: StoryFn = (args) => { 44 | const [selectedDates, onDatesChange] = useState([]); 45 | 46 | return ( 47 | 48 | 57 | 58 | 59 | 60 | {selectedDates.length 61 | ? selectedDates.map((date) => date.toISOString()).join(', ') 62 | : 'None.'} 63 | 64 | 65 | 66 | ); 67 | }; 68 | 69 | export const Base: StoryFn = DateTimePickerTemplate.bind({}); 70 | 71 | Base.args = { 72 | calendarMode: 'static', 73 | minDate: new Date(), 74 | showDatePresets: false, 75 | showTimePicker: false, 76 | }; 77 | 78 | export const WithDatePresets: StoryFn = DateTimePickerTemplate.bind( 79 | {}, 80 | ); 81 | 82 | WithDatePresets.args = { 83 | calendarMode: 'static', 84 | minDate: new Date(), 85 | showDatePresets: true, 86 | showTimePicker: false, 87 | }; 88 | 89 | export const WithTimePicker: StoryFn = DateTimePickerTemplate.bind( 90 | {}, 91 | ); 92 | 93 | WithTimePicker.args = { 94 | calendarMode: 'static', 95 | minDate: new Date(), 96 | showDatePresets: false, 97 | showTimePicker: true, 98 | }; 99 | 100 | export default Component; 101 | -------------------------------------------------------------------------------- /components/DateTimePicker/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DateTimePicker'; 2 | -------------------------------------------------------------------------------- /components/DateTimePickerInput/DateTimePickerInput.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React, { useState } from 'react'; 3 | 4 | import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory'; 5 | import { Flex } from '../Flex'; 6 | import { Label } from '../Label'; 7 | import { Text } from '../Text'; 8 | import { 9 | DateTimePickerInput, 10 | DateTimePickerInputProps, 11 | DateTimePickerInputVariants, 12 | } from './DateTimePickerInput'; 13 | 14 | const DateTimePickerInputWrapper = (props: DateTimePickerInputProps): JSX.Element => ( 15 | 16 | ); 17 | 18 | const DateTimePickerInputForStory = modifyVariantsForStory< 19 | DateTimePickerInputVariants, 20 | DateTimePickerInputProps 21 | >(DateTimePickerInputWrapper); 22 | 23 | const Component: Meta = { 24 | title: 'Components/DateTimePickerInput', 25 | component: DateTimePickerInputForStory, 26 | argTypes: { 27 | pickerPlacement: { 28 | control: 'inline-radio', 29 | options: [ 30 | 'top-start', 31 | 'right-start', 32 | 'bottom-start', 33 | 'left-start', 34 | 'top', 35 | 'right', 36 | 'bottom', 37 | 'left', 38 | 'top-end', 39 | 'right-end', 40 | 'bottom-end', 41 | 'left-end', 42 | ], 43 | }, 44 | showDatePresets: { 45 | control: 'boolean', 46 | }, 47 | showTimePicker: { 48 | control: 'boolean', 49 | }, 50 | }, 51 | }; 52 | 53 | const DateTimePickerTemplate: StoryFn = (args) => { 54 | const [selectedDates, onDatesChange] = useState([]); 55 | 56 | return ( 57 |
58 | 59 | 66 | 67 | 68 | 69 | {selectedDates.length 70 | ? selectedDates 71 | .map((date) => (!isNaN(date.getTime()) ? date.toISOString() : 'Invalid date')) 72 | .join(', ') 73 | : 'None.'} 74 | 75 | 76 | 77 |
78 | ); 79 | }; 80 | 81 | export const Base: StoryFn = DateTimePickerTemplate.bind({}); 82 | 83 | Base.args = { 84 | showDatePresets: true, 85 | showTimePicker: true, 86 | }; 87 | 88 | export default Component; 89 | -------------------------------------------------------------------------------- /components/DateTimePickerInput/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DateTimePickerInput'; 2 | -------------------------------------------------------------------------------- /components/Dialog/Dialog.themes.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '@stitches/react/types/css'; 2 | 3 | import { ColorInfo } from '../../utils/getPrimaryColorInfo'; 4 | 5 | export namespace Theme { 6 | type Colors = { 7 | dialogBackground: Property.Color; 8 | }; 9 | 10 | type Factory = (primaryColor?: ColorInfo) => Colors; 11 | 12 | export const getLight: Factory = () => ({ 13 | dialogBackground: '$deepBlue2', 14 | }); 15 | 16 | export const getDark: Factory = () => ({ 17 | dialogBackground: '$deepBlue3', 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /components/Dialog/Dialog.tsx: -------------------------------------------------------------------------------- 1 | import * as DialogPrimitive from '@radix-ui/react-dialog'; 2 | import { Cross1Icon } from '@radix-ui/react-icons'; 3 | import React, { ComponentProps } from 'react'; 4 | 5 | import { CSS, styled, VariantProps } from '../../stitches.config'; 6 | import { Card } from '../Card'; 7 | import { elevationVariants } from '../Elevation/Elevation'; 8 | import { IconButton } from '../IconButton'; 9 | import { overlayStyles } from '../Overlay'; 10 | 11 | type DialogProps = React.ComponentProps & { 12 | children: React.ReactNode; 13 | }; 14 | 15 | export const DialogOverlay = styled(DialogPrimitive.Overlay, overlayStyles, { 16 | position: 'fixed', 17 | top: 0, 18 | right: 0, 19 | bottom: 0, 20 | left: 0, 21 | }); 22 | 23 | export function Dialog({ children, ...props }: DialogProps) { 24 | return {children}; 25 | } 26 | 27 | export const StyledContent = styled(DialogPrimitive.Content, Card, { 28 | position: 'fixed', 29 | top: '50%', 30 | left: '50%', 31 | transform: 'translate(-50%, -50%)', 32 | minWidth: 200, 33 | maxHeight: '85vh', 34 | padding: '$4', 35 | marginTop: '-5vh', 36 | backgroundColor: '$dialogBackground', 37 | boxShadow: 'inset 0 0 0 1px $colors$deepBlue4', 38 | borderRadius: '$3', 39 | // animation: `${fadeIn} 125ms linear, ${moveDown} 125ms cubic-bezier(0.22, 1, 0.36, 1)`, 40 | willChange: 'transform', 41 | overflow: 'auto', 42 | 43 | '&::before': { 44 | boxShadow: 'none', 45 | }, 46 | 47 | '&:focus': { 48 | outline: 'none', 49 | }, 50 | 51 | variants: { 52 | elevation: elevationVariants, 53 | }, 54 | defaultVariants: { 55 | elevation: 5, 56 | }, 57 | }); 58 | 59 | const StyledCloseButton = styled(DialogPrimitive.Close, { 60 | position: 'absolute', 61 | top: '$2', 62 | right: '$2', 63 | cursor: 'pointer', 64 | }); 65 | 66 | interface DialogCloseButtonProps 67 | extends VariantProps, 68 | ComponentProps {} 69 | export const DialogCloseIconButton = React.forwardRef< 70 | React.ElementRef, 71 | DialogCloseButtonProps 72 | >((props, forwardedRef) => ( 73 | 74 | 75 | 76 | 77 | 78 | )); 79 | 80 | type DialogContentPrimitiveProps = React.ComponentProps; 81 | type DialogContentProps = DialogContentPrimitiveProps & 82 | VariantProps & { css?: CSS }; 83 | 84 | export const DialogContent = React.forwardRef< 85 | React.ElementRef, 86 | DialogContentProps 87 | >(({ children, ...props }, forwardedRef) => ( 88 | 89 | {children} 90 | 91 | 92 | )); 93 | 94 | export const DialogClosePrimitive = DialogPrimitive.Close; 95 | export const DialogDescription = DialogPrimitive.Description; 96 | export const DialogPortal = DialogPrimitive.Portal; 97 | export const DialogTitle = DialogPrimitive.Title; 98 | export const DialogTrigger = DialogPrimitive.Trigger; 99 | -------------------------------------------------------------------------------- /components/Dialog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Dialog'; 2 | -------------------------------------------------------------------------------- /components/DropdownMenu/DropdownMenu.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { Button } from '../Button'; 5 | import { 6 | DropdownMenu, 7 | DropdownMenuCheckboxItem, 8 | DropdownMenuContent, 9 | DropdownMenuGroup, 10 | DropdownMenuItem, 11 | DropdownMenuLabel, 12 | DropdownMenuPortal, 13 | DropdownMenuRadioGroup, 14 | DropdownMenuRadioItem, 15 | DropdownMenuSeparator, 16 | DropdownMenuTrigger, 17 | } from './DropdownMenu'; 18 | 19 | const Component: Meta = { 20 | title: 'Components/DropdownMenu', 21 | component: DropdownMenu, 22 | argTypes: { onOpenChange: { action: 'clicked' } }, 23 | }; 24 | 25 | const Template: StoryFn = (args) => ( 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Item 34 | Item 35 | Item 36 | 37 | Item 38 | Item 39 | Item 40 | 41 | Choose one 42 | 43 | Item 44 | Item 45 | Item 46 | 47 | 48 | 49 | 50 | 51 | ); 52 | 53 | export const Basic: StoryFn = Template.bind({}); 54 | 55 | export const Modal: StoryFn = Template.bind({}); 56 | 57 | Modal.args = { 58 | modal: true, 59 | }; 60 | 61 | export const DefaultOpen: StoryFn = Template.bind({}); 62 | 63 | DefaultOpen.args = { 64 | defaultOpen: true, 65 | }; 66 | 67 | export default Component; 68 | -------------------------------------------------------------------------------- /components/DropdownMenu/DropdownMenu.tsx: -------------------------------------------------------------------------------- 1 | import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; 2 | import { CheckIcon } from '@radix-ui/react-icons'; 3 | import React from 'react'; 4 | 5 | import { CSS, styled } from '../../stitches.config'; 6 | import { Box } from '../Box'; 7 | import { Flex } from '../Flex'; 8 | import { panelStyles } from '../Panel'; 9 | import { itemCss, labelCss, menuCss, separatorCss } from './styles'; 10 | 11 | export const DropdownMenu = DropdownMenuPrimitive.Root; 12 | export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; 13 | export const DropdownMenuContent = styled(DropdownMenuPrimitive.Content, menuCss, panelStyles); 14 | export const DropdownMenuSeparator = styled(DropdownMenuPrimitive.Separator, separatorCss); 15 | export const DropdownMenuItem = styled(DropdownMenuPrimitive.Item, itemCss); 16 | 17 | const StyledDropdownMenuRadioItem = styled(DropdownMenuPrimitive.RadioItem, itemCss); 18 | 19 | type DialogMenuRadioItemPrimitiveProps = Omit< 20 | React.ComponentProps, 21 | 'as' 22 | >; 23 | type DialogMenuRadioItemProps = DialogMenuRadioItemPrimitiveProps & { css?: CSS }; 24 | 25 | export const DropdownMenuRadioItem = React.forwardRef< 26 | React.ElementRef, 27 | DialogMenuRadioItemProps 28 | >(({ children, ...props }, forwardedRef) => ( 29 | 30 | 31 | 32 | 33 | 41 | 42 | 43 | 44 | {children} 45 | 46 | )); 47 | 48 | const StyledDropdownMenuCheckboxItem = styled(DropdownMenuPrimitive.CheckboxItem, itemCss); 49 | 50 | type DialogMenuCheckboxItemPrimitiveProps = Omit< 51 | React.ComponentProps, 52 | 'as' 53 | >; 54 | type DialogMenuCheckboxItemProps = DialogMenuCheckboxItemPrimitiveProps & { css?: CSS }; 55 | 56 | export const DropdownMenuCheckboxItem = React.forwardRef< 57 | React.ElementRef, 58 | DialogMenuCheckboxItemProps 59 | >(({ children, ...props }, forwardedRef) => ( 60 | 61 | 62 | 63 | 64 | 65 | 66 | {children} 67 | 68 | )); 69 | 70 | export const DropdownMenuLabel = styled(DropdownMenuPrimitive.Label, labelCss); 71 | export const DropdownMenuRadioGroup = styled(DropdownMenuPrimitive.RadioGroup, {}); 72 | export const DropdownMenuGroup = styled(DropdownMenuPrimitive.Group, {}); 73 | export const DropdownMenuPortal = DropdownMenuPrimitive.Portal; 74 | -------------------------------------------------------------------------------- /components/DropdownMenu/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DropdownMenu'; 2 | -------------------------------------------------------------------------------- /components/DropdownMenu/styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '../../stitches.config'; 2 | import { elevationVariants } from '../Elevation'; 3 | 4 | export const baseItemCss = css({ 5 | display: 'flex', 6 | alignItems: 'center', 7 | justifyContent: 'space-between', 8 | fontFamily: '$rubik', 9 | fontSize: '$1', 10 | fontVariantNumeric: 'tabular-nums', 11 | lineHeight: '1', 12 | cursor: 'default', 13 | userSelect: 'none', 14 | whiteSpace: 'nowrap', 15 | height: '$5', 16 | px: '$5', 17 | }); 18 | 19 | export const itemCss = css(baseItemCss, { 20 | position: 'relative', 21 | color: '$hiContrast', 22 | 23 | '&:focus': { 24 | outline: 'none', 25 | backgroundColor: '$blue9', 26 | color: 'white', 27 | }, 28 | 29 | '&[data-disabled]': { 30 | color: '$slate9', 31 | }, 32 | }); 33 | 34 | export const labelCss = css(baseItemCss, { 35 | color: '$slate11', 36 | }); 37 | 38 | export const menuCss = css({ 39 | boxSizing: 'border-box', 40 | minWidth: 120, 41 | py: '$1', 42 | variants: { 43 | elevation: elevationVariants, 44 | }, 45 | defaultVariants: { 46 | elevation: 2, 47 | }, 48 | }); 49 | 50 | export const separatorCss = css({ 51 | height: 1, 52 | my: '$1', 53 | backgroundColor: '$slate6', 54 | }); 55 | -------------------------------------------------------------------------------- /components/Elevation/Elevation.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { Flex } from '../Flex'; 5 | import { Text } from '../Text'; 6 | import { Elevation } from './Elevation'; 7 | 8 | const Component: Meta = { 9 | title: 'Components/Elevation', 10 | component: Elevation, 11 | }; 12 | 13 | export const Basic: StoryFn = () => ( 14 | 15 | 16 | 21 | Hello world 22 | 23 | 24 | 25 | 30 | Hello world 31 | 32 | 33 | 34 | 39 | Hello world 40 | 41 | 42 | 43 | 48 | Hello world 49 | 50 | 51 | 52 | 57 | Hello world 58 | 59 | 60 | 61 | 66 | Hello world 67 | 68 | 69 | 70 | ); 71 | 72 | Basic.args = {}; 73 | 74 | Basic.argTypes = { 75 | variant: { 76 | control: 'inline-radio', 77 | options: [0, 1, 2, 3, 4, 5], 78 | }, 79 | }; 80 | 81 | export default Component; 82 | -------------------------------------------------------------------------------- /components/Elevation/Elevation.tsx: -------------------------------------------------------------------------------- 1 | import { CSS, styled } from '../../stitches.config'; 2 | 3 | type ElevationVariant = Record; 4 | 5 | export const elevationVariants: ElevationVariant = { 6 | 0: { 7 | boxShadow: 'none', 8 | }, 9 | 1: { 10 | bc: '$01dp', 11 | boxShadow: 12 | '0 1px 5px 0 hsla(0, 0%, 0%, 0.2), 0 3px 1px -2px hsla(0, 0%, 0%, 0.12), 0 2px 2px 0 hsla(0, 0%, 0%, 0.14)', 13 | }, 14 | 2: { 15 | bc: '$02dp', 16 | boxShadow: 17 | '0 2px 4px -1px hsla(0, 0%, 0%, 0.2), 0 1px 10px 0 hsla(0, 0%, 0%, 0.12), 0 4px 5px 0 hsla(0, 0%, 0%, 0.14)', 18 | }, 19 | 3: { 20 | bc: '$03dp', 21 | boxShadow: 22 | '0 3px 5px -1px hsla(0, 0%, 0%, 0.2), 0 1px 18px 0 hsla(0, 0%, 0%, 0.12), 0 6px 10px 0 hsla(0, 0%, 0%, 0.14)', 23 | }, 24 | 4: { 25 | bc: '$04dp', 26 | boxShadow: 27 | '0 7px 8px -4px hsla(0, 0%, 0%, 0.2), 0 5px 22px 4px hsla(0, 0%, 0%, 0.12), 0 12px 17px 2px hsla(0, 0%, 0%, 0.14)', 28 | }, 29 | 5: { 30 | bc: '$05dp', 31 | boxShadow: 32 | '0 11px 15px -7px hsla(0, 0%, 0%, 0.2), 0 9px 46px 8px hsla(0, 0%, 0%, 0.12), 0 24px 38px 3px hsla(0, 0%, 0%, 0.14)', 33 | }, 34 | }; 35 | 36 | export const Elevation = styled('div', { 37 | display: 'inline-block', 38 | 39 | variants: { 40 | variant: elevationVariants, 41 | }, 42 | defaultVariants: { 43 | variant: 1, 44 | }, 45 | }); 46 | -------------------------------------------------------------------------------- /components/Elevation/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Elevation'; 2 | -------------------------------------------------------------------------------- /components/FaencyProvider.tsx: -------------------------------------------------------------------------------- 1 | import { Provider as TooltipProvider } from '@radix-ui/react-tooltip'; 2 | import React from 'react'; 3 | 4 | interface FaencyProviderProps { 5 | children?: React.ReactNode; 6 | } 7 | 8 | export const FaencyProvider = ({ children }: FaencyProviderProps) => ( 9 | {children} 10 | ); 11 | -------------------------------------------------------------------------------- /components/Flex/Flex.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { Box } from '../Box'; 5 | import { Flex } from './Flex'; 6 | 7 | const Component: Meta = { 8 | title: 'Components/Flex', 9 | component: Flex, 10 | }; 11 | 12 | export const Basic: StoryFn = (args) => ( 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | 20 | Basic.args = { 21 | direction: 'column', 22 | align: 'center', 23 | gap: '6', 24 | }; 25 | 26 | export default Component; 27 | -------------------------------------------------------------------------------- /components/Flex/Flex.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '../../stitches.config'; 2 | 3 | export const Flex = styled('div', { 4 | boxSizing: 'border-box', 5 | display: 'flex', 6 | 7 | variants: { 8 | direction: { 9 | row: { 10 | flexDirection: 'row', 11 | }, 12 | column: { 13 | flexDirection: 'column', 14 | }, 15 | rowReverse: { 16 | flexDirection: 'row-reverse', 17 | }, 18 | columnReverse: { 19 | flexDirection: 'column-reverse', 20 | }, 21 | }, 22 | align: { 23 | start: { 24 | alignItems: 'flex-start', 25 | }, 26 | center: { 27 | alignItems: 'center', 28 | }, 29 | end: { 30 | alignItems: 'flex-end', 31 | }, 32 | stretch: { 33 | alignItems: 'stretch', 34 | }, 35 | baseline: { 36 | alignItems: 'baseline', 37 | }, 38 | ['space-evenly']: { 39 | alignItems: 'space-evenly', 40 | }, 41 | ['space-between']: { 42 | alignItems: 'space-between', 43 | }, 44 | ['space-around']: { 45 | alignItems: 'space-around', 46 | }, 47 | }, 48 | justify: { 49 | start: { 50 | justifyContent: 'flex-start', 51 | }, 52 | center: { 53 | justifyContent: 'center', 54 | }, 55 | end: { 56 | justifyContent: 'flex-end', 57 | }, 58 | between: { 59 | justifyContent: 'space-between', 60 | }, 61 | ['space-evenly']: { 62 | justifyContent: 'space-evenly', 63 | }, 64 | ['space-between']: { 65 | justifyContent: 'space-between', 66 | }, 67 | ['space-around']: { 68 | justifyContent: 'space-around', 69 | }, 70 | }, 71 | wrap: { 72 | noWrap: { 73 | flexWrap: 'nowrap', 74 | }, 75 | wrap: { 76 | flexWrap: 'wrap', 77 | }, 78 | wrapReverse: { 79 | flexWrap: 'wrap-reverse', 80 | }, 81 | }, 82 | gap: { 83 | 1: { 84 | gap: '$1', 85 | }, 86 | 2: { 87 | gap: '$2', 88 | }, 89 | 3: { 90 | gap: '$3', 91 | }, 92 | 4: { 93 | gap: '$4', 94 | }, 95 | 5: { 96 | gap: '$5', 97 | }, 98 | 6: { 99 | gap: '$6', 100 | }, 101 | 7: { 102 | gap: '$7', 103 | }, 104 | 8: { 105 | gap: '$8', 106 | }, 107 | 9: { 108 | gap: '$9', 109 | }, 110 | }, 111 | }, 112 | defaultVariants: { 113 | direction: 'row', 114 | align: 'stretch', 115 | justify: 'start', 116 | wrap: 'noWrap', 117 | }, 118 | }); 119 | -------------------------------------------------------------------------------- /components/Flex/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Flex'; 2 | -------------------------------------------------------------------------------- /components/Grid/Grid.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { Box } from '../Box'; 5 | import { Text } from '../Text'; 6 | import { Grid } from './Grid'; 7 | 8 | const Component: Meta = { 9 | title: 'Components/Grid', 10 | component: Grid, 11 | }; 12 | 13 | export const Basic: StoryFn = (args) => ( 14 | 15 | 16 | 17 | Traefik Labs develops the world's most popular cloud-native application networking software. 18 | It helps developers and operations teams of all sizes build, deploy and run modern 19 | microservices applications quickly and easily. 20 | 21 | 22 | 23 | 24 | Traefik Labs develops the world's most popular cloud-native application networking software. 25 | It helps developers and operations teams of all sizes build, deploy and run modern 26 | microservices applications quickly and easily. 27 | 28 | 29 | 30 | 31 | Traefik Labs develops the world's most popular cloud-native application networking software. 32 | It helps developers and operations teams of all sizes build, deploy and run modern 33 | microservices applications quickly and easily. 34 | 35 | 36 | 37 | ); 38 | 39 | Basic.args = { 40 | columns: '3', 41 | gap: '6', 42 | }; 43 | 44 | export default Component; 45 | -------------------------------------------------------------------------------- /components/Grid/Grid.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '../../stitches.config'; 2 | 3 | export const Grid = styled('div', { 4 | boxSizing: 'border-box', 5 | display: 'grid', 6 | 7 | variants: { 8 | align: { 9 | start: { 10 | alignItems: 'start', 11 | }, 12 | center: { 13 | alignItems: 'center', 14 | }, 15 | end: { 16 | alignItems: 'end', 17 | }, 18 | stretch: { 19 | alignItems: 'stretch', 20 | }, 21 | baseline: { 22 | alignItems: 'baseline', 23 | }, 24 | }, 25 | justify: { 26 | start: { 27 | justifyContent: 'start', 28 | }, 29 | center: { 30 | justifyContent: 'center', 31 | }, 32 | end: { 33 | justifyContent: 'end', 34 | }, 35 | between: { 36 | justifyContent: 'space-between', 37 | }, 38 | }, 39 | flow: { 40 | row: { 41 | gridAutoFlow: 'row', 42 | }, 43 | column: { 44 | gridAutoFlow: 'column', 45 | }, 46 | dense: { 47 | gridAutoFlow: 'dense', 48 | }, 49 | rowDense: { 50 | gridAutoFlow: 'row dense', 51 | }, 52 | columnDense: { 53 | gridAutoFlow: 'column dense', 54 | }, 55 | }, 56 | columns: { 57 | 1: { 58 | gridTemplateColumns: 'repeat(1, 1fr)', 59 | }, 60 | 2: { 61 | gridTemplateColumns: 'repeat(2, 1fr)', 62 | }, 63 | 3: { 64 | gridTemplateColumns: 'repeat(3, 1fr)', 65 | }, 66 | 4: { 67 | gridTemplateColumns: 'repeat(4, 1fr)', 68 | }, 69 | }, 70 | gap: { 71 | 1: { 72 | gap: '$1', 73 | }, 74 | 2: { 75 | gap: '$2', 76 | }, 77 | 3: { 78 | gap: '$3', 79 | }, 80 | 4: { 81 | gap: '$4', 82 | }, 83 | 5: { 84 | gap: '$5', 85 | }, 86 | 6: { 87 | gap: '$6', 88 | }, 89 | 7: { 90 | gap: '$7', 91 | }, 92 | 8: { 93 | gap: '$8', 94 | }, 95 | 9: { 96 | gap: '$9', 97 | }, 98 | }, 99 | gapX: { 100 | 1: { 101 | columnGap: '$1', 102 | }, 103 | 2: { 104 | columnGap: '$2', 105 | }, 106 | 3: { 107 | columnGap: '$3', 108 | }, 109 | 4: { 110 | columnGap: '$4', 111 | }, 112 | 5: { 113 | columnGap: '$5', 114 | }, 115 | 6: { 116 | columnGap: '$6', 117 | }, 118 | 7: { 119 | columnGap: '$7', 120 | }, 121 | 8: { 122 | columnGap: '$8', 123 | }, 124 | 9: { 125 | columnGap: '$9', 126 | }, 127 | }, 128 | gapY: { 129 | 1: { 130 | rowGap: '$1', 131 | }, 132 | 2: { 133 | rowGap: '$2', 134 | }, 135 | 3: { 136 | rowGap: '$3', 137 | }, 138 | 4: { 139 | rowGap: '$4', 140 | }, 141 | 5: { 142 | rowGap: '$5', 143 | }, 144 | 6: { 145 | rowGap: '$6', 146 | }, 147 | 7: { 148 | rowGap: '$7', 149 | }, 150 | 8: { 151 | rowGap: '$8', 152 | }, 153 | 9: { 154 | rowGap: '$9', 155 | }, 156 | }, 157 | }, 158 | }); 159 | -------------------------------------------------------------------------------- /components/Grid/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Grid'; 2 | -------------------------------------------------------------------------------- /components/Heading/Heading.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { Flex } from '../Flex'; 5 | import { H1, H2, H3, H4, H5, H6 } from './Heading'; 6 | 7 | const Component: Meta = { 8 | title: 'Components/Heading', 9 | component: H1, 10 | }; 11 | 12 | const Template: StoryFn = (args) => ( 13 | 14 |

Heading level 1

15 |

Heading level 2

16 |

Heading level 3

17 |

Heading level 4

18 |
Heading level 5
19 |
Heading level 6
20 |
21 | ); 22 | 23 | export const Basic: StoryFn = Template.bind({}); 24 | 25 | Basic.args = {}; 26 | Basic.argTypes = { 27 | transform: { 28 | options: ['uppercase', 'capitalize', 'capitalizeWords'], 29 | control: 'inline-radio', 30 | }, 31 | }; 32 | 33 | export const Transform: StoryFn = (args) => ( 34 | 35 |

heading level 1 default

36 |

37 | heading level 1 uppercase 38 |

39 |

40 | heading level 1 capitalize 41 |

42 |

43 | heading level 1 capitalize each word 44 |

45 |
46 | ); 47 | 48 | export default Component; 49 | -------------------------------------------------------------------------------- /components/Heading/Heading.test.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | 3 | import { H1, H2, H3, H4, H5, H6 } from './Heading'; 4 | 5 | const HEADINGS = [H1, H2, H3, H4, H5, H6]; 6 | 7 | describe('Heading', () => { 8 | it.each(HEADINGS)('should render heading with proper role', (HeadingComponent) => { 9 | const { getByRole } = render(Heading); 10 | 11 | expect(getByRole('heading')).toBeInTheDocument(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /components/Heading/Heading.themes.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '@stitches/react/types/css'; 2 | import tinycolor from 'tinycolor2'; 3 | 4 | import { ColorInfo } from '../../utils/getPrimaryColorInfo'; 5 | 6 | export namespace Theme { 7 | type Colors = { 8 | headingDefault: Property.Color; 9 | }; 10 | 11 | type Factory = (primaryColor: ColorInfo) => Colors; 12 | 13 | export const getLight: Factory = () => ({ 14 | headingDefault: tinycolor('black').setAlpha(0.74).toHslString(), 15 | }); 16 | 17 | export const getDark: Factory = () => ({ 18 | headingDefault: tinycolor('white').setAlpha(0.74).toHslString(), 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /components/Heading/Heading.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '../../stitches.config'; 2 | 3 | const HEADING_BASE_STYLES = { 4 | fontFamily: '$rubik', 5 | fontVariantNumeric: 'proportional-nums', 6 | display: 'block', 7 | lineHeight: '1.25', 8 | fontWeight: '$medium', 9 | margin: 0, 10 | color: '$headingDefault', 11 | variants: { 12 | transform: { 13 | uppercase: { 14 | textTransform: 'uppercase', 15 | }, 16 | capitalize: { 17 | // WARNING: this will only work with block elements (display block/inline-block) 18 | // @see https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter 19 | display: 'block', 20 | '&::first-letter': { 21 | textTransform: 'uppercase', 22 | }, 23 | }, 24 | capitalizeWords: { 25 | textTransform: 'capitalize', 26 | }, 27 | }, 28 | }, 29 | }; 30 | 31 | export const H1 = styled('h1', { 32 | ...HEADING_BASE_STYLES, 33 | fontSize: '$12', 34 | }); 35 | 36 | export const H2 = styled('h2', { 37 | ...HEADING_BASE_STYLES, 38 | fontSize: '$10', 39 | }); 40 | 41 | export const H3 = styled('h3', { 42 | ...HEADING_BASE_STYLES, 43 | fontSize: '$8', 44 | }); 45 | 46 | export const H4 = styled('h4', { 47 | ...HEADING_BASE_STYLES, 48 | fontSize: '$7', 49 | }); 50 | 51 | export const H5 = styled('h5', { 52 | ...HEADING_BASE_STYLES, 53 | fontSize: '$6', 54 | }); 55 | 56 | export const H6 = styled('h6', { 57 | ...HEADING_BASE_STYLES, 58 | fontSize: '$4', 59 | }); 60 | -------------------------------------------------------------------------------- /components/Heading/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Heading'; 2 | -------------------------------------------------------------------------------- /components/IconButton/IconButton.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as Icons from '@radix-ui/react-icons'; 2 | import { Meta, StoryFn } from '@storybook/react'; 3 | import React from 'react'; 4 | 5 | import { Flex } from '../Flex'; 6 | import { IconButton } from './IconButton'; 7 | 8 | const Component: Meta = { 9 | title: 'Components/IconButton', 10 | component: IconButton, 11 | }; 12 | 13 | export const Sizes: StoryFn = (args) => ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | 30 | Sizes.args = { 31 | variant: 'default', 32 | }; 33 | 34 | Sizes.argTypes = { 35 | variant: { 36 | control: 'inline-radio', 37 | options: ['default', 'primary', 'red', 'green', 'orange'], 38 | }, 39 | }; 40 | 41 | export const Variants: StoryFn = (args) => ( 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | ); 63 | 64 | Variants.args = { 65 | size: '3', 66 | }; 67 | 68 | Variants.argTypes = { 69 | size: { 70 | control: 'inline-radio', 71 | options: ['1', '2', '3', '4'], 72 | }, 73 | }; 74 | 75 | export default Component; 76 | -------------------------------------------------------------------------------- /components/IconButton/IconButton.themes.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '@stitches/react/types/css'; 2 | 3 | import { ColorInfo } from '../../utils/getPrimaryColorInfo'; 4 | 5 | export namespace Theme { 6 | type Colors = { 7 | iconButtonBackground: Property.Color; 8 | iconButtonHoverBorder: Property.Color; 9 | iconButtonHoverBackground: Property.Color; 10 | iconButtonFocusBorder: Property.Color; 11 | }; 12 | 13 | type Factory = (primaryColor?: ColorInfo) => Colors; 14 | 15 | export const getLight: Factory = () => ({ 16 | iconButtonBackground: 'white', 17 | iconButtonHoverBorder: '$slate9', 18 | iconButtonHoverBackground: '$slateA3', 19 | iconButtonFocusBorder: '$slate10', 20 | }); 21 | 22 | export const getDark: Factory = () => ({ 23 | iconButtonBackground: '$deepBlue2', 24 | iconButtonHoverBorder: '$deepBlue6', 25 | iconButtonHoverBackground: '$slateA4', 26 | iconButtonFocusBorder: '$deepBlue7', 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /components/IconButton/IconButton.tsx: -------------------------------------------------------------------------------- 1 | import { styled, VariantProps } from '../../stitches.config'; 2 | import { BUTTON_BASE_STYLES } from '../Button'; 3 | 4 | export const IconButton = styled('button', { 5 | ...BUTTON_BASE_STYLES, 6 | // Reset 7 | alignItems: 'center', 8 | display: 'inline-flex', 9 | flexShrink: 0, 10 | fontFamily: 'inherit', 11 | fontSize: '14px', 12 | justifyContent: 'center', 13 | lineHeight: '1', 14 | // Custom 15 | position: 'relative', 16 | padding: '0', 17 | textDecoration: 'none', 18 | backgroundColor: 'transparent', 19 | 20 | '&::before': { 21 | boxSizing: 'border-box', 22 | content: '""', 23 | position: 'absolute', 24 | inset: 0, 25 | borderRadius: 'inherit', 26 | }, 27 | '&::after': { 28 | boxSizing: 'border-box', 29 | content: '""', 30 | position: 'absolute', 31 | inset: 0, 32 | borderRadius: 'inherit', 33 | }, 34 | '@hover': { 35 | '&:hover': { 36 | cursor: 'pointer', 37 | '&::before': { 38 | backgroundColor: 'rgba(255, 255, 255, 0.15)', 39 | }, 40 | '&::after': { 41 | opacity: 0.05, 42 | }, 43 | }, 44 | }, 45 | 46 | '&:focus-visible': { 47 | outline: '2px solid currentColor', 48 | '&::before': { 49 | backgroundColor: 'rgba(255, 255, 255, 0.15)', 50 | }, 51 | '&::after': { 52 | opacity: 0.15, 53 | }, 54 | }, 55 | 56 | '&:active': { 57 | '&::before': { 58 | backgroundColor: 'rgba(0, 0, 0, 0.15)', 59 | }, 60 | }, 61 | 62 | variants: { 63 | size: { 64 | '1': { 65 | borderRadius: '$1', 66 | height: '$5', 67 | width: '$5', 68 | }, 69 | '2': { 70 | borderRadius: '$2', 71 | height: '$6', 72 | width: '$6', 73 | }, 74 | '3': { 75 | borderRadius: '$2', 76 | height: '$7', 77 | width: '$7', 78 | }, 79 | '4': { 80 | borderRadius: '$3', 81 | height: '$8', 82 | width: '$8', 83 | }, 84 | }, 85 | variant: { 86 | default: { 87 | color: '$slate10', 88 | }, 89 | contrast: { 90 | color: '$hiContrast', 91 | }, 92 | primary: { 93 | color: '$primary', 94 | }, 95 | red: { 96 | color: '$red9', 97 | }, 98 | green: { 99 | color: '$green9', 100 | }, 101 | orange: { 102 | color: '$orange9', 103 | }, 104 | }, 105 | }, 106 | defaultVariants: { 107 | variant: 'default', 108 | size: '2', 109 | }, 110 | }); 111 | 112 | export type IconButtonVariants = VariantProps; 113 | -------------------------------------------------------------------------------- /components/IconButton/index.ts: -------------------------------------------------------------------------------- 1 | export * from './IconButton'; 2 | -------------------------------------------------------------------------------- /components/Image/Image.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { Image } from './Image'; 5 | 6 | const Component: Meta = { 7 | title: 'Components/Image', 8 | component: Image, 9 | }; 10 | 11 | const Template: StoryFn = (args) => ; 12 | 13 | export const Basic: StoryFn = Template.bind({}); 14 | 15 | Basic.args = { 16 | src: 'https://picsum.photos/200/300', 17 | }; 18 | 19 | export const Large: StoryFn = Template.bind({}); 20 | 21 | Large.args = { 22 | src: 'https://picsum.photos/2000/3000', 23 | }; 24 | 25 | export default Component; 26 | -------------------------------------------------------------------------------- /components/Image/Image.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '../../stitches.config'; 2 | 3 | export const Image = styled('img', { 4 | // Reset 5 | verticalAlign: 'middle', 6 | maxWidth: '100%', 7 | }); 8 | -------------------------------------------------------------------------------- /components/Image/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Image'; 2 | -------------------------------------------------------------------------------- /components/Input/Input.themes.ts: -------------------------------------------------------------------------------- 1 | import { Property } from '@stitches/react/types/css'; 2 | import tinycolor from 'tinycolor2'; 3 | 4 | import { ColorInfo } from '../../utils/getPrimaryColorInfo'; 5 | 6 | export namespace Theme { 7 | export type Colors = { 8 | inputBg: Property.Color; 9 | inputBorder: Property.Color; 10 | inputFocusBg: Property.Color; 11 | inputFocusBorder: Property.Color; 12 | inputHoverBg: Property.Color; 13 | inputText: Property.Color; 14 | inputPlaceholder: Property.Color; 15 | inputDisabledText: Property.Color; 16 | inputInvalidBorder: Property.Color; 17 | }; 18 | 19 | type Factory = (primaryColor: ColorInfo) => Colors; 20 | 21 | export const getLight: Factory = (primaryColor) => ({ 22 | inputBg: '$deepBlue1', 23 | inputBorder: '$grayBlue9', 24 | inputFocusBg: tinycolor('black').setAlpha(0.15).toHslString(), 25 | inputFocusBorder: primaryColor.helpers.pickScale(8), 26 | inputHoverBg: '$whiteA9', 27 | inputText: tinycolor('black').setAlpha(0.74).toHslString(), 28 | inputPlaceholder: '$blackA10', 29 | inputDisabledText: tinycolor('black').setAlpha(0.35).toHslString(), 30 | inputInvalidBorder: '$red9', 31 | }); 32 | 33 | export const getDark: Factory = (primaryColor) => ({ 34 | inputBg: '$grayBlue7', 35 | inputBorder: '$grayBlue9', 36 | inputFocusBg: tinycolor('black').setAlpha(0.15).toHslString(), 37 | inputFocusBorder: primaryColor.helpers.pickScale(11), 38 | inputHoverBg: '$whiteA4', 39 | inputText: tinycolor('white').setAlpha(0.8).toHslString(), 40 | inputPlaceholder: '$whiteA10', 41 | inputDisabledText: tinycolor('white').setAlpha(0.35).toHslString(), 42 | inputInvalidBorder: '$red9', 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /components/Input/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Input'; 2 | -------------------------------------------------------------------------------- /components/Label/Label.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryFn } from '@storybook/react'; 2 | import React from 'react'; 3 | 4 | import { VariantProps } from '../../stitches.config'; 5 | import ignoreArgType from '../../utils/ignoreArgType'; 6 | import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory'; 7 | import { Box } from '../Box'; 8 | import { Label } from './Label'; 9 | 10 | type LabelVariants = VariantProps; 11 | type LabelProps = LabelVariants & NonNullable; 12 | 13 | const BaseLabel = (props: LabelProps): JSX.Element =>