├── src ├── util │ ├── index.tsx │ ├── cypress-utils.ts │ ├── test-utils.tsx │ ├── storybook-utils.tsx │ └── helpers.tsx ├── index.ts └── theme │ ├── components │ ├── container │ │ ├── container.ts │ │ └── container.stories.tsx │ ├── code │ │ ├── code.ts │ │ └── code.stories.tsx │ ├── form-label │ │ ├── form-label.ts │ │ └── form-label.stories.tsx │ ├── link │ │ ├── link.ts │ │ └── link.stories.tsx │ ├── list │ │ ├── list.ts │ │ └── list.stories.tsx │ ├── divider │ │ ├── divider.ts │ │ └── divider.stories.tsx │ ├── kbd │ │ ├── kbd.ts │ │ └── kbd.stories.tsx │ ├── skip-link │ │ ├── skip-link.ts │ │ └── skip-link.stories.tsx │ ├── spinner │ │ ├── spinner.ts │ │ └── spinner.stories.tsx │ ├── breadcrumb │ │ ├── breadcrumb.ts │ │ └── breadcrumb.stories.tsx │ ├── tooltip │ │ ├── tooltip.ts │ │ └── tooltip.stories.tsx │ ├── heading │ │ ├── heading.ts │ │ └── heading.stories.tsx │ ├── textarea │ │ ├── textarea.ts │ │ └── textarea.stories.tsx │ ├── editable │ │ ├── editable.ts │ │ └── editable.stories.tsx │ ├── form-error │ │ ├── form-error.ts │ │ └── form-error.stories.tsx │ ├── form │ │ ├── form.ts │ │ └── form.stories.tsx │ ├── stat │ │ ├── stat.ts │ │ └── stat.stories.tsx │ ├── pin-input │ │ ├── pin-input.ts │ │ └── pin-input.stories.tsx │ ├── skeleton │ │ ├── skeleton.ts │ │ └── skeleton.stories.tsx │ ├── accordion │ │ ├── accordion.ts │ │ └── accordion.stories.tsx │ ├── close-button │ │ ├── close-button.ts │ │ └── close-button.stories.tsx │ ├── radio │ │ ├── radio.ts │ │ └── radio.stories.tsx │ ├── select │ │ ├── select.stories.tsx │ │ └── select.ts │ ├── avatar │ │ ├── avatar.stories.tsx │ │ └── avatar.ts │ ├── number-input │ │ ├── number-input.stories.tsx │ │ └── number-input.ts │ ├── popover │ │ ├── popover.ts │ │ └── popover.stories.tsx │ ├── index.ts │ ├── menu │ │ ├── menu.ts │ │ └── menu.stories.tsx │ ├── tag │ │ └── tag.ts │ ├── badge │ │ └── badge.ts │ ├── drawer │ │ ├── drawer.ts │ │ └── drawer.stories.tsx │ ├── modal │ │ ├── modal.stories.tsx │ │ └── modal.ts │ ├── switch │ │ ├── switch.ts │ │ └── switch.stories.tsx │ ├── progress │ │ └── progress.ts │ ├── checkbox │ │ └── checkbox.ts │ ├── alert │ │ └── alert.ts │ ├── table │ │ └── table.stories.tsx │ └── button │ │ └── button.ts │ ├── foundations │ ├── colors.stories.tsx │ ├── colors.ts │ └── colors.test.tsx │ ├── index.ts │ └── styles.ts ├── .gitignore ├── cypress.json ├── doc └── images │ ├── plus.png │ ├── chakra-ui-logo.png │ └── radix-ui-logo.png ├── cypress ├── fixtures │ ├── profile.json │ └── example.json ├── tsconfig.json ├── support │ ├── index.js │ └── commands.js ├── integration │ └── components │ │ ├── container.spec.ts │ │ ├── stat.spec.ts │ │ ├── list.spec.ts │ │ ├── breadcrumb.spec.ts │ │ ├── editable.spec.ts │ │ ├── heading.spec.ts │ │ ├── kbd.spec.ts │ │ ├── form-label.spec.ts │ │ ├── pin-input.spec.ts │ │ ├── select.spec.ts │ │ ├── skip-link.spec.ts │ │ ├── spinner.spec.ts │ │ ├── skeleton.spec.ts │ │ ├── divider.spec.ts │ │ ├── link.spec.ts │ │ ├── number-input.spec.ts │ │ ├── modal.spec.ts │ │ ├── tooltip.spec.ts │ │ ├── form-error.spec.ts │ │ ├── form.spec.ts │ │ ├── popover.spec.ts │ │ ├── drawer.spec.ts │ │ ├── accordion.spec.ts │ │ ├── textarea.spec.ts │ │ ├── radio.spec.ts │ │ ├── tag.spec.ts │ │ ├── close-button.spec.ts │ │ ├── menu.spec.ts │ │ ├── avatar.spec.ts │ │ ├── table.spec.ts │ │ └── code.spec.ts └── plugins │ └── index.js ├── babel.config.js ├── release.config.js ├── .storybook ├── preview.js ├── decorators │ └── chakraDecorator.tsx └── main.js ├── .npmignore ├── .github ├── dependabot.yml └── workflows │ ├── push-to-main.yml │ └── pull-on-main.yml ├── LICENSE └── package.json /src/util/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./helpers"; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | coverage/ 4 | out/ -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./theme"; 2 | export * from "./util"; 3 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "video": false, 3 | "screenshotOnRunFailure": false 4 | } 5 | -------------------------------------------------------------------------------- /doc/images/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dub-stack/chakra-radix-colors/HEAD/doc/images/plus.png -------------------------------------------------------------------------------- /cypress/fixtures/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 8739, 3 | "name": "Jane", 4 | "email": "jane@example.com" 5 | } -------------------------------------------------------------------------------- /doc/images/chakra-ui-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dub-stack/chakra-radix-colors/HEAD/doc/images/chakra-ui-logo.png -------------------------------------------------------------------------------- /doc/images/radix-ui-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dub-stack/chakra-radix-colors/HEAD/doc/images/radix-ui-logo.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ["@babel/preset-env", { targets: { node: "current" } }], 4 | "@babel/preset-react", 5 | "@babel/preset-typescript", 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /release.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | branches: ["main"], 3 | plugins: [ 4 | "@semantic-release/commit-analyzer", 5 | "@semantic-release/release-notes-generator", 6 | "@semantic-release/npm", 7 | "@semantic-release/github", 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /src/theme/components/container/container.ts: -------------------------------------------------------------------------------- 1 | import type { SystemStyleObject } from "@chakra-ui/theme-tools"; 2 | 3 | const baseStyle: SystemStyleObject = { 4 | w: "100%", 5 | mx: "auto", 6 | maxW: "60ch", 7 | px: "1rem", 8 | }; 9 | 10 | export default { 11 | baseStyle, 12 | }; 13 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import { chakraDecorator } from "./decorators/chakraDecorator"; 2 | 3 | export const parameters = { 4 | actions: { argTypesRegex: "^on[A-Z].*" }, 5 | controls: { 6 | matchers: { 7 | color: /(background|color)$/i, 8 | date: /Date$/, 9 | }, 10 | }, 11 | }; 12 | 13 | // export const decorators = [chakraDecorator]; 14 | -------------------------------------------------------------------------------- /.storybook/decorators/chakraDecorator.tsx: -------------------------------------------------------------------------------- 1 | import { ChakraProvider, ColorModeScript } from "@chakra-ui/react"; 2 | import theme from "../../src/theme"; 3 | 4 | export const chakraDecorator = (Story) => ( 5 | <> 6 | 7 | 8 | 9 | 10 | > 11 | ); 12 | -------------------------------------------------------------------------------- /cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es5", "dom"], 5 | "types": ["cypress", "cypress-real-events"], 6 | "baseUrl": "..", 7 | "paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */ 8 | "*": ["src/*"] 9 | } 10 | }, 11 | "include": ["**/*.ts"] 12 | } -------------------------------------------------------------------------------- /src/theme/components/code/code.ts: -------------------------------------------------------------------------------- 1 | import type { SystemStyleObject } from "@chakra-ui/theme-tools"; 2 | import Badge from "../badge/badge"; 3 | 4 | const { variants, defaultProps } = Badge; 5 | 6 | const baseStyle: SystemStyleObject = { 7 | fontFamily: "mono", 8 | fontSize: "sm", 9 | px: "0.2em", 10 | borderRadius: "sm", 11 | }; 12 | 13 | export default { 14 | baseStyle, 15 | variants, 16 | defaultProps, 17 | }; 18 | -------------------------------------------------------------------------------- /src/theme/components/form-label/form-label.ts: -------------------------------------------------------------------------------- 1 | import type { SystemStyleObject } from "@chakra-ui/theme-tools"; 2 | 3 | const baseStyle: SystemStyleObject = { 4 | fontSize: "md", 5 | marginEnd: 3, 6 | mb: 2, 7 | fontWeight: "medium", 8 | transitionProperty: "common", 9 | transitionDuration: "normal", 10 | opacity: 1, 11 | _disabled: { 12 | opacity: 0.4, 13 | }, 14 | }; 15 | 16 | export default { 17 | baseStyle, 18 | }; 19 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | !package.json 2 | !LICENSE 3 | 4 | # ignore all test and story files 5 | dist/**/*.test.{ts,tsx,js,jsx} 6 | dist/**/*.stories.{ts,tsx,js,jsx} 7 | dist/util/test-utils.tsx 8 | .storybook/ 9 | coverage/ 10 | cypress/ 11 | cypress.json 12 | .github/ 13 | 14 | # ignore all config files 15 | *.config.{ts,tsx,js,jsx} 16 | tsconfig.json 17 | 18 | # ignore all source files 19 | src/ 20 | 21 | # ignore npm package files 22 | out/ 23 | 24 | # ignore documentation 25 | doc/ -------------------------------------------------------------------------------- /src/theme/components/link/link.ts: -------------------------------------------------------------------------------- 1 | import type { SystemStyleObject } from "@chakra-ui/theme-tools"; 2 | 3 | const baseStyle: SystemStyleObject = { 4 | transitionProperty: "common", 5 | transitionDuration: "fast", 6 | transitionTimingFunction: "ease-out", 7 | cursor: "pointer", 8 | textDecoration: "none", 9 | outline: "none", 10 | color: "inherit", 11 | _hover: { 12 | textDecoration: "underline", 13 | }, 14 | _focus: { 15 | boxShadow: "outline", 16 | }, 17 | }; 18 | 19 | export default { 20 | baseStyle, 21 | }; 22 | -------------------------------------------------------------------------------- /src/theme/components/list/list.ts: -------------------------------------------------------------------------------- 1 | import { listAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleObject, 4 | SystemStyleObject, 5 | } from "@chakra-ui/theme-tools"; 6 | 7 | const baseStyleIcon: SystemStyleObject = { 8 | marginEnd: "0.5rem", 9 | display: "inline", 10 | verticalAlign: "text-bottom", 11 | }; 12 | 13 | const baseStyle: PartsStyleObject = { 14 | container: {}, 15 | item: {}, 16 | icon: baseStyleIcon, 17 | }; 18 | 19 | export default { 20 | parts: parts.keys, 21 | baseStyle, 22 | }; 23 | -------------------------------------------------------------------------------- /src/theme/components/divider/divider.ts: -------------------------------------------------------------------------------- 1 | import type { SystemStyleObject } from "@chakra-ui/theme-tools"; 2 | 3 | const baseStyle: SystemStyleObject = { 4 | opacity: 0.6, 5 | borderColor: "inherit", 6 | }; 7 | 8 | const variantSolid: SystemStyleObject = { 9 | borderStyle: "solid", 10 | }; 11 | 12 | const variantDashed: SystemStyleObject = { 13 | borderStyle: "dashed", 14 | }; 15 | 16 | const variants = { 17 | solid: variantSolid, 18 | dashed: variantDashed, 19 | }; 20 | 21 | const defaultProps = { 22 | variant: "solid", 23 | }; 24 | 25 | export default { 26 | baseStyle, 27 | variants, 28 | defaultProps, 29 | }; 30 | -------------------------------------------------------------------------------- /src/theme/foundations/colors.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { Box } from "@chakra-ui/react"; 4 | import { Decorators } from "util/storybook-utils"; 5 | 6 | export default { 7 | title: "Colors", 8 | component: Box, 9 | } as ComponentMeta; 10 | 11 | const New = ; 12 | const Default = ; 13 | 14 | const Template: ComponentStory = (args) => { 15 | return ; 16 | }; 17 | 18 | export const Primary = Template.bind({}); 19 | -------------------------------------------------------------------------------- /src/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { extendTheme, Theme } from "@chakra-ui/react"; 2 | 3 | // Global style overrides 4 | import styles from "./styles"; 5 | 6 | // Foundational style overrides 7 | import colors, { RadixColorsType } from "./foundations/colors"; 8 | 9 | // Component style overrides 10 | import * as components from "./components"; 11 | 12 | // create the theme 13 | const theme = extendTheme({ 14 | colors: { ...colors }, 15 | components: { ...components }, 16 | styles: { ...styles }, 17 | }) as unknown as Omit & { colors: RadixColorsType }; 18 | theme.colors = colors; // remove the default @chakra-ui colors 19 | 20 | // export the theme 21 | export { theme }; 22 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | 6 | # update dependencies on sunday at 9:00AM CST 7 | schedule: 8 | interval: "weekly" 9 | day: "sunday" 10 | time: "09:00" 11 | timezone: "America/Chicago" 12 | 13 | # keep all dependencies up-to-date 14 | allow: 15 | - dependency-type: "all" 16 | 17 | # see how this plays with mergify, don't need to update the 18 | # package and trigger release with only a dev-deps update 19 | commit-message: 20 | prefix: "chore" 21 | prefix-development: "chore" 22 | include: "scope" 23 | 24 | labels: 25 | - "dependency" 26 | -------------------------------------------------------------------------------- /.github/workflows/push-to-main.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: [main] 5 | 6 | jobs: 7 | # PUBLISH_TO_NPM: ✅ 8 | # publish-to-npm job exists to publish a new patch or minor version to NPM. 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 14 17 | - run: npm install 18 | - run: npm run build 19 | - name: Semantic Release 20 | uses: cycjimmy/semantic-release-action@v2 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 24 | -------------------------------------------------------------------------------- /src/theme/components/kbd/kbd.ts: -------------------------------------------------------------------------------- 1 | import type { SystemStyleFunction } from "@chakra-ui/theme-tools"; 2 | import { mode } from "@chakra-ui/theme-tools"; 3 | import { getColorInfo } from "../../../util/helpers"; 4 | 5 | const baseStyle: SystemStyleFunction = (props) => { 6 | const { theme } = props; 7 | const { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 8 | 9 | return { 10 | bg: mode(`${_gray}.3`, `${_grayDark}.3`)(props), 11 | borderRadius: "md", 12 | borderWidth: "1px", 13 | borderBottomWidth: "3px", 14 | fontSize: "0.8em", 15 | fontWeight: "bold", 16 | lineHeight: "normal", 17 | px: "0.4em", 18 | whiteSpace: "nowrap", 19 | }; 20 | }; 21 | 22 | export default { 23 | baseStyle, 24 | }; 25 | -------------------------------------------------------------------------------- /src/theme/components/skip-link/skip-link.ts: -------------------------------------------------------------------------------- 1 | import type { SystemStyleFunction } from "@chakra-ui/theme-tools"; 2 | import { mode } from "@chakra-ui/theme-tools"; 3 | import { getColorInfo } from "../../../util/helpers"; 4 | 5 | const baseStyle: SystemStyleFunction = (props) => { 6 | const { theme } = props; 7 | const { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 8 | 9 | return { 10 | borderRadius: "md", 11 | fontWeight: "semibold", 12 | _focus: { 13 | boxShadow: "outline", 14 | padding: "1rem", 15 | position: "fixed", 16 | top: "1.5rem", 17 | insetStart: "1.5rem", 18 | bg: mode(`${_gray}.1`, `${_grayDark}.1`)(props), 19 | }, 20 | }; 21 | }; 22 | 23 | export default { 24 | baseStyle, 25 | }; 26 | -------------------------------------------------------------------------------- /src/theme/components/spinner/spinner.ts: -------------------------------------------------------------------------------- 1 | import { cssVar, SystemStyleObject } from "@chakra-ui/theme-tools"; 2 | 3 | const $size = cssVar("spinner-size"); 4 | 5 | const baseStyle: SystemStyleObject = { 6 | width: [$size.reference], 7 | height: [$size.reference], 8 | }; 9 | 10 | const sizes: Record = { 11 | xs: { 12 | [$size.variable]: "0.75rem", 13 | }, 14 | sm: { 15 | [$size.variable]: "1rem", 16 | }, 17 | md: { 18 | [$size.variable]: "1.5rem", 19 | }, 20 | lg: { 21 | [$size.variable]: "2rem", 22 | }, 23 | xl: { 24 | [$size.variable]: "3rem", 25 | }, 26 | }; 27 | 28 | const defaultProps = { 29 | size: "md", 30 | }; 31 | 32 | export default { 33 | baseStyle, 34 | sizes, 35 | defaultProps, 36 | }; 37 | -------------------------------------------------------------------------------- /src/theme/components/breadcrumb/breadcrumb.ts: -------------------------------------------------------------------------------- 1 | import { breadcrumbAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleObject, 4 | SystemStyleObject, 5 | } from "@chakra-ui/theme-tools"; 6 | 7 | const baseStyleLink: SystemStyleObject = { 8 | transitionProperty: "common", 9 | transitionDuration: "fast", 10 | transitionTimingFunction: "ease-out", 11 | cursor: "pointer", 12 | textDecoration: "none", 13 | outline: "none", 14 | color: "inherit", 15 | _hover: { 16 | textDecoration: "underline", 17 | }, 18 | _focus: { 19 | boxShadow: "outline", 20 | }, 21 | }; 22 | 23 | const baseStyle: PartsStyleObject = { 24 | link: baseStyleLink, 25 | }; 26 | 27 | export default { 28 | parts: parts.keys, 29 | baseStyle, 30 | }; 31 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import "./commands"; 18 | import "cypress-real-events/support"; 19 | 20 | // Alternatively you can use CommonJS syntax: 21 | // require('./commands') 22 | -------------------------------------------------------------------------------- /cypress/integration/components/container.spec.ts: -------------------------------------------------------------------------------- 1 | describe("Default", () => { 2 | beforeEach(() => { 3 | cy.visit( 4 | "http://localhost:6006/iframe.html?id=components-container--default&args=&viewMode=story" 5 | ); 6 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 7 | }); 8 | 9 | it("displays in light mode correctly", () => { 10 | // no theme changes, but make sure this mounts correctly 11 | cy.get("[data-testid=NewDefault] .chakra-container"); 12 | }); 13 | 14 | it("displays in dark mode correctly", () => { 15 | cy.get("#switch-color-mode").click(); 16 | 17 | // no theme changes, but make sure this mounts correctly 18 | cy.get("[data-testid=NewDefault] .chakra-container"); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/theme/styles.ts: -------------------------------------------------------------------------------- 1 | import { mode, StyleFunctionProps, Styles } from "@chakra-ui/theme-tools"; 2 | 3 | export function globalStyles(props: StyleFunctionProps) { 4 | return { 5 | body: { 6 | fontFamily: "body", 7 | color: mode("_gray.12", "_grayDark.12")(props), 8 | bg: mode("_gray.1", "_grayDark.1")(props), 9 | transitionProperty: "background-color", 10 | transisionDuration: "normal", 11 | lineHeight: "base", 12 | }, 13 | "*::placeholder": { 14 | color: mode("_gray.8", "_grayDark.8")(props), 15 | }, 16 | "*, *::before, &::after": { 17 | borderColor: mode("_gray.6", "_grayDark.6")(props), 18 | wordWrap: "break-word", 19 | }, 20 | }; 21 | } 22 | 23 | const styles: Styles = { 24 | global: globalStyles, 25 | }; 26 | 27 | export default styles; 28 | -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | require("tsconfig-paths").register(); 15 | 16 | /** 17 | * @type {Cypress.PluginConfig} 18 | */ 19 | // eslint-disable-next-line no-unused-vars 20 | module.exports = (on, config) => { 21 | // `on` is used to hook into various events Cypress emits 22 | // `config` is the resolved Cypress config 23 | }; 24 | -------------------------------------------------------------------------------- /cypress/integration/components/stat.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-stat--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | const _2em = "24px"; 13 | cy.get("[data-testid=NewDefault] .chakra-stat__number").should( 14 | "have.css", 15 | "font-size", 16 | _2em 17 | ); 18 | }); 19 | 20 | it("displays in dark mode correctly", () => { 21 | const _2em = "24px"; 22 | cy.get("[data-testid=NewDefault] .chakra-stat__number").should( 23 | "have.css", 24 | "font-size", 25 | _2em 26 | ); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /cypress/integration/components/list.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-list--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] .chakra-icon").should( 13 | "have.css", 14 | "color", 15 | getRgbFromThemeColor("_gray.12") 16 | ); 17 | }); 18 | 19 | it("displays in dark mode correctly", () => { 20 | cy.get("#switch-color-mode").click(); 21 | cy.get("[data-testid=NewDefault] .chakra-icon").should( 22 | "have.css", 23 | "color", 24 | getRgbFromThemeColor("_grayDark.12") 25 | ); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/theme/components/tooltip/tooltip.ts: -------------------------------------------------------------------------------- 1 | import { mode, cssVar, SystemStyleFunction } from "@chakra-ui/theme-tools"; 2 | import { getColorInfo } from "../../../util/helpers"; 3 | 4 | const $bg = cssVar("tooltip-bg"); 5 | const $arrowBg = cssVar("popper-arrow-bg"); 6 | 7 | const baseStyle: SystemStyleFunction = (props) => { 8 | const { theme } = props; 9 | const { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 10 | 11 | const bg = mode(`${_grayDark}.2`, `${_gray}.2`)(props); 12 | return { 13 | [$bg.variable]: `colors.${bg}`, 14 | px: "8px", 15 | py: "2px", 16 | bg: [$bg.reference], 17 | [$arrowBg.variable]: [$bg.reference], 18 | color: mode(`${_gray}.1`, `${_grayDark}.1`)(props), 19 | borderRadius: "sm", 20 | fontWeight: "medium", 21 | fontSize: "sm", 22 | boxShadow: "md", 23 | maxW: "320px", 24 | zIndex: "tooltip", 25 | }; 26 | }; 27 | 28 | export default { 29 | baseStyle, 30 | }; 31 | -------------------------------------------------------------------------------- /cypress/integration/components/breadcrumb.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-breadcrumb--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] .chakra-breadcrumb__list-item") 13 | .eq(0) 14 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")); 15 | }); 16 | it("displays in dark mode correctly", () => { 17 | cy.get("#switch-color-mode").click(); 18 | cy.get("[data-testid=NewDefault] .chakra-breadcrumb__list-item") 19 | .eq(0) 20 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /cypress/integration/components/editable.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-editable--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] .chakra-editable").should( 13 | "have.css", 14 | "color", 15 | getRgbFromThemeColor("_gray.12") 16 | ); 17 | }); 18 | 19 | it("displays in dark mode correctly", () => { 20 | cy.get("#switch-color-mode").click(); 21 | cy.get("[data-testid=NewDefault] .chakra-editable").should( 22 | "have.css", 23 | "color", 24 | getRgbFromThemeColor("_grayDark.12") 25 | ); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /cypress/integration/components/heading.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-heading--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] .chakra-heading").should( 13 | "have.css", 14 | "color", 15 | getRgbFromThemeColor("_gray.12") 16 | ); 17 | }); 18 | 19 | it("displays in dark mode correctly", () => { 20 | cy.get("#switch-color-mode").click(); 21 | cy.get("[data-testid=NewDefault] .chakra-heading").should( 22 | "have.css", 23 | "color", 24 | getRgbFromThemeColor("_grayDark.12") 25 | ); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /cypress/integration/components/kbd.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-kbd--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] .chakra-kbd").should( 13 | "have.css", 14 | "background-color", 15 | getRgbFromThemeColor("_gray.3") 16 | ); 17 | }); 18 | 19 | it("displays in dark mode correctly", () => { 20 | cy.get("#switch-color-mode").click(); 21 | cy.get("[data-testid=NewDefault] .chakra-kbd").should( 22 | "have.css", 23 | "background-color", 24 | getRgbFromThemeColor("_grayDark.3") 25 | ); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /cypress/integration/components/form-label.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-formlabel--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] .chakra-form__label").should( 13 | "have.css", 14 | "color", 15 | getRgbFromThemeColor("_gray.12") 16 | ); 17 | }); 18 | 19 | it("displays in dark mode correctly", () => { 20 | cy.get("#switch-color-mode").click(); 21 | cy.get("[data-testid=NewDefault] .chakra-form__label").should( 22 | "have.css", 23 | "color", 24 | getRgbFromThemeColor("_grayDark.12") 25 | ); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/util/cypress-utils.ts: -------------------------------------------------------------------------------- 1 | import { getColor } from "@chakra-ui/theme-tools"; 2 | import { theme } from "../theme"; 3 | import { hsl, hex } from "color-convert"; 4 | 5 | /** 6 | * Turns an HSL string into an array of the h, s, l values. 7 | * 8 | * @example normalizeHslString("hsl(360, 97.9%, 94.8%)") -> [360, 97.9, 94.8] 9 | * 10 | * @param hsl An hsl string 11 | * @returns 12 | */ 13 | function normalizeHslString(hsl: string) { 14 | let [h, s, l] = hsl 15 | .replace(/[^\d,.]/g, "") 16 | .split(",") 17 | .map((item) => parseFloat(item)); 18 | 19 | return [h, s, l]; 20 | } 21 | 22 | export function getRgbFromThemeColor(color: string) { 23 | const c = getColor(theme, color); 24 | let [h, s, l] = normalizeHslString(c); 25 | let [r, g, b] = hsl.rgb([h, s, l]); 26 | return `rgb(${r}, ${g}, ${b})`; 27 | } 28 | 29 | export function getRgbFromHex(color: string) { 30 | let [r, g, b] = hex.rgb(color); 31 | return `rgb(${r}, ${g}, ${b})`; 32 | } 33 | -------------------------------------------------------------------------------- /cypress/integration/components/pin-input.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-pininput--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] .chakra-pin-input").should( 13 | "have.css", 14 | "border-color", 15 | getRgbFromThemeColor("_gray.6") 16 | ); 17 | }); 18 | 19 | it("displays in dark mode correctly", () => { 20 | cy.get("#switch-color-mode").click(); 21 | cy.get("[data-testid=NewDefault] .chakra-pin-input").should( 22 | "have.css", 23 | "border-color", 24 | getRgbFromThemeColor("_grayDark.6") 25 | ); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /cypress/integration/components/select.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-select--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] .chakra-select").should( 13 | "have.css", 14 | "background-color", 15 | getRgbFromThemeColor("_gray.3") 16 | ); 17 | }); 18 | 19 | it("displays in dark mode correctly", () => { 20 | cy.get("#switch-color-mode").click(); 21 | cy.get("[data-testid=NewDefault] .chakra-select").should( 22 | "have.css", 23 | "background-color", 24 | getRgbFromThemeColor("_grayDark.3") 25 | ); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /cypress/integration/components/skip-link.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-skipnavlink--default&args=&viewMode=story#chakra-skip-nav" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] a") 13 | .focus() 14 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.1")); 15 | }); 16 | 17 | it("displays in dark mode correctly", () => { 18 | cy.get("#switch-color-mode").click(); 19 | cy.get("[data-testid=NewDefault] a") 20 | .focus() 21 | .should( 22 | "have.css", 23 | "background-color", 24 | getRgbFromThemeColor("_grayDark.1") 25 | ); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/theme/components/heading/heading.ts: -------------------------------------------------------------------------------- 1 | import { SystemStyleObject } from "@chakra-ui/theme-tools"; 2 | 3 | const baseStyle: SystemStyleObject = { 4 | fontFamily: "heading", 5 | fontWeight: "bold", 6 | }; 7 | 8 | const sizes: Record = { 9 | "4xl": { 10 | fontSize: ["6xl", null, "7xl"], 11 | lineHeight: 1, 12 | }, 13 | "3xl": { 14 | fontSize: ["5xl", null, "6xl"], 15 | lineHeight: 1, 16 | }, 17 | "2xl": { 18 | fontSize: ["4xl", null, "5xl"], 19 | lineHeight: [1.2, null, 1], 20 | }, 21 | xl: { 22 | fontSize: ["3xl", null, "4xl"], 23 | lineHeight: [1.33, null, 1.2], 24 | }, 25 | lg: { 26 | fontSize: ["2xl", null, "3xl"], 27 | lineHeight: [1.33, null, 1.2], 28 | }, 29 | md: { fontSize: "xl", lineHeight: 1.2 }, 30 | sm: { fontSize: "md", lineHeight: 1.2 }, 31 | xs: { fontSize: "sm", lineHeight: 1.2 }, 32 | }; 33 | 34 | const defaultProps = { 35 | size: "xl", 36 | }; 37 | 38 | export default { 39 | baseStyle, 40 | sizes, 41 | defaultProps, 42 | }; 43 | -------------------------------------------------------------------------------- /cypress/integration/components/spinner.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-spinner--default&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | const c = getRgbFromThemeColor("_gray.12"); 13 | cy.get("[data-testid=NewDefault] .chakra-spinner").should( 14 | "have.css", 15 | "border-color", 16 | `${c} ${c} rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)` 17 | ); 18 | }); 19 | 20 | it("displays in dark mode correctly", () => { 21 | cy.get("#switch-color-mode").click(); 22 | 23 | const c = getRgbFromThemeColor("_grayDark.12"); 24 | cy.get("[data-testid=NewDefault] .chakra-spinner").should( 25 | "have.css", 26 | "border-color", 27 | `${c} ${c} rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)` 28 | ); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /cypress/integration/components/skeleton.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-skeleton--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | const animation = 13 | "0.8s linear 0s infinite alternate none running animation-12xlxsq"; 14 | cy.get("[data-testid=NewDefault] .chakra-skeleton") 15 | .eq(0) 16 | .should("have.css", "animation", animation); 17 | }); 18 | 19 | it("displays in dark mode correctly", () => { 20 | cy.get("#switch-color-mode").click(); 21 | 22 | const animation = 23 | "0.8s linear 0s infinite alternate none running animation-t2xdlo"; 24 | cy.get("[data-testid=NewDefault] .chakra-skeleton") 25 | .eq(0) 26 | .should("have.css", "animation", animation); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/theme/components/textarea/textarea.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | SystemStyleInterpolation, 3 | SystemStyleObject, 4 | } from "@chakra-ui/theme-tools"; 5 | import Input from "../input/input"; 6 | 7 | const baseStyle: SystemStyleObject = { 8 | ...Input.baseStyle.field, 9 | paddingY: "8px", 10 | minHeight: "80px", 11 | lineHeight: "short", 12 | verticalAlign: "top", 13 | }; 14 | 15 | const variants: Record = { 16 | outline: (props) => Input.variants.outline(props).field ?? {}, 17 | flushed: (props) => Input.variants.flushed(props).field ?? {}, 18 | filled: (props) => Input.variants.filled(props).field ?? {}, 19 | unstyled: Input.variants.unstyled.field ?? {}, 20 | }; 21 | 22 | const sizes: Record = { 23 | xs: Input.sizes.xs.field ?? {}, 24 | sm: Input.sizes.sm.field ?? {}, 25 | md: Input.sizes.md.field ?? {}, 26 | lg: Input.sizes.lg.field ?? {}, 27 | }; 28 | 29 | const defaultProps = { 30 | size: "md", 31 | variant: "outline", 32 | }; 33 | 34 | export default { 35 | baseStyle, 36 | sizes, 37 | variants, 38 | defaultProps, 39 | }; 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 dub-stack 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cypress/integration/components/divider.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-divider--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] hr") 13 | .eq(0) 14 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")); 15 | cy.get("[data-testid=NewDefault] hr") 16 | .eq(1) 17 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")); 18 | }); 19 | 20 | it("displays in dark mode correctly", () => { 21 | cy.get("#switch-color-mode").click(); 22 | cy.get("[data-testid=NewDefault] hr") 23 | .eq(0) 24 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")); 25 | cy.get("[data-testid=NewDefault] hr") 26 | .eq(1) 27 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/theme/components/editable/editable.ts: -------------------------------------------------------------------------------- 1 | import { editableAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleObject, 4 | SystemStyleObject, 5 | } from "@chakra-ui/theme-tools"; 6 | 7 | const baseStylePreview: SystemStyleObject = { 8 | borderRadius: "md", 9 | py: "3px", 10 | transitionProperty: "common", 11 | transitionDuration: "normal", 12 | }; 13 | 14 | const baseStyleInput: SystemStyleObject = { 15 | borderRadius: "md", 16 | py: "3px", 17 | transitionProperty: "common", 18 | transitionDuration: "normal", 19 | width: "full", 20 | _focus: { boxShadow: "outline" }, 21 | _placeholder: { opacity: 0.6 }, 22 | }; 23 | 24 | const baseStyleTextarea: SystemStyleObject = { 25 | borderRadius: "md", 26 | py: "3px", 27 | transitionProperty: "common", 28 | transitionDuration: "normal", 29 | width: "full", 30 | _focus: { boxShadow: "outline" }, 31 | _placeholder: { opacity: 0.6 }, 32 | }; 33 | 34 | const baseStyle: PartsStyleObject = { 35 | preview: baseStylePreview, 36 | input: baseStyleInput, 37 | textarea: baseStyleTextarea, 38 | }; 39 | 40 | export default { 41 | parts: parts.keys, 42 | baseStyle, 43 | }; 44 | -------------------------------------------------------------------------------- /src/theme/components/form-error/form-error.ts: -------------------------------------------------------------------------------- 1 | import { formErrorAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleFunction, 4 | SystemStyleFunction, 5 | } from "@chakra-ui/theme-tools"; 6 | import { mode } from "@chakra-ui/theme-tools"; 7 | import { getColorInfo } from "../../../util/helpers"; 8 | 9 | const baseStyleText: SystemStyleFunction = (props) => { 10 | const { theme } = props; 11 | const { light: redLight, dark: redDark } = getColorInfo("red", theme); 12 | 13 | return { 14 | color: mode(`${redLight}.9`, `${redDark}.9`)(props), 15 | mt: 2, 16 | fontSize: "sm", 17 | lineHeight: "normal", 18 | }; 19 | }; 20 | 21 | const baseStyleIcon: SystemStyleFunction = (props) => { 22 | const { theme } = props; 23 | const { light: redLight, dark: redDark } = getColorInfo("red", theme); 24 | 25 | return { 26 | marginEnd: "0.5em", 27 | color: mode(`${redLight}.9`, `${redDark}.9`)(props), 28 | }; 29 | }; 30 | 31 | const baseStyle: PartsStyleFunction = (props) => ({ 32 | text: baseStyleText(props), 33 | icon: baseStyleIcon(props), 34 | }); 35 | 36 | export default { 37 | parts: parts.keys, 38 | baseStyle, 39 | }; 40 | -------------------------------------------------------------------------------- /src/theme/components/form/form.ts: -------------------------------------------------------------------------------- 1 | import { formAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleFunction, 4 | SystemStyleFunction, 5 | } from "@chakra-ui/theme-tools"; 6 | import { mode } from "@chakra-ui/theme-tools"; 7 | import { getColorInfo } from "../../../util/helpers"; 8 | 9 | const baseStyleRequiredIndicator: SystemStyleFunction = (props) => { 10 | const { theme } = props; 11 | const { light: redLight, dark: redDark } = getColorInfo("red", theme); 12 | return { 13 | marginStart: 1, 14 | color: mode(`${redLight}.9`, `${redDark}.9`)(props), 15 | }; 16 | }; 17 | 18 | const baseStyleHelperText: SystemStyleFunction = (props) => { 19 | const { theme } = props; 20 | const { light, dark } = getColorInfo("_gray", theme); 21 | 22 | return { 23 | mt: 2, 24 | color: mode(`${light}.9`, `${dark}.9`)(props), 25 | lineHeight: "normal", 26 | fontSize: "sm", 27 | }; 28 | }; 29 | 30 | const baseStyle: PartsStyleFunction = (props) => ({ 31 | container: { width: "100%", position: "relative" }, 32 | requiredIndicator: baseStyleRequiredIndicator(props), 33 | helperText: baseStyleHelperText(props), 34 | }); 35 | 36 | export default { 37 | parts: parts.keys, 38 | baseStyle, 39 | }; 40 | -------------------------------------------------------------------------------- /src/theme/components/stat/stat.ts: -------------------------------------------------------------------------------- 1 | import { statAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleObject, 4 | SystemStyleObject, 5 | } from "@chakra-ui/theme-tools"; 6 | 7 | const baseStyleLabel: SystemStyleObject = { 8 | fontWeight: "medium", 9 | }; 10 | 11 | const baseStyleHelpText: SystemStyleObject = { 12 | opacity: 0.8, 13 | marginBottom: 2, 14 | }; 15 | 16 | const baseStyleNumber: SystemStyleObject = { 17 | verticalAlign: "baseline", 18 | fontWeight: "semibold", 19 | }; 20 | 21 | const baseStyleIcon: SystemStyleObject = { 22 | marginEnd: 1, 23 | w: "14px", 24 | h: "14px", 25 | verticalAlign: "middle", 26 | }; 27 | 28 | const baseStyle: PartsStyleObject = { 29 | container: {}, 30 | label: baseStyleLabel, 31 | helpText: baseStyleHelpText, 32 | number: baseStyleNumber, 33 | icon: baseStyleIcon, 34 | }; 35 | 36 | const sizes: Record> = { 37 | md: { 38 | label: { fontSize: "sm" }, 39 | helpText: { fontSize: "sm" }, 40 | number: { fontSize: "2xl" }, 41 | }, 42 | }; 43 | 44 | const defaultProps = { 45 | size: "md", 46 | }; 47 | 48 | export default { 49 | parts: parts.keys, 50 | baseStyle, 51 | sizes, 52 | defaultProps, 53 | }; 54 | -------------------------------------------------------------------------------- /cypress/integration/components/link.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-link--default&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] .chakra-link") 13 | .realHover() 14 | .wait(200) 15 | .should( 16 | "have.css", 17 | "text-decoration", 18 | `underline solid ${getRgbFromThemeColor("_gray.12")}` 19 | ) 20 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")); 21 | }); 22 | 23 | it("displays in dark mode correctly", () => { 24 | cy.get("#switch-color-mode").click(); 25 | cy.get("[data-testid=NewDefault] .chakra-link") 26 | .realHover() 27 | .wait(200) 28 | .should( 29 | "have.css", 30 | "text-decoration", 31 | `underline solid ${getRgbFromThemeColor("_grayDark.12")}` 32 | ) 33 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/theme/components/pin-input/pin-input.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | SystemStyleInterpolation, 3 | SystemStyleObject, 4 | } from "@chakra-ui/theme-tools"; 5 | import Input from "../input/input"; 6 | 7 | const baseStyle: SystemStyleObject = { 8 | ...Input.baseStyle.field, 9 | textAlign: "center", 10 | }; 11 | 12 | const sizes: Record = { 13 | lg: { 14 | fontSize: "lg", 15 | w: 12, 16 | h: 12, 17 | borderRadius: "md", 18 | }, 19 | md: { 20 | fontSize: "md", 21 | w: 10, 22 | h: 10, 23 | borderRadius: "md", 24 | }, 25 | sm: { 26 | fontSize: "sm", 27 | w: 8, 28 | h: 8, 29 | borderRadius: "sm", 30 | }, 31 | xs: { 32 | fontSize: "xs", 33 | w: 6, 34 | h: 6, 35 | borderRadius: "sm", 36 | }, 37 | }; 38 | 39 | const variants: Record = { 40 | outline: (props) => Input.variants.outline(props).field ?? {}, 41 | flushed: (props) => Input.variants.flushed(props).field ?? {}, 42 | filled: (props) => Input.variants.filled(props).field ?? {}, 43 | unstyled: Input.variants.unstyled.field ?? {}, 44 | }; 45 | 46 | const defaultProps = Input.defaultProps; 47 | 48 | export default { 49 | baseStyle, 50 | sizes, 51 | variants, 52 | defaultProps, 53 | }; 54 | -------------------------------------------------------------------------------- /src/util/test-utils.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactElement } from "react"; 2 | import { render } from "@testing-library/react"; 3 | import { ChakraProvider } from "@chakra-ui/provider"; 4 | import { theme } from "../theme"; 5 | import { RenderOptions } from "@storybook/addons"; 6 | import { matchers } from "@emotion/jest"; 7 | import { Button, useColorMode } from "@chakra-ui/react"; 8 | 9 | const ColorModeButtons = () => { 10 | const { toggleColorMode, setColorMode } = useColorMode(); 11 | return ( 12 | <> 13 | {" "} 14 | setColorMode("light")} 16 | data-testid="reset-color-mode" 17 | /> 18 | > 19 | ); 20 | }; 21 | 22 | const AllTheProviders: FC = ({ children }) => { 23 | return ( 24 | 25 | <> 26 | 27 | {children} 28 | > 29 | 30 | ); 31 | }; 32 | 33 | const customRender = ( 34 | ui: ReactElement, 35 | options?: Omit 36 | ) => render(ui, { wrapper: AllTheProviders, ...options }); 37 | 38 | expect.extend(matchers); 39 | 40 | export * from "@testing-library/react"; 41 | export { customRender as render }; 42 | -------------------------------------------------------------------------------- /src/theme/components/kbd/kbd.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { Kbd, Text, StackProps, VStack, Box } from "@chakra-ui/react"; 4 | import { Decorators } from "util/storybook-utils"; 5 | import { useThemedColor } from "util/helpers"; 6 | 7 | export default { 8 | title: "Components / Kbd", 9 | component: Kbd, 10 | } as ComponentMeta; 11 | 12 | const NewDefault = (props: StackProps) => { 13 | const c = useThemedColor(); 14 | return ( 15 | 16 | 17 | New Theme 18 | 19 | 20 | Ctrl+K 21 | 22 | 23 | ); 24 | }; 25 | 26 | const OldDefault = (props: StackProps) => { 27 | return ( 28 | 29 | 30 | Old Theme 31 | 32 | 33 | Ctrl+K 34 | 35 | 36 | ); 37 | }; 38 | 39 | export const Default: ComponentStory = (args) => ( 40 | } 42 | oldComponent={} 43 | /> 44 | ); 45 | -------------------------------------------------------------------------------- /src/theme/components/spinner/spinner.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { Spinner, StackProps, VStack, Box, Text } from "@chakra-ui/react"; 4 | import { Decorators } from "util/storybook-utils"; 5 | import { useThemedColor } from "util/helpers"; 6 | 7 | export default { 8 | title: "Components / Spinner", 9 | component: Spinner, 10 | } as ComponentMeta; 11 | 12 | const NewDefault = (props: StackProps) => { 13 | const c = useThemedColor(); 14 | return ( 15 | 16 | 17 | New Theme 18 | 19 | 20 | 21 | 22 | 23 | ); 24 | }; 25 | 26 | const OldDefault = (props: StackProps) => { 27 | return ( 28 | 29 | 30 | Old Theme 31 | 32 | 33 | 34 | 35 | 36 | ); 37 | }; 38 | 39 | export const Default: ComponentStory = (args) => ( 40 | } 42 | oldComponent={} 43 | /> 44 | ); 45 | -------------------------------------------------------------------------------- /src/theme/components/link/link.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { Link, Text, StackProps, VStack, Box } from "@chakra-ui/react"; 4 | import { Decorators } from "util/storybook-utils"; 5 | import { useThemedColor } from "util/helpers"; 6 | 7 | export default { 8 | title: "Components / Link", 9 | component: Link, 10 | } as ComponentMeta; 11 | 12 | const NewDefault = (props: StackProps) => { 13 | const c = useThemedColor(); 14 | return ( 15 | 16 | 17 | New Theme 18 | 19 | 20 | To # 21 | 22 | 23 | ); 24 | }; 25 | 26 | const OldDefault = (props: StackProps) => { 27 | return ( 28 | 29 | 30 | New Theme 31 | 32 | 33 | To # 34 | 35 | 36 | ); 37 | }; 38 | 39 | export const Default: ComponentStory = (args) => ( 40 | } 42 | oldComponent={} 43 | /> 44 | ); 45 | -------------------------------------------------------------------------------- /src/theme/components/skeleton/skeleton.ts: -------------------------------------------------------------------------------- 1 | import { keyframes } from "@chakra-ui/system"; 2 | import type { SystemStyleFunction } from "@chakra-ui/theme-tools"; 3 | import { getColor, mode } from "@chakra-ui/theme-tools"; 4 | import { getColorInfo } from "../../../util/helpers"; 5 | 6 | const fade = (startColor: string, endColor: string) => 7 | keyframes({ 8 | from: { borderColor: startColor, background: startColor }, 9 | to: { borderColor: endColor, background: endColor }, 10 | }); 11 | 12 | const baseStyle: SystemStyleFunction = (props) => { 13 | const { theme } = props; 14 | const { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 15 | 16 | const defaultStartColor = mode(`${_gray}.3`, `${_grayDark}.3`)(props); 17 | const defaultEndColor = mode(`${_gray}.9`, `${_grayDark}.9`)(props); 18 | 19 | const { 20 | startColor = defaultStartColor, 21 | endColor = defaultEndColor, 22 | speed, 23 | } = props; 24 | 25 | const start = getColor(theme, startColor); 26 | const end = getColor(theme, endColor); 27 | 28 | return { 29 | opacity: 0.7, 30 | borderRadius: "2px", 31 | borderColor: start, 32 | background: end, 33 | animation: `${speed}s linear infinite alternate ${fade(start, end)}`, 34 | }; 35 | }; 36 | 37 | export default { 38 | baseStyle, 39 | }; 40 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 5 | addons: [ 6 | "@storybook/addon-links", 7 | "@storybook/addon-essentials", 8 | "@storybook/addon-interactions", 9 | ], 10 | framework: "@storybook/react", 11 | webpackFinal: async (config, { configType }) => { 12 | // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION' 13 | // You can change the configuration based on that. 14 | // 'PRODUCTION' is used when building the static version of storybook. 15 | 16 | // Allow for .mjs files to be loaded 17 | config.module.rules.push({ 18 | type: "javascript/auto", 19 | test: /\.mjs$/, 20 | include: /node_modules/, 21 | }); 22 | 23 | // Add aliases manually for Chakra due to https://github.com/storybookjs/storybook/issues/13114 24 | config.resolve.alias["@emotion/core"] = "@emotion/react"; 25 | config.resolve.alias["emotion-theming"] = "@emotion/react"; 26 | 27 | // Allow for default imports from "src/*". This makes it possible to do an import like this: 28 | // import { chakraTokensFromPalette } from "util/helpers"; 29 | config.resolve.modules.push(path.resolve(__dirname, "..", "src")); 30 | 31 | // Return the altered config 32 | return config; 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /src/theme/components/heading/heading.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { Text, Heading, StackProps, VStack, Box } from "@chakra-ui/react"; 4 | import { Decorators } from "util/storybook-utils"; 5 | import { useThemedColor } from "util/helpers"; 6 | 7 | export default { 8 | title: "Components / Heading", 9 | component: Heading, 10 | } as ComponentMeta; 11 | 12 | const NewDefault = (props: StackProps) => { 13 | const c = useThemedColor(); 14 | return ( 15 | 16 | 17 | New Theme 18 | 19 | 20 | This is a heading. 21 | 22 | 23 | ); 24 | }; 25 | 26 | const OldDefault = (props: StackProps) => { 27 | return ( 28 | 29 | 30 | Old Theme 31 | 32 | 33 | This is a heading. 34 | 35 | 36 | ); 37 | }; 38 | 39 | export const Default: ComponentStory = (args) => ( 40 | } 42 | oldComponent={} 43 | /> 44 | ); 45 | -------------------------------------------------------------------------------- /cypress/integration/components/number-input.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-numberinput--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] [data-testid=increment-stepper]") 13 | .should("have.css", "border-start-color", getRgbFromThemeColor("_gray.6")) 14 | .realMouseDown() 15 | .wait(200) 16 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.5")) 17 | .realMouseUp(); 18 | }); 19 | 20 | it("displays in dark mode correctly", () => { 21 | cy.get("#switch-color-mode").click(); 22 | cy.get("[data-testid=NewDefault] [data-testid=increment-stepper]") 23 | .should( 24 | "have.css", 25 | "border-start-color", 26 | getRgbFromThemeColor("_grayDark.6") 27 | ) 28 | .realMouseDown() 29 | .wait(200) 30 | .should( 31 | "have.css", 32 | "background-color", 33 | getRgbFromThemeColor("_grayDark.5") 34 | ) 35 | .realMouseUp(); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/theme/components/accordion/accordion.ts: -------------------------------------------------------------------------------- 1 | import { accordionAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import { 3 | PartsStyleFunction, 4 | SystemStyleFunction, 5 | SystemStyleObject, 6 | mode, 7 | } from "@chakra-ui/theme-tools"; 8 | 9 | const baseStyleContainer: SystemStyleObject = { 10 | borderTopWidth: "1px", 11 | borderColor: "inherit", 12 | _last: { 13 | borderBottomWidth: "1px", 14 | }, 15 | }; 16 | 17 | const baseStyleButton: SystemStyleFunction = (props) => { 18 | return { 19 | transitionProperty: "common", 20 | transitionDuration: "normal", 21 | fontSize: "1rem", 22 | _focus: { 23 | boxShadow: "outline", 24 | }, 25 | _hover: { 26 | bg: mode("blackA.3", "whiteA.3")(props), 27 | }, 28 | _disabled: { 29 | opacity: 0.4, 30 | cursor: "not-allowed", 31 | }, 32 | px: 4, 33 | py: 2, 34 | }; 35 | }; 36 | 37 | const baseStylePanel: SystemStyleObject = { 38 | pt: 2, 39 | px: 4, 40 | pb: 5, 41 | }; 42 | 43 | const baseStyleIcon: SystemStyleObject = { 44 | fontSize: "1.25em", 45 | }; 46 | 47 | const baseStyle: PartsStyleFunction = (props) => ({ 48 | root: {}, 49 | container: baseStyleContainer, 50 | button: baseStyleButton(props), 51 | panel: baseStylePanel, 52 | icon: baseStyleIcon, 53 | }); 54 | 55 | export default { 56 | parts: parts.keys, 57 | baseStyle, 58 | }; 59 | -------------------------------------------------------------------------------- /src/theme/components/tooltip/tooltip.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { Tooltip, StackProps, VStack, Box, Text } from "@chakra-ui/react"; 4 | import { Decorators } from "util/storybook-utils"; 5 | import { useThemedColor } from "util/helpers"; 6 | 7 | export default { 8 | title: "Components / Tooltip", 9 | component: Tooltip, 10 | } as ComponentMeta; 11 | 12 | const NewDefault = (props: StackProps) => { 13 | const c = useThemedColor(); 14 | return ( 15 | 16 | 17 | New Theme 18 | 19 | 20 | Hover me 21 | 22 | 23 | ); 24 | }; 25 | 26 | const OldDefault = (props: StackProps) => { 27 | return ( 28 | 29 | 30 | Old Theme 31 | 32 | 33 | Hover me 34 | 35 | 36 | ); 37 | }; 38 | 39 | export const Default: ComponentStory = (args) => ( 40 | } 42 | oldComponent={} 43 | /> 44 | ); 45 | -------------------------------------------------------------------------------- /cypress/integration/components/modal.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-modal--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | const _grayA10 = "rgba(0, 16, 27, 0.506)"; 13 | 14 | cy.contains("New Open").click(); 15 | 16 | cy.get(".chakra-modal__content").should( 17 | "have.css", 18 | "background-color", 19 | getRgbFromThemeColor("_gray.1") 20 | ); 21 | 22 | cy.get(".chakra-modal__overlay").should( 23 | "have.css", 24 | "background-color", 25 | _grayA10 26 | ); 27 | }); 28 | 29 | it("displays in dark mode correctly", () => { 30 | const _grayA10 = "rgba(0, 16, 27, 0.506)"; 31 | 32 | cy.get("#switch-color-mode").click(); 33 | 34 | cy.contains("New Open").click(); 35 | 36 | cy.get(".chakra-modal__content").should( 37 | "have.css", 38 | "background-color", 39 | getRgbFromThemeColor("_grayDark.1") 40 | ); 41 | 42 | cy.get(".chakra-modal__overlay").should( 43 | "have.css", 44 | "background-color", 45 | _grayA10 46 | ); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /cypress/integration/components/tooltip.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-tooltip--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | // open the tooltip 13 | cy.get("[data-testid=NewDefault]").within(() => { 14 | cy.contains("Hover me").realHover(); 15 | }); 16 | 17 | // test 18 | cy.contains("This is a chakra tooltip") 19 | .should( 20 | "have.css", 21 | "background-color", 22 | getRgbFromThemeColor("_grayDark.2") 23 | ) 24 | .should("have.css", "color", getRgbFromThemeColor("_gray.1")); 25 | }); 26 | 27 | it("displays in dark mode correctly", () => { 28 | cy.get("#switch-color-mode").click(); 29 | 30 | // open the tooltip 31 | cy.get("[data-testid=NewDefault]").within(() => { 32 | cy.contains("Hover me").realHover(); 33 | }); 34 | 35 | // test 36 | cy.contains("This is a chakra tooltip") 37 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.2")) 38 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.1")); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /cypress/integration/components/form-error.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-formerror--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | // test the error message 13 | cy.get("[data-testid=NewDefault] .chakra-form__error-message").should( 14 | "have.css", 15 | "color", 16 | getRgbFromThemeColor("red.9") 17 | ); 18 | 19 | // test the error icon 20 | cy.get("[data-testid=NewDefault] .chakra-form__error-icon").should( 21 | "have.css", 22 | "color", 23 | getRgbFromThemeColor("red.9") 24 | ); 25 | }); 26 | 27 | it("displays in dark mode correctly", () => { 28 | cy.get("#switch-color-mode").click(); 29 | 30 | // test the error message 31 | cy.get("[data-testid=NewDefault] .chakra-form__error-message").should( 32 | "have.css", 33 | "color", 34 | getRgbFromThemeColor("redDark.9") 35 | ); 36 | 37 | // test the error icon 38 | cy.get("[data-testid=NewDefault] .chakra-form__error-icon").should( 39 | "have.css", 40 | "color", 41 | getRgbFromThemeColor("redDark.9") 42 | ); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /cypress/integration/components/form.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-form--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | // test the required indicator 13 | cy.get("[data-testid=NewDefault] .chakra-form__required-indicator").should( 14 | "have.css", 15 | "color", 16 | getRgbFromThemeColor("red.9") 17 | ); 18 | 19 | // test the helper text 20 | cy.get("[data-testid=NewDefault] .chakra-form__helper-text").should( 21 | "have.css", 22 | "color", 23 | getRgbFromThemeColor("_gray.9") 24 | ); 25 | }); 26 | 27 | it("displays in dark mode correctly", () => { 28 | cy.get("#switch-color-mode").click(); 29 | 30 | // test the required indicator 31 | cy.get("[data-testid=NewDefault] .chakra-form__required-indicator").should( 32 | "have.css", 33 | "color", 34 | getRgbFromThemeColor("redDark.9") 35 | ); 36 | 37 | // test the helper text 38 | cy.get("[data-testid=NewDefault] .chakra-form__helper-text").should( 39 | "have.css", 40 | "color", 41 | getRgbFromThemeColor("_grayDark.9") 42 | ); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/theme/components/skip-link/skip-link.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { SkipNavContent, SkipNavLink } from "@chakra-ui/skip-nav"; 4 | import { VStack, StackProps, Box, Text } from "@chakra-ui/react"; 5 | import { Decorators } from "util/storybook-utils"; 6 | import { useThemedColor } from "util/helpers"; 7 | 8 | export default { 9 | title: "Components / SkipNavLink", 10 | component: SkipNavLink, 11 | } as ComponentMeta; 12 | 13 | const NewDefault = (props: StackProps) => { 14 | const c = useThemedColor(); 15 | return ( 16 | 17 | 18 | New Theme 19 | 20 | 21 | Skip to Content 22 | 23 | 24 | 25 | ); 26 | }; 27 | 28 | const OldDefault = (props: StackProps) => { 29 | return ( 30 | 31 | 32 | Old Theme 33 | 34 | 35 | Skip to Content 36 | 37 | 38 | 39 | ); 40 | }; 41 | 42 | export const Default: ComponentStory = (args) => ( 43 | } 45 | oldComponent={} 46 | /> 47 | ); 48 | -------------------------------------------------------------------------------- /src/theme/components/close-button/close-button.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | SystemStyleFunction, 3 | SystemStyleObject, 4 | } from "@chakra-ui/theme-tools"; 5 | import { cssVar, mode } from "@chakra-ui/theme-tools"; 6 | import { getColorInfo } from "../../../util/helpers"; 7 | 8 | const $size = cssVar("close-button-size"); 9 | 10 | const baseStyle: SystemStyleFunction = (props) => { 11 | const { theme } = props; 12 | const { light, dark } = getColorInfo("_gray", theme); 13 | 14 | const hoverBg = mode(`${light}A.3`, `${dark}A.3`)(props); 15 | const activeBg = mode(`${light}A.4`, `${dark}A.4`)(props); 16 | 17 | return { 18 | w: [$size.reference], 19 | h: [$size.reference], 20 | borderRadius: "md", 21 | transitionProperty: "common", 22 | transitionDuration: "normal", 23 | _disabled: { 24 | opacity: 0.4, 25 | cursor: "not-allowed", 26 | boxShadow: "none", 27 | }, 28 | _hover: { bg: hoverBg }, 29 | _active: { bg: activeBg }, 30 | _focus: { 31 | boxShadow: "outline", 32 | }, 33 | }; 34 | }; 35 | 36 | const sizes: Record = { 37 | lg: { 38 | [$size.variable]: "40px", 39 | fontSize: "16px", 40 | }, 41 | md: { 42 | [$size.variable]: "32px", 43 | fontSize: "12px", 44 | }, 45 | sm: { 46 | [$size.variable]: "24px", 47 | fontSize: "10px", 48 | }, 49 | }; 50 | 51 | const defaultProps = { 52 | size: "md", 53 | }; 54 | 55 | export default { 56 | baseStyle, 57 | sizes, 58 | defaultProps, 59 | }; 60 | -------------------------------------------------------------------------------- /cypress/integration/components/popover.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-popover--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] #popover-trigger-2").click(); 13 | cy.get("[data-testid=NewDefault] .chakra-popover__content").should( 14 | "have.css", 15 | "background-color", 16 | getRgbFromThemeColor("_gray.1") 17 | ); 18 | cy.get("[data-testid=NewDefault] .chakra-popover__arrow").should( 19 | "have.css", 20 | "box-shadow", 21 | `${getRgbFromThemeColor("_gray.4")} 1px -1px 1px 0px` 22 | ); 23 | }); 24 | 25 | it("displays in dark mode correctly", () => { 26 | cy.get("#switch-color-mode").click(); 27 | 28 | cy.get("[data-testid=NewDefault] #popover-trigger-2").click(); 29 | 30 | cy.get("[data-testid=NewDefault] .chakra-popover__content").should( 31 | "have.css", 32 | "background-color", 33 | getRgbFromThemeColor("_grayDark.1") 34 | ); 35 | cy.get("[data-testid=NewDefault] .chakra-popover__arrow").should( 36 | "have.css", 37 | "box-shadow", 38 | `${getRgbFromThemeColor("_grayDark.4")} 1px -1px 1px 0px` 39 | ); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/theme/foundations/colors.ts: -------------------------------------------------------------------------------- 1 | import * as radixColors from "@radix-ui/colors"; 2 | 3 | // create the types for radix colors 4 | export interface IColorScale { 5 | 1: string; 6 | 2: string; 7 | 3: string; 8 | 4: string; 9 | 5: string; 10 | 6: string; 11 | 7: string; 12 | 8: string; 13 | 9: string; 14 | 10: string; 15 | 11: string; 16 | 12: string; 17 | } 18 | export type RadixColorKeyType = keyof typeof radixColors; 19 | export type RadixColorsType = Record; 20 | 21 | // Extract and create the radixColors into a RadixColorsType object. 22 | // This "colors" object will be compliant with the @chakra-ui/react config format 23 | export const colors = Object.fromEntries( 24 | Object.entries(radixColors).map(([key, value]) => [ 25 | key, 26 | Object.fromEntries( 27 | Object.entries(value).map(([colorKey, colorValue]) => [ 28 | colorKey.match(/[0-9]+$/)![0], 29 | colorValue, 30 | ]) 31 | ), 32 | ]) 33 | ) as unknown as RadixColorsType; 34 | 35 | // override the default styles 36 | export default { 37 | // add all radix-ui color scales to the chakra-ui overrides 38 | ...colors, 39 | 40 | // select a default grayscale from the radix-ui colors as chakra-ui 41 | // has only one grayscale. Radix-ui provides many grayscales and we need 42 | // a default for all component overrides 43 | _gray: { ...colors.slate }, 44 | _grayA: { ...colors.slateA }, 45 | _grayDark: { ...colors.slateDark }, 46 | _grayDarkA: { ...colors.slateDarkA }, 47 | }; 48 | -------------------------------------------------------------------------------- /src/theme/components/radio/radio.ts: -------------------------------------------------------------------------------- 1 | import { radioAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import { 3 | PartsStyleFunction, 4 | PartsStyleObject, 5 | SystemStyleFunction, 6 | } from "@chakra-ui/theme-tools"; 7 | import Checkbox from "../checkbox/checkbox"; 8 | 9 | const baseStyleControl: SystemStyleFunction = (props) => { 10 | const { control = {} } = Checkbox.baseStyle(props); 11 | 12 | return { 13 | ...control, 14 | borderRadius: "full", 15 | _checked: { 16 | ...(control as any)["_checked"], 17 | _before: { 18 | content: `""`, 19 | display: "inline-block", 20 | pos: "relative", 21 | w: "50%", 22 | h: "50%", 23 | borderRadius: "50%", 24 | bg: "currentColor", 25 | }, 26 | }, 27 | }; 28 | }; 29 | 30 | const baseStyle: PartsStyleFunction = (props) => ({ 31 | label: Checkbox.baseStyle(props).label, 32 | container: Checkbox.baseStyle(props).container, 33 | control: baseStyleControl(props), 34 | }); 35 | 36 | const sizes: Record> = { 37 | md: { 38 | control: { w: 4, h: 4 }, 39 | label: { fontSize: "md" }, 40 | }, 41 | lg: { 42 | control: { w: 5, h: 5 }, 43 | label: { fontSize: "lg" }, 44 | }, 45 | sm: { 46 | control: { width: 3, height: 3 }, 47 | label: { fontSize: "sm" }, 48 | }, 49 | }; 50 | 51 | const defaultProps = { 52 | size: "md", 53 | colorScheme: "blue", 54 | }; 55 | 56 | export default { 57 | parts: parts.keys, 58 | baseStyle, 59 | sizes, 60 | defaultProps, 61 | }; 62 | -------------------------------------------------------------------------------- /src/theme/components/divider/divider.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Container, 5 | Divider, 6 | StackProps, 7 | VStack, 8 | HStack, 9 | Box, 10 | Text, 11 | } from "@chakra-ui/react"; 12 | import { Decorators } from "util/storybook-utils"; 13 | import { useThemedColor } from "util/helpers"; 14 | 15 | export default { 16 | title: "Components / Divider", 17 | component: Divider, 18 | } as ComponentMeta; 19 | 20 | const NewDefault = (props: StackProps) => { 21 | const c = useThemedColor(); 22 | return ( 23 | 24 | 25 | New Theme 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | const OldDefault = (props: StackProps) => { 38 | return ( 39 | 40 | 41 | Old Theme 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ); 51 | }; 52 | 53 | export const Default: ComponentStory = (args) => ( 54 | } 56 | oldComponent={} 57 | /> 58 | ); 59 | -------------------------------------------------------------------------------- /cypress/integration/components/drawer.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-drawer--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | // open the overlay 13 | cy.contains("Open New").click(); 14 | 15 | // test the overlay 16 | const _grayA10 = "rgba(0, 16, 27, 0.506)"; 17 | const overlay = cy.get(".chakra-modal__overlay"); 18 | overlay.should("have.css", "background-color", _grayA10); 19 | 20 | // test the drawer 21 | const drawer = cy.get(".chakra-modal__content"); 22 | drawer.should( 23 | "have.css", 24 | "background-color", 25 | getRgbFromThemeColor("_gray.1") 26 | ); 27 | }); 28 | 29 | it("displays in dark mode correctly", () => { 30 | cy.get("#switch-color-mode").click(); 31 | 32 | // open the overlay 33 | cy.contains("Open New").click(); 34 | 35 | // test the overlay 36 | const _grayA10 = "rgba(0, 16, 27, 0.506)"; 37 | const overlay = cy.get(".chakra-modal__overlay"); 38 | overlay.should("have.css", "background-color", _grayA10); 39 | 40 | // test the drawer 41 | const drawer = cy.get(".chakra-modal__content"); 42 | drawer.should( 43 | "have.css", 44 | "background-color", 45 | getRgbFromThemeColor("_grayDark.1") 46 | ); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /cypress/integration/components/accordion.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-accordion--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | // test the item 13 | cy.get("[data-testid=NewDefault] .chakra-accordion__item") 14 | .eq(0) 15 | .should("have.css", "border-color", getRgbFromThemeColor("_gray.6")); 16 | 17 | // test the button 18 | cy.get("[data-testid=accordion-button]") 19 | .realHover() 20 | .wait(200) // wait for color transition to complete 21 | .should("have.css", "background-color", "rgba(0, 0, 0, 0.047)") 22 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")); 23 | }); 24 | 25 | it("displays in dark mode correctly", () => { 26 | cy.get("#switch-color-mode").click(); 27 | 28 | // test the item 29 | cy.get("[data-testid=NewDefault] .chakra-accordion__item") 30 | .eq(0) 31 | .should("have.css", "border-color", getRgbFromThemeColor("_grayDark.6")); 32 | 33 | // test the button 34 | cy.get("[data-testid=accordion-button]") 35 | .realHover() 36 | .wait(200) // wait for color transition to complete 37 | .should("have.css", "background-color", "rgba(255, 255, 255, 0.035)") 38 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/theme/components/close-button/close-button.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | CloseButton, 5 | StackProps, 6 | VStack, 7 | HStack, 8 | Box, 9 | Text, 10 | } from "@chakra-ui/react"; 11 | import { useThemedColor } from "util/helpers"; 12 | import { Decorators } from "util/storybook-utils"; 13 | 14 | export default { 15 | title: "Components / CloseButton", 16 | component: CloseButton, 17 | } as ComponentMeta; 18 | 19 | const NewDefault = (props: StackProps) => { 20 | const c = useThemedColor(); 21 | return ( 22 | 23 | {/* display the light */} 24 | 25 | New Theme 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | const OldDefault = (props: StackProps) => { 38 | return ( 39 | 40 | 41 | Old Theme 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ); 51 | }; 52 | 53 | export const Default: ComponentStory = (args) => ( 54 | } 56 | oldComponent={} 57 | /> 58 | ); 59 | -------------------------------------------------------------------------------- /src/theme/components/skeleton/skeleton.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Skeleton, 5 | SkeletonText, 6 | SkeletonCircle, 7 | StackProps, 8 | VStack, 9 | Box, 10 | Text, 11 | } from "@chakra-ui/react"; 12 | import { Decorators } from "util/storybook-utils"; 13 | import { useThemedColor } from "util/helpers"; 14 | 15 | export default { 16 | title: "Components / Skeleton", 17 | component: Skeleton, 18 | } as ComponentMeta; 19 | 20 | const NewDefault = (props: StackProps) => { 21 | const c = useThemedColor(); 22 | return ( 23 | 24 | 25 | New Theme 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | }; 35 | 36 | const OldDefault = (props: StackProps) => { 37 | return ( 38 | 39 | 40 | Old Theme 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | ); 49 | }; 50 | 51 | export const Default: ComponentStory = (args) => ( 52 | } 54 | oldComponent={} 55 | /> 56 | ); 57 | -------------------------------------------------------------------------------- /src/theme/components/form-label/form-label.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | FormControl, 5 | FormLabel, 6 | Input, 7 | StackProps, 8 | VStack, 9 | Box, 10 | Text, 11 | } from "@chakra-ui/react"; 12 | import { Decorators } from "util/storybook-utils"; 13 | import { useThemedColor } from "util/helpers"; 14 | 15 | export default { 16 | title: "Components / FormLabel", 17 | component: FormLabel, 18 | } as ComponentMeta; 19 | 20 | const NewDefault = (props: StackProps) => { 21 | const c = useThemedColor(); 22 | return ( 23 | 24 | 25 | New Theme 26 | 27 | 28 | 29 | Email Address 30 | 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | const OldDefault = (props: StackProps) => { 38 | return ( 39 | 40 | 41 | Old Theme 42 | 43 | 44 | 45 | Email Address 46 | 47 | 48 | 49 | 50 | ); 51 | }; 52 | 53 | export const Default: ComponentStory = (args) => ( 54 | } 56 | oldComponent={} 57 | /> 58 | ); 59 | -------------------------------------------------------------------------------- /src/theme/components/editable/editable.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | useEditable, 5 | Editable, 6 | EditableInput, 7 | EditablePreview, 8 | EditableTextarea, 9 | useEditableControls, 10 | StackProps, 11 | VStack, 12 | Box, 13 | Text, 14 | chakra, 15 | } from "@chakra-ui/react"; 16 | import { Decorators } from "util/storybook-utils"; 17 | import { useThemedColor } from "util/helpers"; 18 | 19 | export default { 20 | title: "Components / Editable", 21 | component: Editable, 22 | } as ComponentMeta; 23 | 24 | const NewDefault = (props: StackProps) => { 25 | const c = useThemedColor(); 26 | return ( 27 | 28 | 29 | New Theme 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ); 39 | }; 40 | 41 | const OldDefault = (props: StackProps) => { 42 | const c = useThemedColor(); 43 | return ( 44 | 45 | 46 | New Theme 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ); 56 | }; 57 | 58 | export const Default: ComponentStory = (props) => ( 59 | } 61 | oldComponent={} 62 | /> 63 | ); 64 | -------------------------------------------------------------------------------- /src/theme/components/pin-input/pin-input.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | HStack, 5 | PinInput, 6 | PinInputField, 7 | VStack, 8 | StackProps, 9 | Box, 10 | Text, 11 | } from "@chakra-ui/react"; 12 | import { Decorators } from "util/storybook-utils"; 13 | import { useThemedColor } from "util/helpers"; 14 | 15 | export default { 16 | title: "Components / PinInput", 17 | component: PinInput, 18 | } as ComponentMeta; 19 | 20 | const NewDefault = (props: StackProps) => { 21 | const c = useThemedColor(); 22 | return ( 23 | 24 | 25 | New Theme 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ); 39 | }; 40 | 41 | const OldDefault = (props: StackProps) => { 42 | return ( 43 | 44 | 45 | Old Theme 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | ); 59 | }; 60 | 61 | export const Default: ComponentStory = (args) => ( 62 | } 64 | oldComponent={} 65 | /> 66 | ); 67 | -------------------------------------------------------------------------------- /src/theme/components/select/select.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Select, 5 | Container, 6 | StackProps, 7 | VStack, 8 | Box, 9 | Text, 10 | } from "@chakra-ui/react"; 11 | import { Decorators } from "util/storybook-utils"; 12 | import { useThemedColor } from "util/helpers"; 13 | 14 | export default { 15 | title: "Components / Select", 16 | component: Select, 17 | } as ComponentMeta; 18 | 19 | const NewDefault = (props: StackProps) => { 20 | const c = useThemedColor(); 21 | return ( 22 | 23 | 24 | New Theme 25 | 26 | 27 | 28 | Option 1 29 | Option 2 30 | Option 3 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | const OldDefault = (props: StackProps) => { 38 | return ( 39 | 40 | 41 | New Theme 42 | 43 | 44 | 45 | Option 1 46 | Option 2 47 | Option 3 48 | 49 | 50 | 51 | ); 52 | }; 53 | 54 | export const Default: ComponentStory = (args) => ( 55 | } 57 | oldComponent={} 58 | /> 59 | ); 60 | -------------------------------------------------------------------------------- /src/theme/components/form-error/form-error.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { chakra, PropsOf, useMultiStyleConfig } from "@chakra-ui/system"; 4 | import { 5 | FormControl, 6 | FormErrorIcon, 7 | FormErrorMessage, 8 | StackProps, 9 | VStack, 10 | Text, 11 | Box, 12 | } from "@chakra-ui/react"; 13 | import { Decorators } from "util/storybook-utils"; 14 | import { useThemedColor } from "util/helpers"; 15 | 16 | export default { 17 | title: "Components / FormError", 18 | component: FormControl, 19 | } as ComponentMeta; 20 | 21 | const NewDefault = (props: StackProps) => { 22 | const c = useThemedColor(); 23 | return ( 24 | 25 | 26 | New Theme 27 | 28 | 29 | 30 | 31 | 32 | Email is a required field. 33 | 34 | 35 | 36 | 37 | ); 38 | }; 39 | 40 | const OldDefault = (props: StackProps) => { 41 | return ( 42 | 43 | 44 | Old Theme 45 | 46 | 47 | 48 | 49 | 50 | Email is a required field. 51 | 52 | 53 | 54 | 55 | ); 56 | }; 57 | 58 | export const Default: ComponentStory = (args) => ( 59 | } 61 | oldComponent={} 62 | /> 63 | ); 64 | -------------------------------------------------------------------------------- /src/theme/components/textarea/textarea.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { Textarea, StackProps, VStack, Box, Text } from "@chakra-ui/react"; 4 | import { Decorators } from "util/storybook-utils"; 5 | import { useThemedColor } from "util/helpers"; 6 | 7 | export default { 8 | title: "Components / Textarea", 9 | component: Textarea, 10 | } as ComponentMeta; 11 | 12 | const NewDefault = (props: StackProps) => { 13 | const c = useThemedColor(); 14 | return ( 15 | 16 | 17 | New Theme 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | }; 29 | 30 | const OldDefault = (props: StackProps) => { 31 | return ( 32 | 33 | 34 | Old Theme 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | }; 46 | 47 | export const Default: ComponentStory = (args) => ( 48 | } 50 | oldComponent={} 51 | /> 52 | ); 53 | -------------------------------------------------------------------------------- /src/theme/components/form/form.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { chakra, PropsOf, useMultiStyleConfig } from "@chakra-ui/system"; 4 | import { 5 | FormControlOptions, 6 | FormControl, 7 | FormErrorIcon, 8 | FormErrorMessage, 9 | FormHelperText, 10 | FormLabel, 11 | Input, 12 | StackProps, 13 | VStack, 14 | Box, 15 | Text, 16 | } from "@chakra-ui/react"; 17 | import { Decorators } from "util/storybook-utils"; 18 | import { useThemedColor } from "util/helpers"; 19 | 20 | export default { 21 | title: "Components / Form", 22 | component: FormControl, 23 | } as ComponentMeta; 24 | 25 | const NewDefault = (props: StackProps) => { 26 | const c = useThemedColor(); 27 | return ( 28 | 29 | 30 | New Theme 31 | 32 | 33 | 34 | Email address 35 | 36 | We'll never share your email. 37 | 38 | 39 | 40 | ); 41 | }; 42 | 43 | const OldDefault = (props: StackProps) => { 44 | return ( 45 | 46 | 47 | Old Theme 48 | 49 | 50 | 51 | Email address 52 | 53 | We'll never share your email. 54 | 55 | 56 | 57 | ); 58 | }; 59 | 60 | export const Default: ComponentStory = (args) => ( 61 | } 63 | oldComponent={} 64 | /> 65 | ); 66 | -------------------------------------------------------------------------------- /src/theme/components/avatar/avatar.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Avatar, 5 | Stack, 6 | AvatarGroup, 7 | PropsOf, 8 | AvatarBadge, 9 | AvatarBadgeProps, 10 | StackProps, 11 | VStack, 12 | Text, 13 | Box, 14 | } from "@chakra-ui/react"; 15 | import { Decorators } from "util/storybook-utils"; 16 | 17 | export default { 18 | title: "Components / Avatar", 19 | component: Avatar, 20 | } as ComponentMeta; 21 | 22 | const NewDefault = (props: StackProps) => ( 23 | 24 | 25 | New Theme 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ); 40 | 41 | const OldDefault = (props: StackProps) => ( 42 | 43 | 44 | Old Theme 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | ); 59 | 60 | export const Default: ComponentStory = (args) => { 61 | return ( 62 | } 64 | oldComponent={} 65 | /> 66 | ); 67 | }; 68 | -------------------------------------------------------------------------------- /src/theme/components/select/select.ts: -------------------------------------------------------------------------------- 1 | import { selectAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleFunction, 4 | PartsStyleObject, 5 | SystemStyleFunction, 6 | SystemStyleObject, 7 | } from "@chakra-ui/theme-tools"; 8 | import { mergeWith } from "@chakra-ui/utils"; 9 | import { mode } from "@chakra-ui/theme-tools"; 10 | import Input from "../input/input"; 11 | import { getColorInfo } from "../../../util/helpers"; 12 | 13 | const baseStyleField: SystemStyleFunction = (props) => { 14 | const { theme } = props; 15 | let { light: _gray, dark: _grayDark, isDark } = getColorInfo("_gray", theme); 16 | 17 | if (isDark) [_gray, _grayDark] = [_grayDark, _gray]; 18 | 19 | return { 20 | ...Input.baseStyle.field, 21 | bg: mode(`${_gray}.1`, `${_grayDark}.1`)(props), 22 | appearance: "none", 23 | paddingBottom: "1px", 24 | lineHeight: "normal", 25 | "> option, > optgroup": { 26 | bg: mode(`${_gray}.1`, `${_grayDark}.1`)(props), 27 | }, 28 | }; 29 | }; 30 | 31 | const baseStyleIcon: SystemStyleObject = { 32 | width: "1.5rem", 33 | height: "100%", 34 | insetEnd: "0.5rem", 35 | position: "relative", 36 | color: "currentColor", 37 | fontSize: "1.25rem", 38 | _disabled: { 39 | opacity: 0.5, 40 | }, 41 | }; 42 | 43 | const baseStyle: PartsStyleFunction = (props) => ({ 44 | field: baseStyleField(props), 45 | icon: baseStyleIcon, 46 | }); 47 | 48 | const iconSpacing = { paddingInlineEnd: "2rem" }; 49 | 50 | const sizes: Record> = mergeWith( 51 | {}, 52 | Input.sizes, 53 | { 54 | lg: { 55 | field: iconSpacing, 56 | }, 57 | md: { 58 | field: iconSpacing, 59 | }, 60 | sm: { 61 | field: iconSpacing, 62 | }, 63 | xs: { 64 | field: iconSpacing, 65 | icon: { insetEnd: "0.25rem" }, 66 | }, 67 | } 68 | ); 69 | 70 | export default { 71 | parts: parts.keys, 72 | baseStyle, 73 | sizes, 74 | variants: Input.variants, 75 | defaultProps: Input.defaultProps, 76 | }; 77 | -------------------------------------------------------------------------------- /src/theme/components/number-input/number-input.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | NumberInput, 5 | NumberInputField, 6 | NumberInputStepper, 7 | NumberIncrementStepper, 8 | NumberDecrementStepper, 9 | Container, 10 | StackProps, 11 | VStack, 12 | Text, 13 | Box, 14 | } from "@chakra-ui/react"; 15 | import { Decorators } from "util/storybook-utils"; 16 | import { useThemedColor } from "util/helpers"; 17 | 18 | export default { 19 | title: "Components / NumberInput", 20 | component: NumberInput, 21 | } as ComponentMeta; 22 | 23 | const NewDefault = (props: StackProps) => { 24 | const c = useThemedColor(); 25 | return ( 26 | 27 | 28 | New Theme 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ); 41 | }; 42 | 43 | const OldDefault = (props: StackProps) => { 44 | return ( 45 | 46 | 47 | Old Theme 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | ); 60 | }; 61 | 62 | export const Default: ComponentStory = (args) => ( 63 | } 65 | oldComponent={} 66 | /> 67 | ); 68 | -------------------------------------------------------------------------------- /cypress/integration/components/textarea.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-textarea--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | const getTestareas = () => 13 | cy 14 | .get("[data-testid=NewDefault] [data-testid=textarea-stack]") 15 | .children(); 16 | 17 | // test the outline variant 18 | getTestareas() 19 | .eq(0) 20 | .should("have.css", "border-color", getRgbFromThemeColor("_gray.6")); 21 | 22 | // test the flushed variant 23 | getTestareas() 24 | .eq(1) 25 | .should( 26 | "have.css", 27 | "border-bottom-color", 28 | getRgbFromThemeColor("_gray.6") 29 | ); 30 | 31 | // test the filled variant 32 | getTestareas() 33 | .eq(2) 34 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.3")); 35 | }); 36 | 37 | it("displays in dark mode correctly", () => { 38 | cy.get("#switch-color-mode").click(); 39 | 40 | const getTestareas = () => 41 | cy 42 | .get("[data-testid=NewDefault] [data-testid=textarea-stack]") 43 | .children(); 44 | 45 | // test the outline variant 46 | getTestareas() 47 | .eq(0) 48 | .should("have.css", "border-color", getRgbFromThemeColor("_grayDark.6")); 49 | 50 | // test the flushed variant 51 | getTestareas() 52 | .eq(1) 53 | .should( 54 | "have.css", 55 | "border-bottom-color", 56 | getRgbFromThemeColor("_grayDark.6") 57 | ); 58 | 59 | // test the filled variant 60 | getTestareas() 61 | .eq(2) 62 | .should( 63 | "have.css", 64 | "background-color", 65 | getRgbFromThemeColor("_grayDark.3") 66 | ); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /cypress/integration/components/radio.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-radio--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=new-row-1] .chakra-radio__control") 13 | .eq(0) 14 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.4")); 15 | cy.get("[data-testid=new-row-1] .chakra-radio__control") 16 | .eq(1) 17 | .should("have.css", "border-color", getRgbFromThemeColor("_gray.6")); 18 | cy.get("[data-testid=new-row-2] .chakra-radio__control") 19 | .eq(0) 20 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.3")); 21 | cy.get("[data-testid=new-row-2] .chakra-radio__control") 22 | .eq(1) 23 | .should("have.css", "background-color", getRgbFromThemeColor("blue.9")); 24 | }); 25 | 26 | it("displays in dark mode correctly", () => { 27 | cy.get("#switch-color-mode").click(); 28 | 29 | cy.get("[data-testid=new-row-1] .chakra-radio__control") 30 | .eq(0) 31 | .should( 32 | "have.css", 33 | "background-color", 34 | getRgbFromThemeColor("_grayDark.4") 35 | ); 36 | cy.get("[data-testid=new-row-1] .chakra-radio__control") 37 | .eq(1) 38 | .should("have.css", "border-color", getRgbFromThemeColor("_grayDark.6")); 39 | cy.get("[data-testid=new-row-2] .chakra-radio__control") 40 | .eq(0) 41 | .should( 42 | "have.css", 43 | "background-color", 44 | getRgbFromThemeColor("_grayDark.3") 45 | ); 46 | cy.get("[data-testid=new-row-2] .chakra-radio__control") 47 | .eq(1) 48 | .should( 49 | "have.css", 50 | "background-color", 51 | getRgbFromThemeColor("blueDark.9") 52 | ); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /src/theme/components/stat/stat.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | StatGroup, 5 | Stat, 6 | StatLabel, 7 | StatNumber, 8 | StatArrow, 9 | StatHelpText, 10 | StackProps, 11 | VStack, 12 | Box, 13 | Text, 14 | } from "@chakra-ui/react"; 15 | import { Decorators } from "util/storybook-utils"; 16 | import { useThemedColor } from "util/helpers"; 17 | 18 | export default { 19 | title: "Components / Stat", 20 | component: Stat, 21 | } as ComponentMeta; 22 | 23 | const NewDefault = (props: StackProps) => { 24 | const c = useThemedColor(); 25 | return ( 26 | 27 | 28 | New Theme 29 | 30 | 31 | 32 | 33 | Sent 34 | 345,670 35 | 36 | 37 | 38 | 23.36% 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | }; 46 | 47 | const OldDefault = (props: StackProps) => { 48 | return ( 49 | 50 | 51 | Old Theme 52 | 53 | 54 | 55 | 56 | Sent 57 | 345,670 58 | 59 | 60 | 61 | 23.36% 62 | 63 | 64 | 65 | 66 | 67 | ); 68 | }; 69 | 70 | export const Default: ComponentStory = (args) => ( 71 | } 73 | oldComponent={} 74 | /> 75 | ); 76 | -------------------------------------------------------------------------------- /cypress/integration/components/tag.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-tag--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").click(); 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | const getStacks = () => 13 | cy.get("[data-testid=NewDefault] [data-testid=stack-o-stacks]"); 14 | 15 | // test subtle 16 | getStacks() 17 | .children() 18 | .eq(0) 19 | .children() 20 | .eq(0) 21 | .should("have.css", "background-color", getRgbFromThemeColor("teal.4")); 22 | 23 | // test solid 24 | getStacks() 25 | .children() 26 | .eq(1) 27 | .children() 28 | .eq(0) 29 | .should("have.css", "background-color", getRgbFromThemeColor("teal.9")); 30 | 31 | // test outline 32 | getStacks() 33 | .children() 34 | .eq(2) 35 | .children() 36 | .eq(0) 37 | .should( 38 | "have.css", 39 | "box-shadow", 40 | `${getRgbFromThemeColor("teal.9")} 0px 0px 0px 1px inset` 41 | ); 42 | }); 43 | 44 | it("displays in dark mode correctly", () => { 45 | cy.get("#switch-color-mode").click(); 46 | 47 | const getStacks = () => 48 | cy.get("[data-testid=NewDefault] [data-testid=stack-o-stacks]"); 49 | 50 | // test subtle 51 | getStacks() 52 | .children() 53 | .eq(0) 54 | .children() 55 | .eq(0) 56 | .should( 57 | "have.css", 58 | "background-color", 59 | getRgbFromThemeColor("tealDark.4") 60 | ); 61 | 62 | // test solid 63 | getStacks() 64 | .children() 65 | .eq(1) 66 | .children() 67 | .eq(0) 68 | .should( 69 | "have.css", 70 | "background-color", 71 | getRgbFromThemeColor("tealDark.9") 72 | ); 73 | 74 | // test outline 75 | getStacks() 76 | .children() 77 | .eq(2) 78 | .children() 79 | .eq(0) 80 | .should( 81 | "have.css", 82 | "box-shadow", 83 | `${getRgbFromThemeColor("tealDark.9")} 0px 0px 0px 1px inset` 84 | ); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /src/theme/components/popover/popover.ts: -------------------------------------------------------------------------------- 1 | import { popoverAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleFunction, 4 | SystemStyleFunction, 5 | SystemStyleObject, 6 | } from "@chakra-ui/theme-tools"; 7 | import { cssVar, mode } from "@chakra-ui/theme-tools"; 8 | import { getColorInfo } from "../../../util/helpers"; 9 | 10 | const $popperBg = cssVar("popper-bg"); 11 | 12 | const $arrowBg = cssVar("popper-arrow-bg"); 13 | const $arrowShadowColor = cssVar("popper-arrow-shadow-color"); 14 | 15 | const baseStylePopper: SystemStyleObject = { 16 | zIndex: 10, 17 | }; 18 | 19 | const baseStyleContent: SystemStyleFunction = (props) => { 20 | const { theme } = props; 21 | const { light: _gray, dark: _darkGray } = getColorInfo("_gray", theme); 22 | 23 | const bg = mode(`${_gray}.1`, `${_darkGray}.1`)(props); 24 | const shadowColor = mode(`${_gray}.4`, `${_darkGray}.4`)(props); 25 | 26 | return { 27 | [$popperBg.variable]: `colors.${bg}`, 28 | bg: $popperBg.reference, 29 | [$arrowBg.variable]: $popperBg.reference, 30 | [$arrowShadowColor.variable]: `colors.${shadowColor}`, 31 | width: "xs", 32 | border: "1px solid", 33 | borderColor: "inherit", 34 | borderRadius: "md", 35 | boxShadow: "sm", 36 | zIndex: "inherit", 37 | _focus: { 38 | outline: 0, 39 | boxShadow: "outline", 40 | }, 41 | }; 42 | }; 43 | 44 | const baseStyleHeader: SystemStyleObject = { 45 | px: 3, 46 | py: 2, 47 | borderBottomWidth: "1px", 48 | }; 49 | 50 | const baseStyleBody: SystemStyleObject = { 51 | px: 3, 52 | py: 2, 53 | }; 54 | 55 | const baseStyleFooter: SystemStyleObject = { 56 | px: 3, 57 | py: 2, 58 | borderTopWidth: "1px", 59 | }; 60 | 61 | const baseStyleCloseButton: SystemStyleObject = { 62 | position: "absolute", 63 | borderRadius: "md", 64 | top: 1, 65 | insetEnd: 2, 66 | padding: 2, 67 | }; 68 | 69 | const baseStyle: PartsStyleFunction = (props) => ({ 70 | popper: baseStylePopper, 71 | content: baseStyleContent(props), 72 | header: baseStyleHeader, 73 | body: baseStyleBody, 74 | footer: baseStyleFooter, 75 | arrow: {}, 76 | closeButton: baseStyleCloseButton, 77 | }); 78 | 79 | export default { 80 | parts: parts.keys, 81 | baseStyle, 82 | }; 83 | -------------------------------------------------------------------------------- /src/theme/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Accordion } from "./accordion/accordion"; 2 | export { default as Alert } from "./alert/alert"; 3 | export { default as Avatar } from "./avatar/avatar"; 4 | export { default as Badge } from "./badge/badge"; 5 | export { default as Breadcrumb } from "./breadcrumb/breadcrumb"; 6 | export { default as Button } from "./button/button"; 7 | export { default as Checkbox } from "./checkbox/checkbox"; 8 | export { default as CloseButton } from "./close-button/close-button"; 9 | export { default as Code } from "./code/code"; 10 | export { default as Drawer } from "./drawer/drawer"; 11 | export { default as Container } from "./container/container"; 12 | export { default as Divider } from "./divider/divider"; 13 | export { default as Editable } from "./editable/editable"; 14 | export { default as FormError } from "./form-error/form-error"; 15 | export { default as FormLabel } from "./form-label/form-label"; 16 | export { default as Form } from "./form/form"; 17 | export { default as Heading } from "./heading/heading"; 18 | export { default as Input } from "./input/input"; 19 | export { default as Kbd } from "./kbd/kbd"; 20 | export { default as Link } from "./link/link"; 21 | export { default as List } from "./list/list"; 22 | export { default as Menu } from "./menu/menu"; 23 | export { default as Modal } from "./modal/modal"; 24 | export { default as NumberInput } from "./number-input/number-input"; 25 | export { default as PinInput } from "./pin-input/pin-input"; 26 | export { default as Popover } from "./popover/popover"; 27 | export { default as Progress } from "./progress/progress"; 28 | export { default as Radio } from "./radio/radio"; 29 | export { default as Select } from "./select/select"; 30 | export { default as Skeleton } from "./skeleton/skeleton"; 31 | export { default as SkipLink } from "./skip-link/skip-link"; 32 | export { default as Slider } from "./slider/slider"; 33 | export { default as Spinner } from "./spinner/spinner"; 34 | export { default as Stat } from "./stat/stat"; 35 | export { default as Switch } from "./switch/switch"; 36 | export { default as Table } from "./table/table"; 37 | export { default as Tabs } from "./tabs/tabs"; 38 | export { default as Tag } from "./tag/tag"; 39 | export { default as Textarea } from "./textarea/textarea"; 40 | export { default as Tooltip } from "./tooltip/tooltip"; 41 | -------------------------------------------------------------------------------- /.github/workflows/pull-on-main.yml: -------------------------------------------------------------------------------- 1 | name: push-to-main 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | jobs: 7 | # CONVENTIONAL_PULL_REQUESTS: ✅ 8 | # conventional-pr-check job exists so that each PR has a conventional PR title. This helps 9 | # prioritize the review of PRs. Please see https://www.conventionalcommits.org/en/v1.0.0/ 10 | # for details. 11 | # Available types: [feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert] 12 | conventional-pr-check: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | steps: 17 | - name: check-pr-title 18 | uses: amannn/action-semantic-pull-request@v4.4.0 19 | 20 | # PASSING_TESTS: ✅ 21 | # run-check job exists to ensure nothing has broken between tests, and that all tests pass. 22 | run-tests: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: checkout 26 | uses: actions/checkout@v2 27 | - uses: actions/setup-node@v3 28 | with: 29 | node-version: 14 30 | - run: npm install 31 | - run: npm run jest 32 | - run: npm run storybook:ci & npm run cypress:run 33 | 34 | # FULL_COVERAGE: ✅ 35 | # coverage-check job exists to ensure that there is 100% coverage for the codebase. While this 36 | # may not be a practical or recommened standard in most project, this is a relatively simple package 37 | # and 100% coverage will be a feasible and worth-while effort. 38 | 39 | # CONSISTENT_FORMATTING: ✅ 40 | # prettify job exists so that the codebase maintains consistent formatting. This will 41 | # run `prettier` on all committed files of given pull_request and add these changes to 42 | # the last commit. 43 | prettify: 44 | runs-on: ubuntu-latest 45 | steps: 46 | - name: checkout 47 | uses: actions/checkout@v2 48 | with: 49 | # Make sure the actual branch is checked out when running on pull request 50 | ref: ${{ github.head_ref }} 51 | # This is important to fetch the changes to the previous commit 52 | fetch-depth: 0 53 | 54 | - name: prettify 55 | uses: creyD/prettier_action@v4.2 56 | with: 57 | # This will add the changes to the previous commit & prevent new commit. 58 | same_commit: true 59 | # This will apply the format changes only to files that have been changed. 60 | only_changed: true 61 | -------------------------------------------------------------------------------- /src/theme/components/menu/menu.ts: -------------------------------------------------------------------------------- 1 | import { menuAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleFunction, 4 | SystemStyleFunction, 5 | SystemStyleObject, 6 | } from "@chakra-ui/theme-tools"; 7 | import { mode } from "@chakra-ui/theme-tools"; 8 | import { getColorInfo } from "../../../util/helpers"; 9 | 10 | const baseStyleList: SystemStyleFunction = (props) => { 11 | const { theme } = props; 12 | const { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 13 | 14 | return { 15 | bg: mode(`${_gray}.1`, `${_grayDark}.1`)(props), 16 | boxShadow: mode("sm", "dark-lg")(props), 17 | color: "inherit", 18 | minW: "3xs", 19 | py: "2", 20 | zIndex: 1, 21 | borderRadius: "md", 22 | borderWidth: "1px", 23 | }; 24 | }; 25 | 26 | const baseStyleItem: SystemStyleFunction = (props) => { 27 | const { theme } = props; 28 | const { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 29 | 30 | return { 31 | py: "0.4rem", 32 | px: "0.8rem", 33 | transitionProperty: "background", 34 | transitionDuration: "ultra-fast", 35 | transitionTimingFunction: "ease-in", 36 | _focus: { 37 | bg: mode(`${_gray}.3`, `${_grayDark}.3`)(props), 38 | }, 39 | _active: { 40 | bg: mode(`${_gray}.4`, `${_grayDark}.4`)(props), 41 | }, 42 | _expanded: { 43 | bg: mode(`${_gray}.3`, `${_grayDark}.3`)(props), 44 | }, 45 | _disabled: { 46 | opacity: 0.4, 47 | cursor: "not-allowed", 48 | }, 49 | }; 50 | }; 51 | 52 | const baseStyleGroupTitle: SystemStyleObject = { 53 | mx: 4, 54 | my: 2, 55 | fontWeight: "semibold", 56 | fontSize: "sm", 57 | }; 58 | 59 | const baseStyleCommand: SystemStyleObject = { 60 | opacity: 0.6, 61 | }; 62 | 63 | const baseStyleDivider: SystemStyleObject = { 64 | border: 0, 65 | borderBottom: "1px solid", 66 | borderColor: "inherit", 67 | my: "0.5rem", 68 | opacity: 0.6, 69 | }; 70 | 71 | const baseStyleButton: SystemStyleObject = { 72 | transitionProperty: "common", 73 | transitionDuration: "normal", 74 | }; 75 | 76 | const baseStyle: PartsStyleFunction = (props) => ({ 77 | button: baseStyleButton, 78 | list: baseStyleList(props), 79 | item: baseStyleItem(props), 80 | groupTitle: baseStyleGroupTitle, 81 | command: baseStyleCommand, 82 | divider: baseStyleDivider, 83 | }); 84 | 85 | export default { 86 | parts: parts.keys, 87 | baseStyle, 88 | }; 89 | -------------------------------------------------------------------------------- /src/theme/components/avatar/avatar.ts: -------------------------------------------------------------------------------- 1 | import { avatarAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import { isDark, mode, randomColor } from "@chakra-ui/theme-tools"; 3 | import type { 4 | PartsStyleFunction, 5 | PartsStyleObject, 6 | SystemStyleFunction, 7 | } from "@chakra-ui/theme-tools"; 8 | import { Theme, theme } from "@chakra-ui/react"; 9 | 10 | const themeSizes = theme.sizes; 11 | 12 | const baseStyleBadge: SystemStyleFunction = (props) => { 13 | return { 14 | transform: "translate(25%, 25%)", 15 | borderRadius: "full", 16 | border: "0.2em solid", 17 | borderColor: mode("_gray.1", "_grayDark.1")(props), 18 | }; 19 | }; 20 | 21 | const baseStyleExcessLabel: SystemStyleFunction = (props) => { 22 | return { 23 | bg: mode("_gray.4", "_grayDark.4")(props), 24 | }; 25 | }; 26 | 27 | const baseStyleContainer: SystemStyleFunction = (props) => { 28 | const { name, theme } = props; 29 | const bg = name ? randomColor({ string: name }) : "_gray.9"; 30 | const isBgDark = isDark(bg)(theme); 31 | 32 | let color = "_gray.1"; 33 | if (!isBgDark) color = "_gray.11"; 34 | 35 | const borderColor = mode("_gray.1", "_grayDark.1")(props); 36 | 37 | return { 38 | bg, 39 | color, 40 | borderColor, 41 | verticalAlign: "top", 42 | }; 43 | }; 44 | 45 | const baseStyle: PartsStyleFunction = (props) => ({ 46 | badge: baseStyleBadge(props), 47 | excessLabel: baseStyleExcessLabel(props), 48 | container: baseStyleContainer(props), 49 | }); 50 | 51 | function getSize(size: string): PartsStyleObject { 52 | const themeSize = themeSizes[size as keyof Theme["sizes"]]; 53 | return { 54 | container: { 55 | width: size, 56 | height: size, 57 | fontSize: `calc(${themeSize ?? size} / 2.5)`, 58 | }, 59 | excessLabel: { 60 | width: size, 61 | height: size, 62 | }, 63 | label: { 64 | fontSize: `calc(${themeSize ?? size} / 2.5)`, 65 | lineHeight: size !== "100%" ? themeSize ?? size : undefined, 66 | }, 67 | }; 68 | } 69 | 70 | const sizes = { 71 | "2xs": getSize("4"), 72 | xs: getSize("6"), 73 | sm: getSize("8"), 74 | md: getSize("12"), 75 | lg: getSize("16"), 76 | xl: getSize("24"), 77 | "2xl": getSize("32"), 78 | full: getSize("100%"), 79 | }; 80 | 81 | const defaultProps = { 82 | size: "md", 83 | }; 84 | 85 | export default { 86 | parts: parts.keys, 87 | baseStyle, 88 | sizes, 89 | defaultProps, 90 | }; 91 | -------------------------------------------------------------------------------- /src/theme/components/radio/radio.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Radio, 5 | RadioGroup, 6 | StackProps, 7 | VStack, 8 | HStack, 9 | Box, 10 | Text, 11 | } from "@chakra-ui/react"; 12 | import { Decorators } from "util/storybook-utils"; 13 | import { useThemedColor } from "util/helpers"; 14 | 15 | export default { 16 | title: "Components / Radio", 17 | component: Radio, 18 | } as ComponentMeta; 19 | 20 | const NewDefault = (props: StackProps) => { 21 | const c = useThemedColor(); 22 | return ( 23 | 24 | 25 | New Theme 26 | 27 | 28 | 29 | 30 | 31 | Checked 32 | 33 | Unchecked 34 | 35 | 36 | 37 | 38 | 39 | Checked 40 | 41 | Unchecked 42 | 43 | 44 | 45 | 46 | ); 47 | }; 48 | 49 | const OldDefault = (props: StackProps) => { 50 | return ( 51 | 52 | 53 | New Theme 54 | 55 | 56 | 57 | 58 | 59 | Checked 60 | 61 | Unchecked 62 | 63 | 64 | 65 | 66 | 67 | Checked 68 | 69 | Unchecked 70 | 71 | 72 | 73 | 74 | ); 75 | }; 76 | 77 | export const Default: ComponentStory = (args) => ( 78 | } 80 | oldComponent={} 81 | /> 82 | ); 83 | -------------------------------------------------------------------------------- /src/theme/components/list/list.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | List, 5 | ListItem, 6 | ListIcon, 7 | StackProps, 8 | VStack, 9 | Box, 10 | Text, 11 | } from "@chakra-ui/react"; 12 | import { Decorators } from "util/storybook-utils"; 13 | import { MdCheckCircle } from "react-icons/md"; 14 | import { useThemedColor } from "util/helpers"; 15 | 16 | export default { 17 | title: "Components / List", 18 | component: List, 19 | } as ComponentMeta; 20 | 21 | const NewDefault = (props: StackProps) => { 22 | const c = useThemedColor(); 23 | return ( 24 | 25 | 26 | New Theme 27 | 28 | 29 | 30 | 31 | 32 | Lorem ipsum dolor sit amet, consectetur adipisicing elit 33 | 34 | 35 | 36 | Assumenda, quia temporibus eveniet a libero incidunt suscipit 37 | 38 | 39 | 40 | Quidem, ipsam illum quis sed voluptatum quae eum fugit earum 41 | 42 | 43 | 44 | 45 | ); 46 | }; 47 | 48 | const OldDefault = (props: StackProps) => { 49 | return ( 50 | 51 | 52 | New Theme 53 | 54 | 55 | 56 | 57 | 58 | Lorem ipsum dolor sit amet, consectetur adipisicing elit 59 | 60 | 61 | 62 | Assumenda, quia temporibus eveniet a libero incidunt suscipit 63 | 64 | 65 | 66 | Quidem, ipsam illum quis sed voluptatum quae eum fugit earum 67 | 68 | 69 | 70 | 71 | ); 72 | }; 73 | 74 | export const Default: ComponentStory = (args) => ( 75 | } 77 | oldComponent={} 78 | /> 79 | ); 80 | -------------------------------------------------------------------------------- /cypress/integration/components/close-button.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-closebutton--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | const getCloseButtons = () => cy.get("[data-testid=NewDefault] button"); 13 | const _grayA3 = "rgba(0, 37, 73, 0.055)"; 14 | const _grayA4 = "rgba(2, 28, 55, 0.075)"; 15 | 16 | // test the normal button 17 | const normal = getCloseButtons().eq(0); 18 | normal 19 | .realHover() 20 | .wait(200) 21 | .should("have.css", "background-color", _grayA3); 22 | normal 23 | .realMouseDown() 24 | .wait(200) 25 | .should("have.css", "background-color", _grayA4); 26 | 27 | // test the disabled button 28 | const disabled = getCloseButtons().eq(1); 29 | disabled 30 | .realHover() 31 | .wait(200) 32 | .should("have.css", "background-color", _grayA3) 33 | .should("have.css", "opacity", "0.4"); 34 | disabled 35 | .realMouseDown() 36 | .wait(200) 37 | .should("have.css", "background-color", _grayA4) 38 | .should("have.css", "opacity", "0.4"); 39 | }); 40 | 41 | it("displays in dark mode correctly", () => { 42 | cy.get("#switch-color-mode").click(); 43 | 44 | const getCloseButtons = () => cy.get("[data-testid=NewDefault] button"); 45 | const _grayDarkA3 = "rgba(214, 251, 252, 0.06)"; 46 | const _grayDarkA4 = "rgba(226, 240, 253, 0.082)"; 47 | 48 | // test the normal button 49 | const normal = getCloseButtons().eq(0); 50 | normal 51 | .realHover() 52 | .wait(200) 53 | .should("have.css", "background-color", _grayDarkA3); 54 | normal 55 | .realMouseDown() 56 | .wait(200) 57 | .should("have.css", "background-color", _grayDarkA4); 58 | 59 | // test the disabled button 60 | const disabled = getCloseButtons().eq(1); 61 | disabled 62 | .realHover() 63 | .wait(200) 64 | .should("have.css", "background-color", _grayDarkA3) 65 | .should("have.css", "opacity", "0.4"); 66 | disabled 67 | .realMouseDown() 68 | .wait(200) 69 | .should("have.css", "background-color", _grayDarkA4) 70 | .should("have.css", "opacity", "0.4"); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /cypress/integration/components/menu.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-menu--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").click(); 9 | }); 10 | 11 | it("displays in light mode correctly", () => { 12 | cy.get("[data-testid=NewDefault] .chakra-menu__menu-button").click(); 13 | 14 | // test the container 15 | cy.get("[data-testid=NewDefault] .chakra-menu__menu-list").should( 16 | "have.css", 17 | "background-color", 18 | getRgbFromThemeColor("_gray.1") 19 | ); 20 | 21 | // test the group title 22 | cy.get("[data-testid=NewDefault] .chakra-menu__group__title") 23 | .eq(0) 24 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")) 25 | .should("have.css", "font-weight", "600"); 26 | 27 | // test the menu item 28 | cy.get("[data-testid=NewDefault] .chakra-menu__menuitem") 29 | .eq(0) 30 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")) 31 | .realHover() 32 | .wait(200) 33 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.3")) 34 | .realMouseDown() 35 | .wait(200) 36 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.4")); 37 | }); 38 | 39 | it("displays in dark mode correctly", () => { 40 | cy.get("#switch-color-mode").click(); 41 | 42 | cy.get("[data-testid=NewDefault] .chakra-menu__menu-button").click(); 43 | 44 | // test the container 45 | cy.get("[data-testid=NewDefault] .chakra-menu__menu-list").should( 46 | "have.css", 47 | "background-color", 48 | getRgbFromThemeColor("_grayDark.1") 49 | ); 50 | 51 | // test the group title 52 | cy.get("[data-testid=NewDefault] .chakra-menu__group__title") 53 | .eq(0) 54 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")) 55 | .should("have.css", "font-weight", "600"); 56 | 57 | // test the menu item 58 | cy.get("[data-testid=NewDefault] .chakra-menu__menuitem") 59 | .eq(0) 60 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")) 61 | .realHover() 62 | .wait(200) 63 | .should( 64 | "have.css", 65 | "background-color", 66 | getRgbFromThemeColor("_grayDark.3") 67 | ) 68 | .realMouseDown() 69 | .wait(200) 70 | .should( 71 | "have.css", 72 | "background-color", 73 | getRgbFromThemeColor("_grayDark.4") 74 | ); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /src/theme/components/popover/popover.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Popover, 5 | PopoverArrow, 6 | PopoverBody, 7 | PopoverCloseButton, 8 | PopoverContent, 9 | PopoverHeader, 10 | PopoverTrigger, 11 | usePopover, 12 | chakra, 13 | StackProps, 14 | VStack, 15 | Box, 16 | Text, 17 | } from "@chakra-ui/react"; 18 | import { Decorators } from "util/storybook-utils"; 19 | import { useThemedColor } from "util/helpers"; 20 | 21 | export default { 22 | title: "Components / Popover", 23 | component: Popover, 24 | } as ComponentMeta; 25 | 26 | const NewDefault = (props: StackProps) => { 27 | const c = useThemedColor(); 28 | return ( 29 | 30 | 31 | New Theme 32 | 33 | 34 | 35 | 36 | Trigger 37 | 38 | 39 | 40 | 41 | Confirmation! 42 | 43 | Are you sure you want to have that milkshake? 44 | 45 | Yes 46 | No 47 | 48 | 49 | 50 | 51 | 52 | ); 53 | }; 54 | 55 | const OldDefault = (props: StackProps) => ( 56 | 57 | 58 | Old Theme 59 | 60 | 61 | 62 | 63 | Trigger 64 | 65 | 66 | 67 | 68 | Confirmation! 69 | 70 | Are you sure you want to have that milkshake? 71 | 72 | Yes 73 | No 74 | 75 | 76 | 77 | 78 | 79 | ); 80 | 81 | export const Default: ComponentStory = (args) => ( 82 | } 84 | oldComponent={} 85 | /> 86 | ); 87 | -------------------------------------------------------------------------------- /src/theme/components/tag/tag.ts: -------------------------------------------------------------------------------- 1 | import { tagAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleInterpolation, 4 | PartsStyleObject, 5 | SystemStyleObject, 6 | } from "@chakra-ui/theme-tools"; 7 | import Badge from "../badge/badge"; 8 | 9 | const baseStyleContainer: SystemStyleObject = { 10 | fontWeight: "medium", 11 | lineHeight: 1.2, 12 | outline: 0, 13 | _focus: { 14 | boxShadow: "outline", 15 | }, 16 | }; 17 | 18 | const baseStyleLabel: SystemStyleObject = { 19 | lineHeight: 1.2, 20 | overflow: "visible", 21 | }; 22 | 23 | const baseStyleCloseButton: SystemStyleObject = { 24 | fontSize: "18px", 25 | w: "1.25rem", 26 | h: "1.25rem", 27 | transitionProperty: "common", 28 | transitionDuration: "normal", 29 | borderRadius: "full", 30 | marginStart: "0.375rem", 31 | marginEnd: "-1", 32 | opacity: 0.5, 33 | _disabled: { 34 | opacity: 0.4, 35 | }, 36 | _focus: { 37 | boxShadow: "outline", 38 | bg: "rgba(0, 0, 0, 0.14)", 39 | }, 40 | _hover: { opacity: 0.8 }, 41 | _active: { opacity: 1 }, 42 | }; 43 | 44 | const baseStyle: PartsStyleObject = { 45 | container: baseStyleContainer, 46 | label: baseStyleLabel, 47 | closeButton: baseStyleCloseButton, 48 | }; 49 | 50 | const sizes: Record> = { 51 | sm: { 52 | container: { 53 | minH: "1.25rem", 54 | minW: "1.25rem", 55 | fontSize: "xs", 56 | px: 2, 57 | borderRadius: "md", 58 | }, 59 | closeButton: { 60 | marginEnd: "-2px", 61 | marginStart: "0.35rem", 62 | }, 63 | }, 64 | md: { 65 | container: { 66 | minH: "1.5rem", 67 | minW: "1.5rem", 68 | fontSize: "sm", 69 | borderRadius: "md", 70 | px: 2, 71 | }, 72 | }, 73 | lg: { 74 | container: { 75 | minH: 8, 76 | minW: 8, 77 | fontSize: "md", 78 | borderRadius: "md", 79 | px: 3, 80 | }, 81 | }, 82 | }; 83 | 84 | const variants: Record> = { 85 | subtle: (props) => ({ 86 | container: Badge.variants.subtle(props), 87 | }), 88 | solid: (props) => ({ 89 | container: Badge.variants.solid(props), 90 | }), 91 | outline: (props) => ({ 92 | container: Badge.variants.outline(props), 93 | }), 94 | }; 95 | 96 | const defaultProps = { 97 | size: "md", 98 | variant: "subtle", 99 | colorScheme: "gray", 100 | }; 101 | 102 | export default { 103 | parts: parts.keys, 104 | variants, 105 | baseStyle, 106 | sizes, 107 | defaultProps, 108 | }; 109 | -------------------------------------------------------------------------------- /src/theme/components/badge/badge.ts: -------------------------------------------------------------------------------- 1 | import { getColor, mode } from "@chakra-ui/theme-tools"; 2 | import type { 3 | SystemStyleFunction, 4 | SystemStyleObject, 5 | } from "@chakra-ui/theme-tools"; 6 | import { getColorInfo } from "../../../util/helpers"; 7 | 8 | const baseStyle: SystemStyleObject = { 9 | px: 1, 10 | textTransform: "uppercase", 11 | fontSize: "xs", 12 | borderRadius: "sm", 13 | fontWeight: "bold", 14 | }; 15 | 16 | const variantSolid: SystemStyleFunction = (props) => { 17 | const { colorScheme: c, theme } = props; 18 | const { light, dark, isBright, isDark } = getColorInfo(c, theme); 19 | const { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 20 | 21 | let [lightText, darkText] = [`${_gray}.1`, `${_grayDark}.1`]; 22 | if (isBright) [lightText, darkText] = [darkText, darkText]; 23 | if (isDark) [lightText, darkText] = [darkText, lightText]; 24 | 25 | return { 26 | bg: mode(`${light}.9`, `${dark}.9`)(props), 27 | color: mode(lightText, darkText)(props), 28 | }; 29 | }; 30 | 31 | const variantSubtle: SystemStyleFunction = (props) => { 32 | const { colorScheme: c, theme } = props; 33 | const { light, dark } = getColorInfo(c, theme); 34 | let { light: _gray, dark: _grayDark } = getColorInfo(c, theme); 35 | 36 | // when custom text colors, we must flip them here instead of with the 37 | // return from getColorInfo 38 | let [lightText, darkText] = [`${light}.11`, `${dark}.11`]; 39 | if (c.startsWith("_gray")) 40 | [lightText, darkText] = [`${_gray}.12`, `${_grayDark}.12`]; 41 | 42 | return { 43 | bg: mode(`${light}.4`, `${dark}.4`)(props), 44 | color: mode(lightText, darkText)(props), 45 | }; 46 | }; 47 | 48 | const variantOutline: SystemStyleFunction = (props) => { 49 | const { colorScheme: c, theme } = props; 50 | let { light, dark, isDark } = getColorInfo(c, theme); 51 | 52 | // when custom text colors, we must flip them here instead of with the 53 | // return from getColorInfo 54 | let [lightText, darkText] = [`${light}.9`, `${dark}.9`]; 55 | if (isDark) [lightText, darkText] = [darkText, lightText]; 56 | 57 | // get the color 58 | const lightColor = getColor(theme, lightText); 59 | const darkColor = getColor(theme, darkText); 60 | const color = mode(lightColor, darkColor)(props); 61 | 62 | return { 63 | color, 64 | boxShadow: `inset 0 0 0px 1px ${color}`, 65 | }; 66 | }; 67 | 68 | const variants = { 69 | solid: variantSolid, 70 | subtle: variantSubtle, 71 | outline: variantOutline, 72 | }; 73 | 74 | const defaultProps = { 75 | variant: "subtle", 76 | colorScheme: "_gray", 77 | }; 78 | 79 | export default { 80 | baseStyle, 81 | variants, 82 | defaultProps, 83 | }; 84 | -------------------------------------------------------------------------------- /src/theme/components/breadcrumb/breadcrumb.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { BrowserRouter, Link } from "react-router-dom"; 4 | import { 5 | Breadcrumb, 6 | BreadcrumbItem, 7 | BreadcrumbLink, 8 | StackProps, 9 | VStack, 10 | Box, 11 | Text, 12 | } from "@chakra-ui/react"; 13 | import { Decorators } from "util/storybook-utils"; 14 | import { useThemedColor } from "util/helpers"; 15 | 16 | export default { 17 | title: "Components / Breadcrumb", 18 | component: Breadcrumb, 19 | } as ComponentMeta; 20 | 21 | const NewDefault = (props: StackProps) => { 22 | const c = useThemedColor(); 23 | return ( 24 | 25 | 26 | New Theme 27 | 28 | 29 | 30 | 31 | 32 | 33 | Breadcrumb 1 34 | 35 | 36 | 37 | 38 | Breadcrumb 2 39 | 40 | 41 | 42 | Breadcrumb 3 43 | 44 | 45 | 46 | 47 | 48 | ); 49 | }; 50 | 51 | const OldDefault = (props: StackProps) => { 52 | return ( 53 | 54 | 55 | Old Theme 56 | 57 | 58 | 59 | 60 | 61 | 62 | Breadcrumb 1 63 | 64 | 65 | 66 | 67 | Breadcrumb 2 68 | 69 | 70 | 71 | Breadcrumb 3 72 | 73 | 74 | 75 | 76 | 77 | ); 78 | }; 79 | 80 | export const Default: ComponentStory = (args) => { 81 | return ( 82 | } 84 | oldComponent={} 85 | /> 86 | ); 87 | }; 88 | -------------------------------------------------------------------------------- /src/theme/components/drawer/drawer.ts: -------------------------------------------------------------------------------- 1 | import { drawerAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleFunction, 4 | PartsStyleObject, 5 | SystemStyleFunction, 6 | SystemStyleObject, 7 | } from "@chakra-ui/theme-tools"; 8 | import { mode } from "@chakra-ui/theme-tools"; 9 | import { getColorInfo } from "../../../util/helpers"; 10 | 11 | /** 12 | * Since the `maxWidth` prop references theme.sizes internally, 13 | * we can leverage that to size our modals. 14 | */ 15 | function getSize(value: string): PartsStyleObject { 16 | if (value === "full") { 17 | return { 18 | dialog: { maxW: "100vw", h: "100vh" }, 19 | }; 20 | } 21 | return { 22 | dialog: { maxW: value }, 23 | }; 24 | } 25 | 26 | const baseStyleOverlay: SystemStyleObject = { 27 | bg: "_grayA.10", 28 | zIndex: "overlay", 29 | }; 30 | 31 | const baseStyleDialogContainer: SystemStyleObject = { 32 | display: "flex", 33 | zIndex: "modal", 34 | justifyContent: "center", 35 | }; 36 | 37 | const baseStyleDialog: SystemStyleFunction = (props) => { 38 | const { isFullHeight, theme } = props; 39 | const { light, dark } = getColorInfo("_gray", theme); 40 | 41 | return { 42 | ...(isFullHeight && { height: "100vh" }), 43 | zIndex: "modal", 44 | maxH: "100vh", 45 | bg: mode(`${light}.1`, `${dark}.1`)(props), 46 | color: "inherit", 47 | boxShadow: mode("lg", "dark-lg")(props), 48 | }; 49 | }; 50 | 51 | const baseStyleHeader: SystemStyleObject = { 52 | px: 6, 53 | py: 4, 54 | fontSize: "xl", 55 | fontWeight: "semibold", 56 | }; 57 | 58 | const baseStyleCloseButton: SystemStyleObject = { 59 | position: "absolute", 60 | top: 2, 61 | insetEnd: 3, 62 | }; 63 | 64 | const baseStyleBody: SystemStyleObject = { 65 | px: 6, 66 | py: 2, 67 | flex: 1, 68 | overflow: "auto", 69 | }; 70 | 71 | const baseStyleFooter: SystemStyleObject = { 72 | px: 6, 73 | py: 4, 74 | }; 75 | 76 | const baseStyle: PartsStyleFunction = (props) => ({ 77 | overlay: baseStyleOverlay, 78 | dialogContainer: baseStyleDialogContainer, 79 | dialog: baseStyleDialog(props), 80 | header: baseStyleHeader, 81 | closeButton: baseStyleCloseButton, 82 | body: baseStyleBody, 83 | footer: baseStyleFooter, 84 | }); 85 | 86 | const sizes = { 87 | xs: getSize("xs"), 88 | sm: getSize("md"), 89 | md: getSize("lg"), 90 | lg: getSize("2xl"), 91 | xl: getSize("4xl"), 92 | full: getSize("full"), 93 | }; 94 | 95 | const defaultProps = { 96 | size: "xs", 97 | }; 98 | 99 | export default { 100 | parts: parts.keys, 101 | baseStyle, 102 | sizes, 103 | defaultProps, 104 | }; 105 | -------------------------------------------------------------------------------- /src/theme/components/menu/menu.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Menu, 5 | MenuButton, 6 | Button, 7 | MenuGroup, 8 | MenuItem, 9 | MenuOptionGroup, 10 | MenuItemOption, 11 | MenuDivider, 12 | MenuList, 13 | StackProps, 14 | VStack, 15 | Text, 16 | Box, 17 | } from "@chakra-ui/react"; 18 | import { Decorators } from "util/storybook-utils"; 19 | import { useThemedColor } from "util/helpers"; 20 | 21 | export default { 22 | title: "Components / Menu", 23 | component: Menu, 24 | } as ComponentMeta; 25 | 26 | const NewDefault = (props: StackProps) => { 27 | const c = useThemedColor(); 28 | return ( 29 | 30 | 31 | New Theme 32 | 33 | 34 | 35 | 36 | MenuItem 37 | 38 | 39 | 40 | Docs 41 | FAQ 42 | 43 | 44 | Ascending 45 | Descending 46 | 47 | 48 | 49 | 50 | 51 | ); 52 | }; 53 | 54 | const OldDefault = (props: StackProps) => { 55 | return ( 56 | 57 | 58 | New Theme 59 | 60 | 61 | 62 | 63 | MenuItem 64 | 65 | 66 | 67 | Docs 68 | FAQ 69 | 70 | 71 | Ascending 72 | Descending 73 | 74 | 75 | 76 | 77 | 78 | ); 79 | }; 80 | 81 | export const Default: ComponentStory = (args) => ( 82 | } 84 | oldComponent={} 85 | /> 86 | ); 87 | -------------------------------------------------------------------------------- /src/theme/components/container/container.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { Container, Box, VStack, Text, StackProps } from "@chakra-ui/react"; 4 | import { Decorators } from "util/storybook-utils"; 5 | import { useThemedColor } from "util/helpers"; 6 | 7 | export default { 8 | title: "Components / Container", 9 | component: Container, 10 | } as ComponentMeta; 11 | 12 | const NewDefault = (props: StackProps) => { 13 | const c = useThemedColor(); 14 | return ( 15 | 16 | 17 | New Theme 18 | 19 | 20 | 21 | Lorem Ipsum is simply dummy text of the printing and typesetting 22 | industry. Lorem Ipsum has been the industry's standard dummy text ever 23 | since the 1500s, when an unknown printer took a galley of type and 24 | scrambled it to make a type specimen book. It has survived not only 25 | five centuries, but also the leap into electronic typesetting, 26 | remaining essentially unchanged. It was popularised in the 1960s with 27 | the release of Letraset sheets containing Lorem Ipsum passages, and 28 | more recently with desktop publishing software like Aldus PageMaker 29 | including versions of Lorem Ipsum. 30 | 31 | 32 | 33 | ); 34 | }; 35 | 36 | const OldDefault = (props: StackProps) => { 37 | return ( 38 | 39 | 40 | Old Theme 41 | 42 | 43 | 44 | Lorem Ipsum is simply dummy text of the printing and typesetting 45 | industry. Lorem Ipsum has been the industry's standard dummy text ever 46 | since the 1500s, when an unknown printer took a galley of type and 47 | scrambled it to make a type specimen book. It has survived not only 48 | five centuries, but also the leap into electronic typesetting, 49 | remaining essentially unchanged. It was popularised in the 1960s with 50 | the release of Letraset sheets containing Lorem Ipsum passages, and 51 | more recently with desktop publishing software like Aldus PageMaker 52 | including versions of Lorem Ipsum. 53 | 54 | 55 | 56 | ); 57 | }; 58 | 59 | export const Default: ComponentStory = (args) => ( 60 | } 62 | oldComponent={} 63 | /> 64 | ); 65 | -------------------------------------------------------------------------------- /src/theme/components/modal/modal.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | useDisclosure, 5 | Modal, 6 | ModalBody, 7 | ModalCloseButton, 8 | ModalContent, 9 | ModalFooter, 10 | ModalHeader, 11 | ModalOverlay, 12 | StackProps, 13 | Button, 14 | VStack, 15 | HStack, 16 | Box, 17 | Text, 18 | } from "@chakra-ui/react"; 19 | import { Decorators } from "util/storybook-utils"; 20 | import { useThemedColor } from "util/helpers"; 21 | 22 | export default { 23 | title: "Components / Modal", 24 | component: Modal, 25 | } as ComponentMeta; 26 | 27 | const NewDefault = (props: StackProps) => { 28 | const { isOpen, onOpen, onClose } = useDisclosure(); 29 | return ( 30 | 31 | New Open 32 | 33 | 34 | 35 | 36 | Welcome Home 37 | 38 | Sit nulla est ex deserunt exercitation anim occaecat. Nostrud 39 | ullamco deserunt aute id consequat veniam incididunt duis in sint 40 | irure nisi. 41 | 42 | 43 | 44 | Cancel 45 | Save 46 | 47 | 48 | 49 | 50 | 51 | ); 52 | }; 53 | 54 | const OldDefault = (props: StackProps) => { 55 | const { isOpen, onOpen, onClose } = useDisclosure(); 56 | return ( 57 | 58 | Old Open 59 | 60 | 61 | 62 | 63 | Welcome Home 64 | 65 | Sit nulla est ex deserunt exercitation anim occaecat. Nostrud 66 | ullamco deserunt aute id consequat veniam incididunt duis in sint 67 | irure nisi. 68 | 69 | 70 | 71 | Cancel 72 | Save 73 | 74 | 75 | 76 | 77 | 78 | ); 79 | }; 80 | 81 | export const Default: ComponentStory = (args) => ( 82 | } 84 | oldComponent={} 85 | /> 86 | ); 87 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dub-stack/chakra-radix-colors", 3 | "version": "0.0.0-development", 4 | "description": "This package provides the @radix-ui/colors in a chakra project.", 5 | "main": "dist/index.js", 6 | "keywords": [ 7 | "react", 8 | "chakra-ui", 9 | "radix-ui", 10 | "css-in-js", 11 | "colors" 12 | ], 13 | "scripts": { 14 | "build": "tsc", 15 | "watch": "tsc -w", 16 | "jest": "jest --verbose", 17 | "cypress:open": "cypress open", 18 | "cypress:run": "cypress run", 19 | "storybook:dev": "start-storybook -p 6006", 20 | "storybook:ci": "start-storybook -p 6006 --ci", 21 | "build-storybook": "build-storybook", 22 | "semantic-release": "semantic-release" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/dub-stack/chakra-radix-colors.git" 27 | }, 28 | "author": "Spencer Duball ", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/dub-stack/chakra-radix-colors/issues" 32 | }, 33 | "homepage": "https://github.com/dub-stack/chakra-radix-colors#readme", 34 | "devDependencies": { 35 | "@babel/core": "^7.17.9", 36 | "@babel/preset-env": "^7.16.11", 37 | "@babel/preset-react": "^7.16.7", 38 | "@babel/preset-typescript": "^7.16.7", 39 | "@chakra-ui/icons": "^1.1.7", 40 | "@chakra-ui/react": "^1.8.8", 41 | "@chakra-ui/skip-nav": "^1.2.6", 42 | "@emotion/jest": "^11.9.1", 43 | "@emotion/react": "^11.9.0", 44 | "@emotion/styled": "^11.8.1", 45 | "@radix-ui/colors": "^0.1.8", 46 | "@semantic-release/commit-analyzer": "^9.0.2", 47 | "@semantic-release/github": "^8.0.4", 48 | "@semantic-release/npm": "^9.0.1", 49 | "@semantic-release/release-notes-generator": "^10.0.3", 50 | "@storybook/addon-actions": "^6.4.20", 51 | "@storybook/addon-essentials": "^6.4.20", 52 | "@storybook/addon-interactions": "^6.4.20", 53 | "@storybook/addon-links": "^6.4.20", 54 | "@storybook/react": "^6.4.22", 55 | "@storybook/testing-library": "^0.0.9", 56 | "@testing-library/react": "^12.1.4", 57 | "@types/jest": "^27.4.1", 58 | "@types/react-test-renderer": "^18.0.0", 59 | "babel-jest": "^27.5.1", 60 | "babel-loader": "^8.2.4", 61 | "color-convert": "^2.0.1", 62 | "cypress": "^9.6.0", 63 | "cypress-real-events": "^1.7.0", 64 | "framer-motion": "^6.2.9", 65 | "jest": "^27.5.1", 66 | "react": "^17.0.2", 67 | "react-dom": "^17.0.2", 68 | "react-icons": "^4.3.1", 69 | "react-router-dom": "^6.3.0", 70 | "react-spinners": "^0.11.0", 71 | "semantic-release": "^19.0.2", 72 | "ts-node": "^10.7.0", 73 | "tsconfig-paths": "^3.14.1", 74 | "typescript": "^4.6.3" 75 | }, 76 | "peerDependencies": { 77 | "@chakra-ui/react": "^1.8.8", 78 | "@radix-ui/colors": "^0.1.8" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/theme/foundations/colors.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "util/test-utils"; 3 | import { 4 | render as renderNoTheme, 5 | screen as screenNoTheme, 6 | } from "@testing-library/react"; 7 | import { Box } from "@chakra-ui/react"; 8 | 9 | describe("Renders colors from theme", () => { 10 | // When we pass a color value such as "amberA.5" (or any other color from the 11 | // ChakraTheme), we should recieve a css-variable created by chakra for the 12 | // given color. 13 | 14 | test("renders chakra theme css-variable for light colors", () => { 15 | render(); 16 | const box = screen.getByTestId("color-box"); 17 | expect(box).toHaveStyleRule("background", "var(--chakra-colors-sky-5)"); 18 | }); 19 | 20 | test("renders chakra theme css-variable for light-alpha colors", () => { 21 | render(); 22 | const box = screen.getByTestId("color-box"); 23 | expect(box).toHaveStyleRule("background", "var(--chakra-colors-amberA-12)"); 24 | }); 25 | 26 | test("renders chakra theme css-variable for dark colors", () => { 27 | render(); 28 | const box = screen.getByTestId("color-box"); 29 | expect(box).toHaveStyleRule("background", "var(--chakra-colors-skyDark-1)"); 30 | }); 31 | 32 | test("renders chakra theme css-variable for alpha-dark colors", () => { 33 | render(); 34 | const box = screen.getByTestId("color-box"); 35 | expect(box).toHaveStyleRule( 36 | "background", 37 | "var(--chakra-colors-amberDarkA-1)" 38 | ); 39 | }); 40 | }); 41 | 42 | describe("Doesn't render colors when not in theme", () => { 43 | // When we pass a color value such as "grass.5" we should recieve the same string 44 | // back when the theme lookup fails. 45 | test("when no theme provided, lookup fails", () => { 46 | renderNoTheme(); 47 | const box = screenNoTheme.getByTestId("color-box"); 48 | expect(box).toHaveStyleRule("background", "grass.5"); 49 | }); 50 | 51 | // When we don't specify a palette choice (1 - 12) we should recieve the same 52 | // string value passed back. 53 | test("when no palette choice, lookup fails", () => { 54 | render(); 55 | const box = screen.getByTestId("color-box"); 56 | expect(box).toHaveStyleRule("background", "skyDark"); 57 | }); 58 | 59 | // When we exceed the bounds of the color palette (1 - 12) we should recieve the 60 | // same string value passed back. 61 | test("when palette choice out-of-bounds, lookup fails", () => { 62 | render(); 63 | const box = screen.getByTestId("color-box"); 64 | expect(box).toHaveStyleRule("background", "blue.13"); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /src/theme/components/switch/switch.ts: -------------------------------------------------------------------------------- 1 | import { switchAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleFunction, 4 | PartsStyleObject, 5 | SystemStyleFunction, 6 | SystemStyleObject, 7 | } from "@chakra-ui/theme-tools"; 8 | import { calc, cssVar, mode } from "@chakra-ui/theme-tools"; 9 | import { getColorInfo } from "../../../util/helpers"; 10 | 11 | const $width = cssVar("switch-track-width"); 12 | const $height = cssVar("switch-track-height"); 13 | 14 | const $diff = cssVar("switch-track-diff"); 15 | const diffValue = calc.subtract($width, $height); 16 | 17 | const $translateX = cssVar("switch-thumb-x"); 18 | 19 | const baseStyleTrack: SystemStyleFunction = (props) => { 20 | const { colorScheme: c, theme } = props; 21 | const { light, dark, isDark } = getColorInfo(c, theme); 22 | let { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 23 | 24 | if (isDark) [_gray, _grayDark] = [_grayDark, _gray]; 25 | 26 | return { 27 | borderRadius: "full", 28 | p: "2px", 29 | width: [$width.reference], 30 | height: [$height.reference], 31 | transitionProperty: "common", 32 | transitionDuration: "fast", 33 | bg: mode(`${_gray}.7`, `${_grayDark}.7`)(props), 34 | _focus: { 35 | boxShadow: "outline", 36 | }, 37 | _disabled: { 38 | opacity: 0.4, 39 | cursor: "not-allowed", 40 | }, 41 | _checked: { 42 | bg: mode(`${light}.9`, `${dark}.9`)(props), 43 | }, 44 | }; 45 | }; 46 | 47 | const baseStyleThumb: SystemStyleObject = { 48 | bg: "white", 49 | transitionProperty: "transform", 50 | transitionDuration: "normal", 51 | borderRadius: "inherit", 52 | width: [$height.reference], 53 | height: [$height.reference], 54 | _checked: { 55 | transform: `translateX(${$translateX.reference})`, 56 | }, 57 | }; 58 | 59 | const baseStyle: PartsStyleFunction = (props) => ({ 60 | container: { 61 | [$diff.variable]: diffValue, 62 | [$translateX.variable]: $diff.reference, 63 | _rtl: { 64 | [$translateX.variable]: calc($diff).negate().toString(), 65 | }, 66 | }, 67 | track: baseStyleTrack(props), 68 | thumb: baseStyleThumb, 69 | }); 70 | 71 | const sizes: Record> = { 72 | sm: { 73 | container: { 74 | [$width.variable]: "1.375rem", 75 | [$height.variable]: "0.75rem", 76 | }, 77 | }, 78 | md: { 79 | container: { 80 | [$width.variable]: "1.875rem", 81 | [$height.variable]: "1rem", 82 | }, 83 | }, 84 | lg: { 85 | container: { 86 | [$width.variable]: "2.875rem", 87 | [$height.variable]: "1.5rem", 88 | }, 89 | }, 90 | }; 91 | 92 | const defaultProps = { 93 | size: "md", 94 | colorScheme: "blue", 95 | }; 96 | 97 | export default { 98 | parts: parts.keys, 99 | baseStyle, 100 | sizes, 101 | defaultProps, 102 | }; 103 | -------------------------------------------------------------------------------- /cypress/integration/components/avatar.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor, getRgbFromHex } from "util/cypress-utils"; 2 | import { randomColor } from "@chakra-ui/theme-tools"; 3 | 4 | describe("Default", () => { 5 | beforeEach(() => { 6 | cy.visit( 7 | "http://localhost:6006/iframe.html?id=components-avatar--default&args=&viewMode=story" 8 | ); 9 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 10 | }); 11 | 12 | it("displays in light mode correctly", () => { 13 | // test the avatar-with-badge 14 | cy.get("[data-testid=avatar-with-badge]") 15 | .should( 16 | "have.css", 17 | "background-color", 18 | getRgbFromHex(randomColor({ string: "Kent Dodds" })) 19 | ) 20 | .should("have.css", "color", getRgbFromThemeColor("_gray.1")) 21 | .should("have.css", "border-color", getRgbFromThemeColor("_gray.1")); 22 | 23 | // test badge border 24 | cy.get("[data-testid=avatar-with-badge] .chakra-avatar__badge").should( 25 | "have.css", 26 | "border-color", 27 | getRgbFromThemeColor("_gray.1") 28 | ); 29 | 30 | // test the avatar-no-name 31 | cy.get("[data-testid=avatar-no-name]") 32 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.9")) 33 | .should("have.css", "color", getRgbFromThemeColor("_gray.11")) 34 | .should("have.css", "border-color", getRgbFromThemeColor("_gray.1")); 35 | 36 | // test the excess label 37 | cy.get("[data-testid=NewDefault] .chakra-avatar__excess").should( 38 | "have.css", 39 | "background-color", 40 | getRgbFromThemeColor("_gray.4") 41 | ); 42 | }); 43 | 44 | it("displays in dark mode correctly", () => { 45 | cy.get("#switch-color-mode").click(); 46 | 47 | // test the avatar-with-badge 48 | cy.get("[data-testid=avatar-with-badge]") 49 | .should( 50 | "have.css", 51 | "background-color", 52 | getRgbFromHex(randomColor({ string: "Kent Dodds" })) 53 | ) 54 | .should("have.css", "color", getRgbFromThemeColor("_gray.1")) 55 | .should("have.css", "border-color", getRgbFromThemeColor("_grayDark.1")); 56 | 57 | // test badge border 58 | cy.get("[data-testid=avatar-with-badge] .chakra-avatar__badge").should( 59 | "have.css", 60 | "border-color", 61 | getRgbFromThemeColor("_grayDark.1") 62 | ); 63 | 64 | // test the avatar-no-name 65 | cy.get("[data-testid=avatar-no-name]") 66 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.9")) 67 | .should("have.css", "color", getRgbFromThemeColor("_gray.11")) 68 | .should("have.css", "border-color", getRgbFromThemeColor("_grayDark.1")); 69 | 70 | // test the excess label 71 | cy.get("[data-testid=NewDefault] .chakra-avatar__excess").should( 72 | "have.css", 73 | "background-color", 74 | getRgbFromThemeColor("_grayDark.4") 75 | ); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /src/theme/components/code/code.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { Code, VStack, Text, Box, StackProps } from "@chakra-ui/react"; 4 | import { Decorators } from "util/storybook-utils"; 5 | import { useThemedColor } from "util/helpers"; 6 | 7 | export default { 8 | title: "Components / Code", 9 | component: Code, 10 | }; 11 | 12 | const NewDefault = (props: StackProps) => { 13 | const c = useThemedColor(); 14 | return ( 15 | 16 | {/* display the light version in light mode */} 17 | 18 | New Theme 19 | 20 | 21 | 22 | import React from "react" 23 | 24 | import React from "react" 25 | 26 | 27 | import React from "react" 28 | 29 | 30 | 31 | {/* display the light version in dark mode */} 32 | 33 | 34 | import React from "react" 35 | 36 | import React from "react" 37 | 38 | 39 | import React from "react" 40 | 41 | 42 | 43 | 44 | ); 45 | }; 46 | 47 | const OldDefault = (props: StackProps) => { 48 | const c = useThemedColor(); 49 | return ( 50 | 51 | {/* display the light version in light mode */} 52 | 53 | New Theme 54 | 55 | 56 | 57 | import React from "react" 58 | 59 | import React from "react" 60 | 61 | 62 | import React from "react" 63 | 64 | 65 | 66 | {/* display the light version in dark mode */} 67 | 68 | 69 | import React from "react" 70 | 71 | import React from "react" 72 | 73 | 74 | import React from "react" 75 | 76 | 77 | 78 | 79 | ); 80 | }; 81 | 82 | export const Default: ComponentStory = (args) => ( 83 | } 85 | oldComponent={} 86 | /> 87 | ); 88 | -------------------------------------------------------------------------------- /src/theme/components/number-input/number-input.ts: -------------------------------------------------------------------------------- 1 | import { numberInputAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleFunction, 4 | PartsStyleObject, 5 | SystemStyleFunction, 6 | SystemStyleObject, 7 | } from "@chakra-ui/theme-tools"; 8 | import { calc, cssVar, mode } from "@chakra-ui/theme-tools"; 9 | import { theme } from "@chakra-ui/theme"; 10 | import Input from "../input/input"; 11 | import { getColorInfo } from "../../../util/helpers"; 12 | 13 | const { variants, defaultProps } = Input; 14 | 15 | const $stepperWidth = cssVar("number-input-stepper-width"); 16 | 17 | const $inputPadding = cssVar("number-input-input-padding"); 18 | const inputPaddingValue = calc($stepperWidth).add("0.5rem").toString(); 19 | 20 | const baseStyleRoot: SystemStyleObject = { 21 | [$stepperWidth.variable]: "24px", 22 | [$inputPadding.variable]: inputPaddingValue, 23 | }; 24 | 25 | const baseStyleField: SystemStyleObject = Input.baseStyle?.field ?? {}; 26 | 27 | const baseStyleStepperGroup: SystemStyleObject = { 28 | width: [$stepperWidth.reference], 29 | }; 30 | 31 | const baseStyleStepper: SystemStyleFunction = (props) => { 32 | const { theme } = props; 33 | const { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 34 | 35 | return { 36 | borderStart: "1px solid", 37 | borderStartColor: mode(`${_gray}.6`, `${_grayDark}.6`)(props), 38 | _active: { 39 | bg: mode(`${_gray}.5`, `${_grayDark}.5`)(props), 40 | }, 41 | _disabled: { 42 | opacity: 0.4, 43 | cursor: "not-allowed", 44 | }, 45 | }; 46 | }; 47 | 48 | const baseStyle: PartsStyleFunction = (props) => ({ 49 | root: baseStyleRoot, 50 | field: baseStyleField, 51 | stepperGroup: baseStyleStepperGroup, 52 | stepper: baseStyleStepper(props), 53 | }); 54 | 55 | type FontSize = keyof typeof theme.fontSizes; 56 | 57 | function getSize(size: FontSize): PartsStyleObject { 58 | const sizeStyle = Input.sizes[size]; 59 | 60 | const radius: Partial> = { 61 | lg: "md", 62 | md: "md", 63 | sm: "sm", 64 | xs: "sm", 65 | }; 66 | 67 | const _fontSize = (sizeStyle.field?.fontSize ?? "md") as FontSize; 68 | const fontSize = theme.fontSizes[_fontSize]; 69 | 70 | return { 71 | field: { 72 | ...sizeStyle.field, 73 | paddingInlineEnd: $inputPadding.reference, 74 | verticalAlign: "top", 75 | }, 76 | stepper: { 77 | fontSize: calc(fontSize).multiply(0.75).toString(), 78 | _first: { 79 | borderTopEndRadius: radius[size], 80 | }, 81 | _last: { 82 | borderBottomEndRadius: radius[size], 83 | mt: "-1px", 84 | borderTopWidth: 1, 85 | }, 86 | }, 87 | }; 88 | } 89 | 90 | const sizes = { 91 | xs: getSize("xs"), 92 | sm: getSize("sm"), 93 | md: getSize("md"), 94 | lg: getSize("lg"), 95 | }; 96 | 97 | export default { 98 | parts: parts.keys, 99 | baseStyle, 100 | sizes, 101 | variants, 102 | defaultProps, 103 | }; 104 | -------------------------------------------------------------------------------- /src/theme/components/progress/progress.ts: -------------------------------------------------------------------------------- 1 | import { progressAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import { 3 | generateStripe, 4 | getColor, 5 | mode, 6 | PartsStyleFunction, 7 | PartsStyleObject, 8 | StyleFunctionProps, 9 | SystemStyleObject, 10 | SystemStyleFunction, 11 | } from "@chakra-ui/theme-tools"; 12 | import { getColorInfo } from "../../../util/helpers"; 13 | 14 | function filledStyle(props: StyleFunctionProps): SystemStyleObject { 15 | const { colorScheme: c, theme, isIndeterminate, hasStripe } = props; 16 | const { light, dark, isDark, isBright } = getColorInfo(c, theme); 17 | 18 | let stripeStyle = isDark 19 | ? mode(generateStripe("1rem", "rgba(0,0,0,0.1)"), generateStripe())(props) 20 | : mode(generateStripe(), generateStripe("1rem", "rgba(0,0,0,0.1)"))(props); 21 | if (isBright) stripeStyle = generateStripe("1rem", "rgba(0,0,0,0.1)"); 22 | 23 | const bgColor = mode(`${light}.9`, `${dark}.9`)(props); 24 | 25 | const gradient = `linear-gradient( 26 | to right, 27 | transparent 0%, 28 | ${getColor(theme, bgColor)} 50%, 29 | transparent 100% 30 | )`; 31 | 32 | const addStripe = !isIndeterminate && hasStripe; 33 | 34 | return { 35 | ...(addStripe && stripeStyle), 36 | ...(isIndeterminate ? { bgImage: gradient } : { bgColor }), 37 | }; 38 | } 39 | 40 | const baseStyleLabel: SystemStyleFunction = (props) => { 41 | const { colorScheme: c, theme } = props; 42 | let { isBright } = getColorInfo(c, theme); 43 | 44 | let color = "_gray.1"; 45 | if (isBright) color = "_gray.12"; 46 | 47 | return { 48 | lineHeight: "1", 49 | fontSize: "0.25em", 50 | fontWeight: "bold", 51 | color, 52 | }; 53 | }; 54 | 55 | const baseStyleTrack: SystemStyleFunction = (props) => { 56 | const { colorScheme: c, theme } = props; 57 | let { isDark } = getColorInfo(c, theme); 58 | let { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 59 | 60 | if (isDark) [_grayDark, _gray] = [_gray, _grayDark]; 61 | 62 | return { 63 | bg: mode(`${_gray}A.3`, `${_grayDark}A.3`)(props), 64 | }; 65 | }; 66 | 67 | const baseStyleFilledTrack: SystemStyleFunction = (props) => { 68 | return { 69 | transitionProperty: "common", 70 | transitionDuration: "slow", 71 | ...filledStyle(props), 72 | }; 73 | }; 74 | 75 | const baseStyle: PartsStyleFunction = (props) => ({ 76 | label: baseStyleLabel(props), 77 | filledTrack: baseStyleFilledTrack(props), 78 | track: baseStyleTrack(props), 79 | }); 80 | 81 | const sizes: Record> = { 82 | xs: { 83 | track: { h: "0.25rem" }, 84 | }, 85 | sm: { 86 | track: { h: "0.5rem" }, 87 | }, 88 | md: { 89 | track: { h: "0.75rem" }, 90 | }, 91 | lg: { 92 | track: { h: "1rem" }, 93 | }, 94 | }; 95 | 96 | const defaultProps = { 97 | size: "md", 98 | colorScheme: "blue", 99 | }; 100 | 101 | export default { 102 | parts: parts.keys, 103 | sizes, 104 | baseStyle, 105 | defaultProps, 106 | }; 107 | -------------------------------------------------------------------------------- /src/theme/components/accordion/accordion.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Accordion, 5 | AccordionItem, 6 | AccordionButton, 7 | chakra, 8 | AccordionIcon, 9 | AccordionPanel, 10 | StackProps, 11 | VStack, 12 | Text, 13 | Box, 14 | } from "@chakra-ui/react"; 15 | import { Decorators } from "util/storybook-utils"; 16 | 17 | export default { 18 | title: "Components / Accordion", 19 | component: Accordion, 20 | } as ComponentMeta; 21 | 22 | const NewDefault = (props: StackProps) => { 23 | return ( 24 | 25 | 26 | New Theme 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Section 1 title 35 | 36 | 37 | 38 | 39 | Panel 1 40 | 41 | 42 | 43 | 44 | 45 | 46 | Section 2 title 47 | 48 | 49 | 50 | 51 | Panel 2 52 | 53 | 54 | 55 | 56 | ); 57 | }; 58 | const OldDefault = (props: StackProps) => { 59 | return ( 60 | 61 | 62 | Old Theme 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Section 1 title 71 | 72 | 73 | 74 | 75 | Panel 1 76 | 77 | 78 | 79 | 80 | 81 | 82 | Section 2 title 83 | 84 | 85 | 86 | 87 | Panel 2 88 | 89 | 90 | 91 | 92 | ); 93 | }; 94 | 95 | export const Default: ComponentStory = (args) => { 96 | return ( 97 | } 99 | oldComponent={} 100 | /> 101 | ); 102 | }; 103 | -------------------------------------------------------------------------------- /src/util/storybook-utils.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { 3 | IconButton, 4 | ChakraProvider, 5 | Flex, 6 | useColorMode, 7 | } from "@chakra-ui/react"; 8 | import { RiMoonFill, RiRecycleFill } from "react-icons/ri"; 9 | import { theme } from "../theme"; 10 | 11 | /** 12 | * Effectively a hook that is used to toggle the colorMode for a Provider. 13 | * This is needed instead of just a hook because we must place this downstream from 14 | * a provider. 15 | * 16 | * @param props 17 | * @returns 18 | */ 19 | const SetColorMode = (props: { colorMode: string }) => { 20 | const { colorMode, setColorMode } = useColorMode(); 21 | if (colorMode !== props.colorMode) setColorMode(props.colorMode); 22 | return <>>; 23 | }; 24 | 25 | /** 26 | * Returns a chakra provider with the new package theme. 27 | * 28 | * @param props The color mode: "light" | "dark" 29 | * @returns 30 | */ 31 | const NewDecorator: React.FC<{ colorMode: string }> = (props) => ( 32 | 33 | 34 | {props.children} 35 | 36 | ); 37 | 38 | /** 39 | * Returns a chakra provider with the orignal chakra-ui theme. 40 | * 41 | * @param props The color mode: "light" | "dark" 42 | * @returns 43 | */ 44 | const OldDecorator: React.FC<{ colorMode: string }> = (props) => ( 45 | 46 | 47 | {props.children} 48 | 49 | ); 50 | 51 | /** 52 | * The wrapper for all storybook components. This allows for easy visual 53 | * comparison between the old chakra theme, and the new package theme. 54 | * 55 | * @param props The components for the new/old providers. 56 | * @returns 57 | */ 58 | export const Decorators = (props: { 59 | newComponent: React.ReactNode; 60 | oldComponent: React.ReactNode; 61 | }) => { 62 | const [colorMode, setColorMode] = useState("light"); 63 | return ( 64 | <> 65 | } 75 | onClick={() => setColorMode(colorMode === "light" ? "dark" : "light")} 76 | /> 77 | } 87 | onClick={() => setColorMode("light")} 88 | /> 89 | 95 | {props.oldComponent} 96 | {props.newComponent} 97 | 98 | > 99 | ); 100 | }; 101 | -------------------------------------------------------------------------------- /src/theme/components/modal/modal.ts: -------------------------------------------------------------------------------- 1 | import { modalAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleFunction, 4 | PartsStyleObject, 5 | SystemStyleFunction, 6 | SystemStyleObject, 7 | } from "@chakra-ui/theme-tools"; 8 | import { mode } from "@chakra-ui/theme-tools"; 9 | import { getColorInfo } from "../../../util/helpers"; 10 | 11 | const baseStyleOverlay: SystemStyleObject = { 12 | bg: "_grayA.10", 13 | zIndex: "modal", 14 | }; 15 | 16 | const baseStyleDialogContainer: SystemStyleFunction = (props) => { 17 | const { isCentered, scrollBehavior } = props; 18 | 19 | return { 20 | display: "flex", 21 | zIndex: "modal", 22 | justifyContent: "center", 23 | alignItems: isCentered ? "center" : "flex-start", 24 | overflow: scrollBehavior === "inside" ? "hidden" : "auto", 25 | }; 26 | }; 27 | 28 | const baseStyleDialog: SystemStyleFunction = (props) => { 29 | const { scrollBehavior, theme } = props; 30 | const { light: _gray, dark: _darkGray } = getColorInfo("_gray", theme); 31 | 32 | return { 33 | borderRadius: "md", 34 | bg: mode(`${_gray}.1`, `${_darkGray}.1`)(props), 35 | color: "inherit", 36 | my: "3.75rem", 37 | zIndex: "modal", 38 | maxH: scrollBehavior === "inside" ? "calc(100% - 7.5rem)" : undefined, 39 | boxShadow: mode("lg", "dark-lg")(props), 40 | }; 41 | }; 42 | 43 | const baseStyleHeader: SystemStyleObject = { 44 | px: 6, 45 | py: 4, 46 | fontSize: "xl", 47 | fontWeight: "semibold", 48 | }; 49 | 50 | const baseStyleCloseButton: SystemStyleObject = { 51 | position: "absolute", 52 | top: 2, 53 | insetEnd: 3, 54 | }; 55 | 56 | const baseStyleBody: SystemStyleFunction = (props) => { 57 | const { scrollBehavior } = props; 58 | return { 59 | px: 6, 60 | py: 2, 61 | flex: 1, 62 | overflow: scrollBehavior === "inside" ? "auto" : undefined, 63 | }; 64 | }; 65 | 66 | const baseStyleFooter: SystemStyleObject = { 67 | px: 6, 68 | py: 4, 69 | }; 70 | 71 | const baseStyle: PartsStyleFunction = (props) => ({ 72 | overlay: baseStyleOverlay, 73 | dialogContainer: baseStyleDialogContainer(props), 74 | dialog: baseStyleDialog(props), 75 | header: baseStyleHeader, 76 | closeButton: baseStyleCloseButton, 77 | body: baseStyleBody(props), 78 | footer: baseStyleFooter, 79 | }); 80 | 81 | /** 82 | * Since the `maxWidth` prop references theme.sizes internally, 83 | * we can leverage that to size our modals. 84 | */ 85 | function getSize(value: string): PartsStyleObject { 86 | if (value === "full") { 87 | return { 88 | dialog: { 89 | maxW: "100vw", 90 | minH: "100vh", 91 | "@supports(min-height: -webkit-fill-available)": { 92 | minH: "-webkit-fill-available", 93 | }, 94 | my: 0, 95 | }, 96 | }; 97 | } 98 | return { 99 | dialog: { maxW: value }, 100 | }; 101 | } 102 | 103 | const sizes = { 104 | xs: getSize("xs"), 105 | sm: getSize("sm"), 106 | md: getSize("md"), 107 | lg: getSize("lg"), 108 | xl: getSize("xl"), 109 | "2xl": getSize("2xl"), 110 | "3xl": getSize("3xl"), 111 | "4xl": getSize("4xl"), 112 | "5xl": getSize("5xl"), 113 | "6xl": getSize("6xl"), 114 | full: getSize("full"), 115 | }; 116 | 117 | const defaultProps = { 118 | size: "md", 119 | }; 120 | 121 | export default { 122 | parts: parts.keys, 123 | baseStyle, 124 | sizes, 125 | defaultProps, 126 | }; 127 | -------------------------------------------------------------------------------- /src/theme/components/checkbox/checkbox.ts: -------------------------------------------------------------------------------- 1 | import { checkboxAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import type { 3 | PartsStyleFunction, 4 | PartsStyleObject, 5 | SystemStyleFunction, 6 | SystemStyleObject, 7 | } from "@chakra-ui/theme-tools"; 8 | import { mode } from "@chakra-ui/theme-tools"; 9 | import { getColorInfo } from "../../../util/helpers"; 10 | 11 | const baseStyleControl: SystemStyleFunction = (props) => { 12 | const { colorScheme: c, theme } = props; 13 | let { light, dark, isBright, isDark } = getColorInfo(c, theme); 14 | let { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 15 | let { light: red, dark: redDark } = getColorInfo("red", theme); 16 | 17 | let [color, disabledColor] = [`${_gray}.1`, `${_gray}.9`]; 18 | if (isBright) [color, disabledColor] = [`${_grayDark}.1`, `${_grayDark}.9`]; 19 | if (isDark) [_gray, _grayDark] = [_grayDark, _gray]; 20 | 21 | return { 22 | w: "100%", 23 | transitionProperty: "box-shadow", 24 | transitionDuration: "normal", 25 | border: "2px solid", 26 | borderRadius: "sm", 27 | borderColor: "inherit", 28 | color, 29 | 30 | _checked: { 31 | bg: mode(`${light}.9`, `${dark}.9`)(props), 32 | borderColor: mode(`${light}.9`, `${dark}.9`)(props), 33 | color, 34 | 35 | _hover: { 36 | bg: mode(`${light}.10`, `${dark}.10`)(props), 37 | borderColor: mode(`${light}.10`, `${dark}.10`)(props), 38 | }, 39 | 40 | _disabled: { 41 | borderColor: mode(`${_gray}.4`, `${_grayDark}.4`)(props), 42 | bg: mode(`${_gray}.4`, `${_grayDark}.4`)(props), 43 | color: disabledColor, 44 | }, 45 | }, 46 | 47 | _indeterminate: { 48 | bg: mode(`${light}.9`, `${dark}.9`)(props), 49 | borderColor: mode(`${light}.9`, `${dark}.9`)(props), 50 | color, 51 | }, 52 | 53 | _disabled: { 54 | bg: mode(`${_gray}.3`, `${_grayDark}.3`)(props), 55 | borderColor: mode(`${_gray}.3`, `${_grayDark}.3`)(props), 56 | }, 57 | 58 | _focus: { 59 | boxShadow: `outline`, 60 | }, 61 | 62 | _invalid: { 63 | borderColor: mode(`${red}.9`, `${redDark}.9`)(props), 64 | }, 65 | }; 66 | }; 67 | 68 | const baseStyleContainer: SystemStyleObject = { 69 | _disabled: { cursor: "not-allowed" }, 70 | }; 71 | 72 | const baseStyleLabel: SystemStyleObject = { 73 | userSelect: "none", 74 | _disabled: { opacity: 0.4 }, 75 | }; 76 | 77 | const baseStyleIcon: SystemStyleObject = { 78 | transitionProperty: "transform", 79 | transitionDuration: "normal", 80 | }; 81 | 82 | const baseStyle: PartsStyleFunction = (props) => ({ 83 | icon: baseStyleIcon, 84 | container: baseStyleContainer, 85 | control: baseStyleControl(props), 86 | label: baseStyleLabel, 87 | }); 88 | 89 | const sizes: Record> = { 90 | sm: { 91 | control: { h: 3, w: 3 }, 92 | label: { fontSize: "sm" }, 93 | icon: { fontSize: "0.45rem" }, 94 | }, 95 | md: { 96 | control: { w: 4, h: 4 }, 97 | label: { fontSize: "md" }, 98 | icon: { fontSize: "0.625rem" }, 99 | }, 100 | lg: { 101 | control: { w: 5, h: 5 }, 102 | label: { fontSize: "lg" }, 103 | icon: { fontSize: "0.625rem" }, 104 | }, 105 | }; 106 | 107 | const defaultProps = { 108 | size: "md", 109 | colorScheme: "blue", 110 | }; 111 | 112 | export default { 113 | parts: parts.keys, 114 | baseStyle, 115 | sizes, 116 | defaultProps, 117 | }; 118 | -------------------------------------------------------------------------------- /src/theme/components/drawer/drawer.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Drawer, 5 | DrawerContent, 6 | DrawerBody, 7 | DrawerOverlay, 8 | DrawerHeader, 9 | DrawerFooter, 10 | Input, 11 | DrawerCloseButton, 12 | Button, 13 | useDisclosure, 14 | StackProps, 15 | VStack, 16 | Box, 17 | Text, 18 | } from "@chakra-ui/react"; 19 | import { Decorators } from "util/storybook-utils"; 20 | import { useThemedColor } from "util/helpers"; 21 | 22 | export default { 23 | title: "Components / Drawer", 24 | component: Drawer, 25 | } as ComponentMeta; 26 | 27 | const NewDefault = (props: StackProps) => { 28 | const c = useThemedColor(); 29 | const btnRef = React.useRef(null); 30 | const { isOpen, onClose, onOpen } = useDisclosure(); 31 | 32 | return ( 33 | 34 | 35 | New Theme 36 | 37 | 38 | 39 | Open New 40 | 41 | 47 | 48 | 49 | 50 | Create your account 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Cancel 59 | 60 | Save 61 | 62 | 63 | 64 | 65 | 66 | ); 67 | }; 68 | 69 | const OldDefault = (props: StackProps) => { 70 | const c = useThemedColor(); 71 | const btnRef = React.useRef(null); 72 | const { isOpen, onClose, onOpen } = useDisclosure(); 73 | 74 | return ( 75 | 76 | 77 | Old Theme 78 | 79 | 80 | 81 | Open Old 82 | 83 | 89 | 90 | 91 | 92 | Create your account 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | Cancel 101 | 102 | Save 103 | 104 | 105 | 106 | 107 | 108 | ); 109 | }; 110 | 111 | export const Default: ComponentStory = (args) => ( 112 | } 114 | oldComponent={} 115 | /> 116 | ); 117 | -------------------------------------------------------------------------------- /src/theme/components/alert/alert.ts: -------------------------------------------------------------------------------- 1 | import { alertAnatomy as parts } from "@chakra-ui/anatomy"; 2 | import { mode } from "@chakra-ui/theme-tools"; 3 | import type { 4 | PartsStyleObject, 5 | PartsStyleFunction, 6 | StyleFunctionProps, 7 | } from "@chakra-ui/theme-tools"; 8 | import { getColorInfo } from "../../../util/helpers"; 9 | 10 | // Please visit: https://www.radix-ui.com/docs/colors/palette-composition/understanding-the-scale 11 | // to understand which values were used where. This link provides a style guide for using colors 12 | // for different UI purposes. 13 | 14 | const baseStyle: PartsStyleObject = { 15 | container: { 16 | px: 4, 17 | py: 3, 18 | }, 19 | title: { 20 | fontWeight: "bold", 21 | lineHeight: 6, 22 | marginEnd: 2, 23 | }, 24 | description: { 25 | lineHeight: 6, 26 | }, 27 | icon: { 28 | flexShrink: 0, 29 | marginEnd: 3, 30 | w: 5, 31 | h: 6, 32 | }, 33 | }; 34 | 35 | function getBg(props: StyleFunctionProps): string { 36 | const { theme, colorScheme: c } = props; 37 | const { light, dark } = getColorInfo(c, theme); 38 | 39 | return mode(`${light}.4`, `${dark}.4`)(props); 40 | } 41 | 42 | const variantSubtle: PartsStyleFunction = (props) => { 43 | const { theme, colorScheme: c } = props; 44 | let { light, dark, isDark } = getColorInfo(c, theme); 45 | let { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 46 | 47 | if (isDark) [_gray, _grayDark] = [_grayDark, _gray]; 48 | 49 | return { 50 | container: { 51 | bg: getBg(props), 52 | color: mode(`${_grayDark}.1`, `${_gray}.1`)(props), 53 | }, 54 | icon: { color: mode(`${light}.9`, `${dark}.9`)(props) }, 55 | }; 56 | }; 57 | 58 | const variantLeftAccent: PartsStyleFunction = (props) => { 59 | const { theme, colorScheme: c } = props; 60 | let { light, dark, isDark } = getColorInfo(c, theme); 61 | let { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 62 | 63 | if (isDark) [_gray, _grayDark] = [_grayDark, _gray]; 64 | 65 | return { 66 | container: { 67 | paddingStart: 3, 68 | borderStartWidth: "4px", 69 | borderStartColor: mode(`${light}.9`, `${dark}.9`)(props), 70 | bg: getBg(props), 71 | color: mode(`${_grayDark}.1`, `${_gray}.1`)(props), 72 | }, 73 | icon: { 74 | color: mode(`${light}.9`, `${dark}.9`)(props), 75 | }, 76 | }; 77 | }; 78 | 79 | const variantTopAccent: PartsStyleFunction = (props) => { 80 | const { theme, colorScheme: c } = props; 81 | let { light, dark, isDark } = getColorInfo(c, theme); 82 | let { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 83 | 84 | if (isDark) [_gray, _grayDark] = [_grayDark, _gray]; 85 | 86 | return { 87 | container: { 88 | pt: 2, 89 | borderTopWidth: "4px", 90 | borderTopColor: mode(`${light}.9`, `${dark}.9`)(props), 91 | bg: getBg(props), 92 | color: mode(`${_grayDark}.1`, `${_gray}.1`)(props), 93 | }, 94 | icon: { 95 | color: mode(`${light}.9`, `${dark}.9`)(props), 96 | }, 97 | }; 98 | }; 99 | 100 | const variantSolid: PartsStyleFunction = (props) => { 101 | const { theme, colorScheme: c } = props; 102 | let { light, dark, isBright, isDark } = getColorInfo(c, theme); 103 | let { light: _gray, dark: _grayDark } = getColorInfo("_gray", theme); 104 | 105 | if (isBright) [_gray, _grayDark] = [_grayDark, _grayDark]; 106 | if (isDark) [_gray, _grayDark] = [_grayDark, _gray]; 107 | 108 | return { 109 | container: { 110 | bg: mode(`${light}.9`, `${dark}.9`)(props), 111 | color: mode(`${_gray}.1`, `${_grayDark}.1`)(props), 112 | }, 113 | }; 114 | }; 115 | 116 | const variants = { 117 | subtle: variantSubtle, 118 | "left-accent": variantLeftAccent, 119 | "top-accent": variantTopAccent, 120 | solid: variantSolid, 121 | }; 122 | 123 | const defaultProps = { 124 | variant: "subtle", 125 | colorScheme: "blue", 126 | }; 127 | 128 | export default { 129 | parts: parts.keys, 130 | baseStyle, 131 | variants, 132 | defaultProps, 133 | }; 134 | -------------------------------------------------------------------------------- /cypress/integration/components/table.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Simple Variant", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-table--simple&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays light color in light mode correctly", () => { 12 | const getTable = () => 13 | cy.get("[data-testid=NewSimple] [data-testid=light-table]"); 14 | 15 | getTable().within(() => { 16 | // test th 17 | cy.get("th") 18 | .eq(0) 19 | .should("have.css", "color", getRgbFromThemeColor("_gray.11")) 20 | .should("have.css", "border-color", getRgbFromThemeColor("purple.6")); 21 | 22 | // test td 23 | cy.get("td") 24 | .eq(0) 25 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")) 26 | .should("have.css", "border-color", getRgbFromThemeColor("purple.6")); 27 | 28 | // test caption 29 | cy.get("caption").should( 30 | "have.css", 31 | "color", 32 | getRgbFromThemeColor("_gray.11") 33 | ); 34 | }); 35 | }); 36 | 37 | it("displays light color in dar mode correctly", () => { 38 | cy.get("#switch-color-mode").click(); 39 | 40 | const getTable = () => 41 | cy.get("[data-testid=NewSimple] [data-testid=light-table]"); 42 | 43 | getTable().within(() => { 44 | // test th 45 | cy.get("th") 46 | .eq(0) 47 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.11")) 48 | .should( 49 | "have.css", 50 | "border-color", 51 | getRgbFromThemeColor("purpleDark.6") 52 | ); 53 | 54 | // test td 55 | cy.get("td") 56 | .eq(0) 57 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")) 58 | .should( 59 | "have.css", 60 | "border-color", 61 | getRgbFromThemeColor("purpleDark.6") 62 | ); 63 | 64 | // test caption 65 | cy.get("caption").should( 66 | "have.css", 67 | "color", 68 | getRgbFromThemeColor("_grayDark.11") 69 | ); 70 | }); 71 | }); 72 | 73 | it("displays dark color in light mode correctly", () => { 74 | const getTable = () => 75 | cy.get("[data-testid=NewSimple] [data-testid=dark-table]"); 76 | 77 | getTable().within(() => { 78 | // test th 79 | cy.get("th") 80 | .eq(0) 81 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.11")) 82 | .should( 83 | "have.css", 84 | "border-color", 85 | getRgbFromThemeColor("purpleDark.6") 86 | ); 87 | 88 | // test td 89 | cy.get("td") 90 | .eq(0) 91 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")) 92 | .should( 93 | "have.css", 94 | "border-color", 95 | getRgbFromThemeColor("purpleDark.6") 96 | ); 97 | 98 | // test caption 99 | cy.get("caption").should( 100 | "have.css", 101 | "color", 102 | getRgbFromThemeColor("_grayDark.11") 103 | ); 104 | }); 105 | }); 106 | 107 | it("displays dark color in dar mode correctly", () => { 108 | cy.get("#switch-color-mode").click(); 109 | 110 | const getTable = () => 111 | cy.get("[data-testid=NewSimple] [data-testid=dark-table]"); 112 | 113 | getTable().within(() => { 114 | // test th 115 | cy.get("th") 116 | .eq(0) 117 | .should("have.css", "color", getRgbFromThemeColor("_gray.11")) 118 | .should("have.css", "border-color", getRgbFromThemeColor("purple.6")); 119 | 120 | // test td 121 | cy.get("td") 122 | .eq(0) 123 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")) 124 | .should("have.css", "border-color", getRgbFromThemeColor("purple.6")); 125 | 126 | // test caption 127 | cy.get("caption").should( 128 | "have.css", 129 | "color", 130 | getRgbFromThemeColor("_gray.11") 131 | ); 132 | }); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /src/util/helpers.tsx: -------------------------------------------------------------------------------- 1 | import { useColorMode } from "@chakra-ui/react"; 2 | import { Dict } from "@chakra-ui/utils"; 3 | 4 | /** 5 | * Accepts a colorMode ("light" | "dark") and returns a function that accepts a color ("red.4"). 6 | * This color is turned into the opposite-mode value when mode is turned to "dark" from "light" 7 | * and vise-versa. 8 | * 9 | * @param mode "light" | "dark" 10 | * @returns Function that accepts string of a color and returns the themed 11 | * color. 12 | * @example 13 | * getThemedColor("light")("blue.4") // "blue.4"; 14 | * getThemedColor("dark")("blueA.5") // "blueDarkA.5" 15 | */ 16 | export const getThemedColor = (colorMode: string) => (color: string) => { 17 | if (typeof color !== "string") return color; 18 | 19 | const [base, scaleIndex] = color.split("."); 20 | const [isDark, isA] = [base.includes("Dark"), base.includes("A")]; 21 | 22 | // ensure the scaleIndex is not undefined and is within the ColorScale range 23 | if (!scaleIndex) return color; 24 | if (parseInt(scaleIndex) > 12 || parseInt(scaleIndex) < 1) return color; 25 | 26 | // search for the base, ex: "blue" from color "blueDarkA" 27 | // we can search for contiguous lowercase to find this 28 | const baseSearch = base.match(/^_?[a-z]+/); 29 | if (!baseSearch) return color; 30 | 31 | // create new base 32 | let newBase = baseSearch[0]; 33 | const A = isA ? "A" : ""; 34 | const Dark = 35 | colorMode === "light" ? (isDark ? "Dark" : "") : isDark ? "" : "Dark"; 36 | 37 | return `${newBase}${Dark}${A}.${scaleIndex}`; 38 | }; 39 | 40 | /** 41 | * Hook that returns a function, (color: string) => themedColor. 42 | * Where the themedColor of the function returned will change with the color mode. 43 | * 44 | * @returns Color mode adaptive color function. 45 | * @example 46 | * const c = useThemedColor(); 47 | * c("blue.4") // blue.4 when light; blueDark.4 when dark 48 | */ 49 | export function useThemedColor() { 50 | const { colorMode } = useColorMode(); 51 | const mode = colorMode === "dark" ? "dark" : "light"; 52 | 53 | return getThemedColor(mode); 54 | } 55 | 56 | /** 57 | * Returns a [lightBaseColor, darkBaseColor] tuple with the light and 58 | * dark colors of the input base color. 59 | * 60 | * @param color A base color string from theme. Ex: "teal" 61 | * @param theme The theme with radix-ui color palettes. 62 | * @returns A color tuple of the [lightBaseColor, darkBaseColor]. 63 | * @example 64 | * const [original, flipped] = getBaseColorPair("red", theme); 65 | * // returns ["red", "redDark"] 66 | */ 67 | export function getBaseColorPair(palette: string, theme: Dict) { 68 | const [isDark, isA] = [palette.includes("Dark"), palette.includes("A")]; 69 | 70 | // make sure the passed color matches a theme color exactly 71 | if (!theme.colors[palette]) return [palette, palette]; 72 | 73 | // search for the base ex: "blue" from color "blueDarkA" 74 | // we can search for contiguous lowercase to find this 75 | const baseSearch = palette.match(/^_?[a-z]+/)!; 76 | 77 | // create new base 78 | let newBase = baseSearch[0]; 79 | const A = isA ? "A" : ""; 80 | 81 | // get light and dark base 82 | const lightBase = `${newBase}${isDark ? "Dark" : ""}${A}`; 83 | const darkBase = `${newBase}${isDark ? "" : "Dark"}${A}`; 84 | 85 | return [`${lightBase}`, `${darkBase}`]; 86 | } 87 | 88 | // Bright colors are meant to be used with different text color. To see the list of 89 | // bright colors visit: https://www.radix-ui.com/docs/colors/palette-composition/the-scales#bright-colors 90 | export const BrightColors = { 91 | Sky: "sky", 92 | Mint: "mint", 93 | Lime: "lime", 94 | Yellow: "yellow", 95 | Amber: "amber", 96 | } as const; 97 | 98 | export function getColorInfo(palette: string, theme: Dict) { 99 | const [light, dark] = getBaseColorPair(palette, theme); 100 | 101 | // get the lightText & darkText 102 | const isDark = palette.includes("Dark"); 103 | 104 | // determine if it is a bright color 105 | const isBright = Object.values(BrightColors).some((brightColor) => 106 | palette.startsWith(brightColor) 107 | ); 108 | 109 | // determine if is an alpha color 110 | const isA = palette.includes("A"); 111 | 112 | return { 113 | light, 114 | dark, 115 | isDark, 116 | isBright, 117 | isA, 118 | }; 119 | } 120 | -------------------------------------------------------------------------------- /src/theme/components/table/table.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Table, 5 | TableCaption, 6 | TableProps, 7 | Tbody, 8 | Td, 9 | Th, 10 | Thead, 11 | Tr, 12 | StackProps, 13 | VStack, 14 | Text, 15 | Box, 16 | } from "@chakra-ui/react"; 17 | import { Decorators } from "util/storybook-utils"; 18 | import { useThemedColor } from "util/helpers"; 19 | 20 | export default { 21 | title: "Components / Table", 22 | component: Table, 23 | } as ComponentMeta; 24 | 25 | const LeTable = (props: TableProps) => ( 26 | 27 | Imperial to metric conversion factors 28 | 29 | 30 | To convert 31 | into 32 | multiply by 33 | 34 | 35 | 36 | 37 | inches 38 | millimetres (mm) 39 | 25.4 40 | 41 | 42 | feet 43 | centimetres (cm) 44 | 30.48 45 | 46 | 47 | yards 48 | metres (m) 49 | 0.91444 50 | 51 | 52 | miles 53 | kilometres (km) 54 | 1.61 55 | 56 | 57 | 58 | ); 59 | 60 | const NewSimple = (props: StackProps) => { 61 | const c = useThemedColor(); 62 | return ( 63 | 64 | {/* display the light version in light mode */} 65 | 66 | New Theme 67 | 68 | 69 | 70 | 71 | {/* display the dark version in light mode */} 72 | 73 | 74 | 75 | 76 | ); 77 | }; 78 | 79 | const OldSimple = (props: StackProps) => { 80 | return ( 81 | 82 | {/* display the light version in light mode */} 83 | 84 | New Theme 85 | 86 | 87 | 88 | 89 | {/* display the dark version in light mode */} 90 | 91 | 92 | 93 | 94 | ); 95 | }; 96 | 97 | export const Simple: ComponentStory = (args) => ( 98 | } 100 | oldComponent={} 101 | /> 102 | ); 103 | 104 | const NewStriped = (props: StackProps) => { 105 | const c = useThemedColor(); 106 | return ( 107 | 108 | {/* display the light version in light mode */} 109 | 110 | New Theme 111 | 112 | 113 | 118 | 119 | {/* display the dark version in light mode */} 120 | 121 | 126 | 127 | 128 | ); 129 | }; 130 | 131 | const OldStriped = (props: StackProps) => { 132 | return ( 133 | 134 | {/* display the light version in light mode */} 135 | 136 | New Theme 137 | 138 | 139 | 144 | 145 | {/* display the dark version in light mode */} 146 | 147 | 152 | 153 | 154 | ); 155 | }; 156 | 157 | export const Striped: ComponentStory = (args) => ( 158 | } 160 | oldComponent={} 161 | /> 162 | ); 163 | -------------------------------------------------------------------------------- /src/theme/components/button/button.ts: -------------------------------------------------------------------------------- 1 | import { mode } from "@chakra-ui/theme-tools"; 2 | import type { 3 | SystemStyleObject, 4 | SystemStyleFunction, 5 | } from "@chakra-ui/theme-tools"; 6 | import { getColorInfo } from "../../../util/helpers"; 7 | 8 | const baseStyle: SystemStyleObject = { 9 | lineHeight: "1.2", 10 | borderRadius: "md", 11 | fontWeight: "semibold", 12 | transitionProperty: "common", 13 | transitionDuration: "normal", 14 | _focus: { 15 | boxShadow: "outline", 16 | }, 17 | _disabled: { 18 | opacity: 0.4, 19 | cursor: "not-allowed", 20 | boxShadow: "none", 21 | }, 22 | _hover: { 23 | _disabled: { 24 | bg: "initial", 25 | }, 26 | }, 27 | }; 28 | 29 | const variantGhost: SystemStyleFunction = (props) => { 30 | const { colorScheme: c, theme } = props; 31 | let { light, dark } = getColorInfo(c, theme); 32 | let { light: _gray, dark: _grayDark } = getColorInfo(c, theme); 33 | 34 | // set the color 35 | let [lightColor, darkColor] = [`${light}.11`, `${dark}.11`]; 36 | if (c.startsWith("_gray")) 37 | [lightColor, darkColor] = [`${_gray}.12`, `${_grayDark}.12`]; 38 | 39 | return { 40 | color: mode(lightColor, darkColor)(props), 41 | _hover: { 42 | bg: mode(`${light}.4`, `${dark}.4`)(props), 43 | }, 44 | _active: { 45 | bg: mode(`${light}.5`, `${dark}.5`)(props), 46 | }, 47 | }; 48 | }; 49 | 50 | const variantOutline: SystemStyleFunction = (props) => { 51 | const { colorScheme: c, theme } = props; 52 | let { isDark } = getColorInfo(c, theme); 53 | 54 | let [lightColor, darkColor] = ["_gray.7", "_grayDark.7"]; 55 | if (isDark) [lightColor, darkColor] = [darkColor, lightColor]; 56 | const borderColor = mode(lightColor, darkColor)(props); 57 | 58 | return { 59 | border: "1px solid", 60 | borderColor: c.startsWith("_gray") ? borderColor : "currentColor", 61 | ...variantGhost(props), 62 | }; 63 | }; 64 | 65 | const variantSolid: SystemStyleFunction = (props) => { 66 | const { colorScheme: c, theme } = props; 67 | let { light, dark, isBright } = getColorInfo(c, theme); 68 | 69 | if (c.startsWith("_gray")) { 70 | const bg = mode(`${light}.4`, `${dark}.4`)(props); 71 | return { 72 | bg, 73 | color: mode(`${light}.12`, `${dark}.12`)(props), 74 | _hover: { 75 | bg: mode(`${light}.5`, `${dark}.5`)(props), 76 | _disabled: { 77 | bg, 78 | }, 79 | }, 80 | _active: { bg: mode(`${light}.6`, `${dark}.6`)(props) }, 81 | }; 82 | } 83 | 84 | let bg = `${light}.9`; 85 | let color = isBright ? "_grayDark.1" : "_gray.1"; 86 | let hoverBg = mode(`${light}.10`, `${dark}.10`)(props); 87 | let activeBg = mode(`${light}.10`, `${dark}.10`)(props); 88 | let background = mode(bg, `${dark}.9`)(props); 89 | 90 | return { 91 | bg: background, 92 | color, 93 | _hover: { 94 | bg: hoverBg, 95 | _disabled: { 96 | background, 97 | }, 98 | }, 99 | _active: { bg: activeBg }, 100 | }; 101 | }; 102 | 103 | const variantLink: SystemStyleFunction = (props) => { 104 | const { colorScheme: c, theme } = props; 105 | let { light, dark } = getColorInfo(c, theme); 106 | return { 107 | padding: 0, 108 | height: "auto", 109 | lineHeight: "normal", 110 | verticalAlign: "baseline", 111 | color: mode(`${light}.9`, `${dark}.9`)(props), 112 | _hover: { 113 | textDecoration: "underline", 114 | _disabled: { 115 | textDecoration: "none", 116 | }, 117 | }, 118 | _active: { 119 | color: mode(`${light}.10`, `${dark}.10`)(props), 120 | }, 121 | }; 122 | }; 123 | 124 | const variantUnstyled: SystemStyleObject = { 125 | bg: "none", 126 | color: "inherit", 127 | display: "inline", 128 | lineHeight: "inherit", 129 | m: 0, 130 | p: 0, 131 | }; 132 | 133 | const variants = { 134 | ghost: variantGhost, 135 | outline: variantOutline, 136 | solid: variantSolid, 137 | link: variantLink, 138 | unstyled: variantUnstyled, 139 | }; 140 | 141 | const sizes: Record = { 142 | lg: { 143 | h: 12, 144 | minW: 12, 145 | fontSize: "lg", 146 | px: 6, 147 | }, 148 | md: { 149 | h: 10, 150 | minW: 10, 151 | fontSize: "md", 152 | px: 4, 153 | }, 154 | sm: { 155 | h: 8, 156 | minW: 8, 157 | fontSize: "sm", 158 | px: 3, 159 | }, 160 | xs: { 161 | h: 6, 162 | minW: 6, 163 | fontSize: "xs", 164 | px: 2, 165 | }, 166 | }; 167 | 168 | const defaultProps = { 169 | variant: "solid", 170 | size: "md", 171 | colorScheme: "_gray", 172 | }; 173 | 174 | export default { 175 | baseStyle, 176 | variants, 177 | sizes, 178 | defaultProps, 179 | }; 180 | -------------------------------------------------------------------------------- /src/theme/components/switch/switch.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComponentStory, ComponentMeta } from "@storybook/react"; 3 | import { 4 | Switch, 5 | HStack, 6 | StackProps, 7 | VStack, 8 | Text, 9 | Box, 10 | } from "@chakra-ui/react"; 11 | import { Decorators } from "util/storybook-utils"; 12 | import { useThemedColor } from "util/helpers"; 13 | 14 | export default { 15 | title: "Components / Switch", 16 | component: Switch, 17 | } as ComponentMeta; 18 | 19 | const NewDefault = (props: StackProps) => { 20 | const c = useThemedColor(); 21 | return ( 22 | 23 | {/* display the light version in light mode */} 24 | 25 | New Theme 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | }; 46 | 47 | const OldDefault = (props: StackProps) => { 48 | return ( 49 | 50 | {/* display the light version in light mode */} 51 | 52 | Old Theme 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | ); 72 | }; 73 | 74 | export const Default: ComponentStory = (args) => ( 75 | } 77 | oldComponent={} 78 | /> 79 | ); 80 | 81 | const NewBrightColor = (props: StackProps) => { 82 | const c = useThemedColor(); 83 | return ( 84 | 85 | {/* display the light version in light mode */} 86 | 87 | New Theme 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | ); 107 | }; 108 | 109 | const OldBrightColor = (props: StackProps) => { 110 | return ( 111 | 112 | {/* display the light version in light mode */} 113 | 114 | Old Theme 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | ); 134 | }; 135 | 136 | export const BrightColor: ComponentStory = (args) => ( 137 | } 139 | oldComponent={} 140 | /> 141 | ); 142 | -------------------------------------------------------------------------------- /cypress/integration/components/code.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRgbFromThemeColor } from "util/cypress-utils"; 2 | 3 | describe("Default", () => { 4 | beforeEach(() => { 5 | cy.visit( 6 | "http://localhost:6006/iframe.html?id=components-code--default&args=&viewMode=story" 7 | ); 8 | cy.get("#reset-color-mode").realHover().click(); // Important! Need to .realHover() to remove the hover state from prior tests. 9 | }); 10 | 11 | it("displays light color in light mode correctly", () => { 12 | const getCodes = () => 13 | cy.get("[data-testid=NewDefault] [data-testid=light-code] .chakra-code"); 14 | 15 | // test the subtle 16 | const subtleGray = getCodes().eq(0); 17 | subtleGray 18 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.4")) 19 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")); 20 | 21 | // test the solid 22 | const solidBright = getCodes().eq(1); 23 | solidBright 24 | .should("have.css", "background-color", getRgbFromThemeColor("amber.9")) 25 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.1")); 26 | 27 | // test the outline 28 | const outlinePurple = getCodes().eq(2); 29 | outlinePurple 30 | .should( 31 | "have.css", 32 | "box-shadow", 33 | `${getRgbFromThemeColor("purple.9")} 0px 0px 0px 1px inset` 34 | ) 35 | .should("have.css", "color", getRgbFromThemeColor("purple.9")); 36 | }); 37 | 38 | it("displays light color in dark mode correctly", () => { 39 | const getCodes = () => 40 | cy.get("[data-testid=NewDefault] [data-testid=light-code] .chakra-code"); 41 | cy.get("#switch-color-mode").click(); 42 | 43 | // test the subtle 44 | const subtleGray = getCodes().eq(0); 45 | subtleGray 46 | .should( 47 | "have.css", 48 | "background-color", 49 | getRgbFromThemeColor("_grayDark.4") 50 | ) 51 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")); 52 | 53 | // test the solid 54 | const solidBright = getCodes().eq(1); 55 | solidBright 56 | .should( 57 | "have.css", 58 | "background-color", 59 | getRgbFromThemeColor("amberDark.9") 60 | ) 61 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.1")); 62 | 63 | // test the outline 64 | const outlinePurple = getCodes().eq(2); 65 | outlinePurple 66 | .should( 67 | "have.css", 68 | "box-shadow", 69 | `${getRgbFromThemeColor("purpleDark.9")} 0px 0px 0px 1px inset` 70 | ) 71 | .should("have.css", "color", getRgbFromThemeColor("purpleDark.9")); 72 | }); 73 | 74 | it("displays dark color in light mode correctly", () => { 75 | const getCodes = () => 76 | cy.get("[data-testid=NewDefault] [data-testid=dark-code] .chakra-code"); 77 | 78 | // test the subtle 79 | const subtleGray = getCodes().eq(0); 80 | subtleGray 81 | .should( 82 | "have.css", 83 | "background-color", 84 | getRgbFromThemeColor("_grayDark.4") 85 | ) 86 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.12")); 87 | 88 | // test the solid 89 | const solidBright = getCodes().eq(1); 90 | solidBright 91 | .should( 92 | "have.css", 93 | "background-color", 94 | getRgbFromThemeColor("amberDark.9") 95 | ) 96 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.1")); 97 | 98 | // test the outline 99 | const outlinePurple = getCodes().eq(2); 100 | outlinePurple 101 | .should( 102 | "have.css", 103 | "box-shadow", 104 | `${getRgbFromThemeColor("purpleDark.9")} 0px 0px 0px 1px inset` 105 | ) 106 | .should("have.css", "color", getRgbFromThemeColor("purpleDark.9")); 107 | }); 108 | 109 | it("displays dark color in dark mode correctly", () => { 110 | const getCodes = () => 111 | cy.get("[data-testid=NewDefault] [data-testid=dark-code] .chakra-code"); 112 | cy.get("#switch-color-mode").click(); 113 | 114 | // test the subtle 115 | const subtleGray = getCodes().eq(0); 116 | subtleGray 117 | .should("have.css", "background-color", getRgbFromThemeColor("_gray.4")) 118 | .should("have.css", "color", getRgbFromThemeColor("_gray.12")); 119 | 120 | // test the solid 121 | const solidBright = getCodes().eq(1); 122 | solidBright 123 | .should("have.css", "background-color", getRgbFromThemeColor("amber.9")) 124 | .should("have.css", "color", getRgbFromThemeColor("_grayDark.1")); 125 | 126 | // test the outline 127 | const outlinePurple = getCodes().eq(2); 128 | outlinePurple 129 | .should( 130 | "have.css", 131 | "box-shadow", 132 | `${getRgbFromThemeColor("purple.9")} 0px 0px 0px 1px inset` 133 | ) 134 | .should("have.css", "color", getRgbFromThemeColor("purple.9")); 135 | }); 136 | }); 137 | --------------------------------------------------------------------------------
Are you sure you want to have that milkshake?
import React from "react"
24 | import React from "react" 25 |
27 | import React from "react" 28 |
36 | import React from "react" 37 |
39 | import React from "react" 40 |
59 | import React from "react" 60 |
62 | import React from "react" 63 |
71 | import React from "react" 72 |
74 | import React from "react" 75 |