reshaped 3.6.0-canary.3 → 3.6.0-canary.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/dist/bundle.css +1 -1
  3. package/dist/bundle.d.ts +1 -0
  4. package/dist/bundle.js +5 -5
  5. package/dist/cjs/cli/theming/index.d.ts +3 -3
  6. package/dist/cjs/cli/theming/index.js +2 -2
  7. package/dist/cjs/cli/theming/tailwind.d.ts +2 -3
  8. package/dist/cjs/themes/_generator/definitions/slate.js +1 -1
  9. package/dist/cjs/themes/_generator/tokens/color/color.transforms.d.ts +2 -2
  10. package/dist/cjs/themes/_generator/tokens/color/color.transforms.js +29 -7
  11. package/dist/cjs/themes/_generator/tokens/color/color.types.d.ts +28 -3
  12. package/dist/cjs/themes/_generator/tokens/color/utilities/a11y.d.ts +20 -0
  13. package/dist/cjs/themes/_generator/tokens/color/utilities/a11y.js +64 -0
  14. package/dist/cjs/themes/_generator/tokens/color/utilities/convert.d.ts +11 -0
  15. package/dist/cjs/themes/_generator/tokens/color/utilities/convert.js +33 -0
  16. package/dist/cjs/themes/_generator/tokens/color/utilities/generateColors.d.ts +4 -0
  17. package/dist/cjs/themes/_generator/tokens/color/utilities/generateColors.js +152 -0
  18. package/dist/cjs/themes/_generator/tokens/color/utilities/generateMetaColors.d.ts +4 -0
  19. package/dist/cjs/themes/_generator/tokens/color/utilities/generateMetaColors.js +68 -0
  20. package/dist/cjs/themes/_generator/tokens/color/utilities/tests/a11y.test.js +38 -0
  21. package/dist/cjs/themes/_generator/tokens/color/utilities/tests/convert.test.d.ts +1 -0
  22. package/dist/cjs/themes/_generator/tokens/color/utilities/tests/convert.test.js +48 -0
  23. package/dist/cjs/themes/_generator/tokens/color/utilities/tests/generateColors.test.d.ts +1 -0
  24. package/dist/cjs/themes/_generator/tokens/color/utilities/tests/generateColors.test.js +21 -0
  25. package/dist/{themes/_generator/utilities → cjs/themes/_generator/tokens}/css.d.ts +1 -1
  26. package/dist/cjs/themes/_generator/tokens/duration/duration.transforms.d.ts +2 -2
  27. package/dist/cjs/themes/_generator/tokens/duration/duration.transforms.js +2 -2
  28. package/dist/cjs/themes/_generator/tokens/easing/easing.transforms.d.ts +2 -2
  29. package/dist/cjs/themes/_generator/tokens/easing/easing.transforms.js +2 -2
  30. package/dist/cjs/themes/_generator/tokens/font/font.transforms.d.ts +2 -2
  31. package/dist/cjs/themes/_generator/tokens/font/font.transforms.js +3 -3
  32. package/dist/cjs/themes/_generator/tokens/fontFamily/fontFamily.transforms.d.ts +2 -2
  33. package/dist/cjs/themes/_generator/tokens/fontFamily/fontFamily.transforms.js +2 -2
  34. package/dist/cjs/themes/_generator/tokens/fontWeight/fontWeight.transforms.d.ts +2 -2
  35. package/dist/cjs/themes/_generator/tokens/fontWeight/fontWeight.transforms.js +2 -2
  36. package/dist/cjs/themes/_generator/tokens/radius/radius.transforms.d.ts +2 -2
  37. package/dist/cjs/themes/_generator/tokens/radius/radius.transforms.js +2 -2
  38. package/dist/cjs/themes/_generator/tokens/shadow/shadow.transforms.d.ts +2 -2
  39. package/dist/cjs/themes/_generator/tokens/shadow/shadow.transforms.js +4 -4
  40. package/dist/cjs/themes/_generator/tokens/types.d.ts +19 -17
  41. package/dist/cjs/themes/_generator/tokens/unit/unit.transforms.d.ts +2 -2
  42. package/dist/cjs/themes/_generator/tokens/unit/unit.transforms.js +2 -2
  43. package/dist/cjs/themes/_generator/tokens/unit/utilities/generate.d.ts +5 -0
  44. package/dist/cjs/themes/_generator/{utilities/generateUnits.js → tokens/unit/utilities/generate.js} +5 -4
  45. package/dist/cjs/themes/_generator/tokens/viewport/viewport.transforms.d.ts +2 -2
  46. package/dist/cjs/themes/_generator/tokens/viewport/viewport.transforms.js +2 -2
  47. package/dist/cjs/themes/_generator/transform.d.ts +3 -2
  48. package/dist/cjs/themes/_generator/transform.js +39 -11
  49. package/dist/cjs/themes/_generator/types.d.ts +4 -8
  50. package/dist/cjs/themes/_generator/utilities/mergeDefinitions.d.ts +1 -3
  51. package/dist/cjs/themes/_generator/utilities/mergeDefinitions.js +6 -4
  52. package/dist/cjs/themes/figma/theme.css +1 -1
  53. package/dist/cjs/themes/fragments/twitter/theme.css +1 -1
  54. package/dist/cjs/themes/index.d.ts +4 -11
  55. package/dist/cjs/themes/index.js +2 -5
  56. package/dist/cjs/themes/reshaped/theme.css +1 -1
  57. package/dist/cjs/themes/slate/theme.css +1 -1
  58. package/dist/cjs/types/config.d.ts +11 -7
  59. package/dist/cli/theming/index.d.ts +3 -3
  60. package/dist/cli/theming/index.js +2 -2
  61. package/dist/cli/theming/tailwind.d.ts +2 -3
  62. package/dist/components/Avatar/Avatar.module.css +1 -1
  63. package/dist/components/FormControl/FormControl.context.d.ts +1 -1
  64. package/dist/components/Reshaped/Reshaped.js +2 -2
  65. package/dist/components/Reshaped/Reshaped.types.d.ts +1 -0
  66. package/dist/components/Reshaped/tests/Reshaped.stories.d.ts +1 -0
  67. package/dist/components/Reshaped/tests/Reshaped.stories.js +20 -0
  68. package/dist/components/Theme/GlobalColorMode.js +6 -11
  69. package/dist/components/Theme/Theme.types.d.ts +1 -0
  70. package/dist/components/Theme/index.d.ts +1 -1
  71. package/dist/index.d.ts +1 -0
  72. package/dist/tests/ThemesPlayground.d.ts +2 -0
  73. package/dist/tests/ThemesPlayground.js +90 -0
  74. package/dist/tests/themes.stories.d.ts +16 -0
  75. package/dist/{themes/_generator/tests → tests}/themes.stories.js +74 -15
  76. package/dist/themes/_generator/definitions/slate.js +1 -1
  77. package/dist/themes/_generator/tokens/color/color.transforms.d.ts +2 -2
  78. package/dist/themes/_generator/tokens/color/color.transforms.js +29 -7
  79. package/dist/themes/_generator/tokens/color/color.types.d.ts +28 -3
  80. package/dist/themes/_generator/tokens/color/utilities/a11y.d.ts +20 -0
  81. package/dist/themes/_generator/tokens/color/utilities/a11y.js +58 -0
  82. package/dist/themes/_generator/tokens/color/utilities/convert.d.ts +11 -0
  83. package/dist/themes/_generator/tokens/color/utilities/convert.js +27 -0
  84. package/dist/themes/_generator/tokens/color/utilities/generateColors.d.ts +4 -0
  85. package/dist/themes/_generator/tokens/color/utilities/generateColors.js +150 -0
  86. package/dist/themes/_generator/tokens/color/utilities/generateMetaColors.d.ts +4 -0
  87. package/dist/themes/_generator/tokens/color/utilities/generateMetaColors.js +66 -0
  88. package/dist/{cjs/themes/_generator/utilities → themes/_generator/tokens}/css.d.ts +1 -1
  89. package/dist/themes/_generator/tokens/duration/duration.transforms.d.ts +2 -2
  90. package/dist/themes/_generator/tokens/duration/duration.transforms.js +2 -2
  91. package/dist/themes/_generator/tokens/easing/easing.transforms.d.ts +2 -2
  92. package/dist/themes/_generator/tokens/easing/easing.transforms.js +2 -2
  93. package/dist/themes/_generator/tokens/font/font.transforms.d.ts +2 -2
  94. package/dist/themes/_generator/tokens/font/font.transforms.js +3 -3
  95. package/dist/themes/_generator/tokens/fontFamily/fontFamily.transforms.d.ts +2 -2
  96. package/dist/themes/_generator/tokens/fontFamily/fontFamily.transforms.js +2 -2
  97. package/dist/themes/_generator/tokens/fontWeight/fontWeight.transforms.d.ts +2 -2
  98. package/dist/themes/_generator/tokens/fontWeight/fontWeight.transforms.js +2 -2
  99. package/dist/themes/_generator/tokens/radius/radius.transforms.d.ts +2 -2
  100. package/dist/themes/_generator/tokens/radius/radius.transforms.js +2 -2
  101. package/dist/themes/_generator/tokens/shadow/shadow.transforms.d.ts +2 -2
  102. package/dist/themes/_generator/tokens/shadow/shadow.transforms.js +4 -4
  103. package/dist/themes/_generator/tokens/types.d.ts +19 -17
  104. package/dist/themes/_generator/tokens/unit/unit.transforms.d.ts +2 -2
  105. package/dist/themes/_generator/tokens/unit/unit.transforms.js +2 -2
  106. package/dist/themes/_generator/tokens/unit/utilities/generate.d.ts +5 -0
  107. package/dist/themes/_generator/{utilities/generateUnits.js → tokens/unit/utilities/generate.js} +4 -5
  108. package/dist/themes/_generator/tokens/viewport/viewport.transforms.d.ts +2 -2
  109. package/dist/themes/_generator/tokens/viewport/viewport.transforms.js +2 -2
  110. package/dist/themes/_generator/transform.d.ts +3 -2
  111. package/dist/themes/_generator/transform.js +39 -11
  112. package/dist/themes/_generator/types.d.ts +4 -8
  113. package/dist/themes/_generator/utilities/mergeDefinitions.d.ts +1 -3
  114. package/dist/themes/_generator/utilities/mergeDefinitions.js +6 -4
  115. package/dist/themes/figma/theme.css +1 -1
  116. package/dist/themes/fragments/twitter/theme.css +1 -1
  117. package/dist/themes/index.d.ts +4 -11
  118. package/dist/themes/index.js +1 -4
  119. package/dist/themes/reshaped/theme.css +1 -1
  120. package/dist/themes/slate/theme.css +1 -1
  121. package/dist/types/config.d.ts +11 -7
  122. package/dist/types/global.d.ts +3 -0
  123. package/package.json +11 -11
  124. package/dist/cjs/themes/_generator/utilities/color.d.ts +0 -105
  125. package/dist/cjs/themes/_generator/utilities/color.js +0 -409
  126. package/dist/cjs/themes/_generator/utilities/generateBackgroundColors.d.ts +0 -4
  127. package/dist/cjs/themes/_generator/utilities/generateBackgroundColors.js +0 -59
  128. package/dist/cjs/themes/_generator/utilities/generateColors.d.ts +0 -11
  129. package/dist/cjs/themes/_generator/utilities/generateColors.js +0 -178
  130. package/dist/cjs/themes/_generator/utilities/generateUnits.d.ts +0 -4
  131. package/dist/cjs/themes/_generator/utilities/mergeDeep.d.ts +0 -5
  132. package/dist/cjs/themes/_generator/utilities/mergeDeep.js +0 -24
  133. package/dist/cjs/themes/_generator/utilities/resolveTokenReference.d.ts +0 -3
  134. package/dist/cjs/themes/_generator/utilities/resolveTokenReference.js +0 -18
  135. package/dist/cjs/themes/_generator/utilities/tests/color.test.js +0 -81
  136. package/dist/themes/_generator/tests/themes.stories.d.ts +0 -16
  137. package/dist/themes/_generator/utilities/color.d.ts +0 -105
  138. package/dist/themes/_generator/utilities/color.js +0 -377
  139. package/dist/themes/_generator/utilities/generateBackgroundColors.d.ts +0 -4
  140. package/dist/themes/_generator/utilities/generateBackgroundColors.js +0 -57
  141. package/dist/themes/_generator/utilities/generateColors.d.ts +0 -11
  142. package/dist/themes/_generator/utilities/generateColors.js +0 -176
  143. package/dist/themes/_generator/utilities/generateUnits.d.ts +0 -4
  144. package/dist/themes/_generator/utilities/mergeDeep.d.ts +0 -5
  145. package/dist/themes/_generator/utilities/mergeDeep.js +0 -22
  146. package/dist/themes/_generator/utilities/resolveTokenReference.d.ts +0 -3
  147. package/dist/themes/_generator/utilities/resolveTokenReference.js +0 -16
  148. /package/dist/cjs/themes/_generator/{utilities/tests/color.test.d.ts → tokens/color/utilities/tests/a11y.test.d.ts} +0 -0
  149. /package/dist/cjs/themes/_generator/{utilities → tokens}/css.js +0 -0
  150. /package/dist/themes/_generator/{utilities → tokens}/css.js +0 -0
@@ -1,14 +1,18 @@
1
- import type { PartialThemeDefinition, ColorHue } from "../themes/_generator/tokens/types";
1
+ import type { PassedThemeDefinition } from "../themes/_generator/tokens/types";
2
+ import type { HexColor, Hue, OklchColor } from "../themes/_generator/tokens/color/color.types";
2
3
  export type ReshapedConfig = {
3
- themes?: Record<string, PartialThemeDefinition>;
4
- themeFragments?: Record<string, PartialThemeDefinition>;
4
+ themes?: Record<string, PassedThemeDefinition>;
5
+ themeFragments?: Record<string, PassedThemeDefinition>;
5
6
  themeOptions?: {
6
- generateOnColorsFor?: string[];
7
7
  colorContrastAlgorithm?: "wcag" | "apca";
8
+ generateOnColorsFor?: string[];
8
9
  onColorValues?: {
9
- [key in ColorHue]?: {
10
- hexDark: string;
11
- hexLight: string;
10
+ [key in Hue]?: {
11
+ hexDark: HexColor;
12
+ hexLight: HexColor;
13
+ } | {
14
+ oklchDark: OklchColor;
15
+ oklchLight: OklchColor;
12
16
  };
13
17
  };
14
18
  };
@@ -1,4 +1,4 @@
1
- import type { PartialThemeDefinition } from "../../themes/_generator/tokens/types";
1
+ import type { PassedThemeDefinition } from "../../themes/_generator/tokens/types";
2
2
  import type * as T from "../../themes/_generator/types";
3
- export declare const addThemeFragment: (name: string, definition: PartialThemeDefinition, options: T.CLIOptions) => void;
4
- export declare const addTheme: (name: string, definition: PartialThemeDefinition, options: T.CLIOptions) => void;
3
+ export declare const addThemeFragment: (name: string, definition: PassedThemeDefinition, options: T.CLIOptions) => void;
4
+ export declare const addTheme: (name: string, definition: PassedThemeDefinition, options: T.CLIOptions) => void;
@@ -17,8 +17,8 @@ const transformDefinition = (name, definition, options) => {
17
17
  const themeJsonPath = path.resolve(themeFolderPath, "theme.json");
18
18
  fs.mkdirSync(themeFolderPath, { recursive: true });
19
19
  fs.writeFileSync(themePath, code.variables);
20
- fs.writeFileSync(themeJsonPath, JSON.stringify(definition));
21
- fs.writeFileSync(twPath, transformToTailwind(definition));
20
+ fs.writeFileSync(themeJsonPath, JSON.stringify(code.theme));
21
+ fs.writeFileSync(twPath, transformToTailwind(code.theme));
22
22
  if (code.media)
23
23
  fs.writeFileSync(themeMediaPath, code.media);
24
24
  const logOutput = `Compiled ${chalk.bold(name)} theme${isFragment ? " fragment" : ""}`;
@@ -1,3 +1,2 @@
1
- import type { FullThemeDefinition } from "../../themes/_generator/tokens/types";
2
- import type * as T from "../../themes/_generator/types";
3
- export declare const transformToTailwind: (theme?: T.PartialDeep<FullThemeDefinition>) => string;
1
+ import type { GeneratedThemeDefinition } from "../../themes/_generator/tokens/types";
2
+ export declare const transformToTailwind: (theme?: GeneratedThemeDefinition) => string;
@@ -1 +1 @@
1
- .root{align-items:center;aspect-ratio:1;display:flex;font-size:calc(var(--rs-h) / 3);font-weight:700;justify-content:center;line-height:100%}.img{border-radius:inherit;height:100%;object-fit:cover;width:100%}.--variant-faded.--color-neutral{color:var(--rs-color-foreground-neutral)}.--variant-faded.--color-critical{color:var(--rs-color-foreground-critical)}.--variant-faded.--color-positive{color:var(--rs-color-foreground-positive)}.--variant-faded.--color-warning{color:var(--rs-color-foreground-warning)}.--variant-faded.--color-primary{color:var(--rs-color-foreground-primary)}
1
+ .root{align-items:center;aspect-ratio:1;display:flex;font-size:calc(var(--rs-h) / 3);font-weight:var(--rs-font-weight-bold);justify-content:center;line-height:100%}.img{border-radius:inherit;height:100%;object-fit:cover;width:100%}.--variant-faded.--color-neutral{color:var(--rs-color-foreground-neutral)}.--variant-faded.--color-critical{color:var(--rs-color-foreground-critical)}.--variant-faded.--color-positive{color:var(--rs-color-foreground-positive)}.--variant-faded.--color-warning{color:var(--rs-color-foreground-warning)}.--variant-faded.--color-primary{color:var(--rs-color-foreground-primary)}
@@ -134,7 +134,7 @@ export declare const useFormControl: () => {
134
134
  onBlurCapture?: React.FocusEventHandler<HTMLElement> | undefined;
135
135
  onChange?: React.FormEventHandler<HTMLElement> | undefined;
136
136
  onChangeCapture?: React.FormEventHandler<HTMLElement> | undefined;
137
- onBeforeInput?: React.FormEventHandler<HTMLElement> | undefined;
137
+ onBeforeInput?: React.InputEventHandler<HTMLElement> | undefined;
138
138
  onBeforeInputCapture?: React.FormEventHandler<HTMLElement> | undefined;
139
139
  onInput?: React.FormEventHandler<HTMLElement> | undefined;
140
140
  onInputCapture?: React.FormEventHandler<HTMLElement> | undefined;
@@ -16,11 +16,11 @@ const ReshapedInner = (props) => {
16
16
  return (_jsx(SingletonKeyboardModeProvider, { children: _jsx(SingletonEnvironmentContext.Provider, { value: { rtl: rtlState, defaultViewport }, children: _jsx(SingletonHotkeysProvider, { children: _jsx(ToastProvider, { options: toastOptions, children: children }) }) }) }));
17
17
  };
18
18
  const Reshaped = (props) => {
19
- const { theme, defaultTheme = "reshaped", defaultColorMode, scoped, className } = props;
19
+ const { theme, defaultTheme = "reshaped", colorMode, defaultColorMode, scoped, className, } = props;
20
20
  const rootClassNames = classNames(s.root, className);
21
21
  const scopeRef = React.useRef(null);
22
22
  const parentGlobalColorMode = useGlobalColorMode();
23
- return (_jsx(GlobalColorMode, { defaultMode: defaultColorMode || parentGlobalColorMode.mode || "light", scopeRef: !!parentGlobalColorMode && scoped ? scopeRef : undefined, children: _jsx(PrivateTheme, { name: theme, defaultName: defaultTheme, className: rootClassNames, scoped: scoped, scopeRef: !!parentGlobalColorMode && scoped ? scopeRef : undefined, children: _jsx(ReshapedInner, { ...props, children: props.children }) }) }));
23
+ return (_jsx(GlobalColorMode, { defaultMode: defaultColorMode || parentGlobalColorMode.mode || "light", mode: colorMode, scopeRef: !!parentGlobalColorMode && scoped ? scopeRef : undefined, children: _jsx(PrivateTheme, { name: theme, defaultName: defaultTheme, className: rootClassNames, scoped: scoped, scopeRef: !!parentGlobalColorMode && scoped ? scopeRef : undefined, children: _jsx(ReshapedInner, { ...props, children: props.children }) }) }));
24
24
  };
25
25
  Reshaped.displayName = "Reshaped";
26
26
  export default Reshaped;
@@ -7,6 +7,7 @@ export type Props = {
7
7
  theme?: NonNullable<ThemeProps["name"]>;
8
8
  defaultTheme?: NonNullable<ThemeProps["defaultName"]>;
9
9
  defaultRTL?: boolean;
10
+ colorMode?: GlobalColorModeProps["mode"];
10
11
  defaultColorMode?: GlobalColorModeProps["defaultMode"];
11
12
  defaultViewport?: G.Viewport;
12
13
  toastOptions?: ToastProviderProps["options"];
@@ -10,6 +10,7 @@ export declare const rtl: {
10
10
  name: string;
11
11
  render: () => import("react").JSX.Element;
12
12
  };
13
+ export declare const controlledMode: StoryObj;
13
14
  export declare const lightMode: {
14
15
  name: string;
15
16
  render: () => import("react").JSX.Element;
@@ -1,3 +1,4 @@
1
+ import { useState } from "react";
1
2
  import { expect, userEvent } from "storybook/test";
2
3
  import { useTheme } from "../../Theme/index.js";
3
4
  import Button from "../../Button/index.js";
@@ -14,6 +15,25 @@ export const rtl = {
14
15
  Hello
15
16
  </Reshaped>),
16
17
  };
18
+ export const controlledMode = {
19
+ name: "colorMode, controlled",
20
+ render: () => {
21
+ const [mode, setMode] = useState("dark");
22
+ return (<Reshaped theme="reshaped" colorMode={mode}>
23
+ <Button onClick={() => {
24
+ setMode(mode === "dark" ? "light" : "dark");
25
+ }}>
26
+ Toggle color mode
27
+ </Button>
28
+ </Reshaped>);
29
+ },
30
+ play: async ({ canvas }) => {
31
+ const button = canvas.getAllByRole("button")[0];
32
+ expect(document.documentElement.getAttribute("data-rs-color-mode")).toEqual("dark");
33
+ await userEvent.click(button);
34
+ expect(document.documentElement.getAttribute("data-rs-color-mode")).toEqual("light");
35
+ },
36
+ };
17
37
  export const lightMode = {
18
38
  name: "defaultColorMode=light",
19
39
  render: () => <Reshaped theme="reshaped">Hello</Reshaped>,
@@ -7,7 +7,7 @@ import { useGlobalColorMode } from "./useTheme.js";
7
7
  import { GlobalColorModeContext } from "./Theme.context.js";
8
8
  import { getRootThemeEl } from "./Theme.utilities.js";
9
9
  const GlobalColorMode = (props) => {
10
- const { defaultMode, scopeRef, children } = props;
10
+ const { defaultMode, mode: passedMode, scopeRef, children } = props;
11
11
  const [mode, setMode] = React.useState(defaultMode);
12
12
  const parentGlobalColorMode = useGlobalColorMode();
13
13
  const changeColorMode = React.useCallback((targetMode) => {
@@ -15,19 +15,14 @@ const GlobalColorMode = (props) => {
15
15
  if (parentGlobalColorMode.mode && !scopeRef) {
16
16
  parentGlobalColorMode.setMode(targetMode);
17
17
  }
18
- setMode((prevMode) => {
19
- if (prevMode !== targetMode) {
20
- // Avoid components styles animating when switching to another color mode
21
- disableTransitions();
22
- }
23
- return targetMode;
24
- });
18
+ setMode(targetMode);
25
19
  }, [scopeRef, parentGlobalColorMode]);
26
20
  useIsomorphicLayoutEffect(() => {
21
+ disableTransitions();
27
22
  onNextFrame(() => {
28
23
  enableTransitions();
29
24
  });
30
- }, [mode]);
25
+ }, [mode, passedMode]);
31
26
  /**
32
27
  * In case color mode was set in html but was not provided to the provider - hydrate the state
33
28
  * This could happen if we're receiving the mode on the client but before React hydration
@@ -38,12 +33,12 @@ const GlobalColorMode = (props) => {
38
33
  changeColorMode(nextColorMode);
39
34
  }, [changeColorMode, scopeRef]);
40
35
  const value = React.useMemo(() => ({
41
- mode,
36
+ mode: passedMode || mode,
42
37
  setMode: changeColorMode,
43
38
  invertMode: () => {
44
39
  changeColorMode(mode === "light" ? "dark" : "light");
45
40
  },
46
- }), [mode, changeColorMode]);
41
+ }), [mode, passedMode, changeColorMode]);
47
42
  return (_jsx(GlobalColorModeContext.Provider, { value: value, children: children }));
48
43
  };
49
44
  GlobalColorMode.displayName = "GlobalColorMode";
@@ -25,6 +25,7 @@ export type PrivateProps = Props & {
25
25
  scopeRef?: React.RefObject<HTMLDivElement | null>;
26
26
  };
27
27
  export type GlobalColorModeProps = {
28
+ mode?: ColorMode;
28
29
  defaultMode: ColorMode;
29
30
  scopeRef?: React.RefObject<HTMLDivElement | null>;
30
31
  children?: React.ReactNode;
@@ -1,4 +1,4 @@
1
1
  export { default, PrivateTheme } from "./Theme";
2
2
  export { default as GlobalColorMode } from "./GlobalColorMode";
3
3
  export { useTheme } from "./useTheme";
4
- export type { Props as ThemeProps, GlobalColorModeProps } from "./Theme.types";
4
+ export type { Props as ThemeProps, GlobalColorModeProps, ColorMode } from "./Theme.types";
package/dist/index.d.ts CHANGED
@@ -127,6 +127,7 @@ export type { ViewProps, ViewItemProps } from "./components/View";
127
127
  */
128
128
  export { useFormControl } from "./components/FormControl";
129
129
  export { default as Theme, useTheme, type ThemeProps } from "./components/Theme";
130
+ export type { ColorMode } from "./components/Theme";
130
131
  export { default as useHandlerRef } from "./hooks/useHandlerRef";
131
132
  export { default as useHotkeys } from "./hooks/useHotkeys";
132
133
  export { default as useIsomorphicLayoutEffect } from "./hooks/useIsomorphicLayoutEffect";
@@ -0,0 +1,2 @@
1
+ declare const ThemePlayground: () => import("react/jsx-runtime").JSX.Element;
2
+ export default ThemePlayground;
@@ -0,0 +1,90 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import View from "../components/View/index.js";
3
+ import Text from "../components/Text/index.js";
4
+ import Divider from "../components/Divider/index.js";
5
+ import Card from "../components/Card/index.js";
6
+ import Button from "../components/Button/index.js";
7
+ import Grid from "../components/Grid/index.js";
8
+ import Avatar from "../components/Avatar/index.js";
9
+ import Image from "../components/Image/index.js";
10
+ import IconChevronRight from "../icons/ChevronRight.js";
11
+ import TextField from "../components/TextField/index.js";
12
+ import FormControl from "../components/FormControl/index.js";
13
+ import Link from "../components/Link/index.js";
14
+ import useToggle from "../hooks/useToggle.js";
15
+ import Switch from "../components/Switch/index.js";
16
+ import Badge from "../components/Badge/index.js";
17
+ import Alert from "../components/Alert/index.js";
18
+ import IconZap from "../icons/Zap.js";
19
+ import CheckboxGroup from "../components/CheckboxGroup/index.js";
20
+ import Checkbox from "../components/Checkbox/index.js";
21
+ import React from "react";
22
+ import Table from "../components/Table/index.js";
23
+ import DropdownMenu from "../components/DropdownMenu/index.js";
24
+ import IconChevronDown from "../icons/ChevronDown.js";
25
+ const Color = (props) => {
26
+ return (_jsx(View, { padding: 2, height: 25, gap: 2, direction: "row", borderRadius: "medium", backgroundColor: props.backgroundColor, borderColor: props.borderColor, attributes: {
27
+ style: {
28
+ borderWidth: 4,
29
+ },
30
+ }, children: props.children }));
31
+ };
32
+ const NeutralContrastTest = () => (_jsxs(View, { gap: 1, children: [_jsxs(View, { direction: "row", gap: 1, children: [_jsx(View, { height: 6, width: 6, backgroundColor: "neutral", borderRadius: "small" }), _jsx(View, { height: 6, width: 6, backgroundColor: "neutral-faded", borderRadius: "small" })] }), _jsxs(View, { direction: "row", gap: 1, children: [_jsx(View, { height: 6, width: 6, backgroundColor: "disabled", borderRadius: "small" }), _jsx(View, { height: 6, width: 6, backgroundColor: "disabled-faded", borderRadius: "small", borderColor: "disabled" })] })] }));
33
+ const Palette = () => {
34
+ return (_jsxs(View, { gap: 2, children: [_jsxs(View, { direction: "row", gap: 2, children: [_jsxs(View, { gap: 2, grow: true, children: [_jsx(Color, { backgroundColor: "neutral", borderColor: "neutral", children: _jsx(Text, { weight: "medium", children: "Aa" }) }), _jsx(Color, { backgroundColor: "neutral-faded", borderColor: "neutral-faded", children: _jsx(Text, { weight: "medium", children: "Aa" }) })] }), _jsxs(View, { gap: 2, grow: true, children: [_jsx(Color, { backgroundColor: "primary", borderColor: "primary", children: _jsx(Text, { weight: "medium", children: "Aa" }) }), _jsxs(Color, { backgroundColor: "primary-faded", borderColor: "primary-faded", children: [_jsx(Text, { weight: "medium", color: "primary", children: "Aa" }), _jsx(Text, { weight: "medium", children: "Aa" })] })] }), _jsxs(View, { gap: 2, grow: true, children: [_jsx(Color, { backgroundColor: "critical", borderColor: "critical", children: _jsx(Text, { weight: "medium", children: "Aa" }) }), _jsxs(Color, { backgroundColor: "critical-faded", borderColor: "critical-faded", children: [_jsx(Text, { weight: "medium", color: "critical", children: "Aa" }), _jsx(Text, { weight: "medium", children: "Aa" })] })] }), _jsxs(View, { gap: 2, grow: true, children: [_jsx(Color, { backgroundColor: "positive", borderColor: "positive", children: _jsx(Text, { weight: "medium", children: "Aa" }) }), _jsxs(Color, { backgroundColor: "positive-faded", borderColor: "positive-faded", children: [_jsx(Text, { weight: "medium", color: "positive", children: "Aa" }), _jsx(Text, { weight: "medium", children: "Aa" })] })] }), _jsxs(View, { gap: 2, grow: true, children: [_jsx(Color, { backgroundColor: "warning", borderColor: "warning", children: _jsx(Text, { weight: "medium", children: "Aa" }) }), _jsxs(Color, { backgroundColor: "warning-faded", borderColor: "warning-faded", children: [_jsx(Text, { weight: "medium", color: "warning", children: "Aa" }), _jsx(Text, { weight: "medium", children: "Aa" })] })] }), _jsxs(View, { gap: 2, grow: true, children: [_jsx(Color, { backgroundColor: "disabled", borderColor: "disabled", children: _jsx(Text, { weight: "medium", color: "disabled", children: "Aa" }) }), _jsx(Color, { backgroundColor: "disabled-faded", borderColor: "disabled", children: _jsx(Text, { weight: "medium", color: "disabled", children: "Aa" }) })] })] }), _jsx(Divider, {}), _jsxs(View, { gap: 2, direction: "row", children: [_jsx(View.Item, { grow: true, children: _jsx(Color, { backgroundColor: "elevation-base", borderColor: "neutral-faded", children: _jsxs(View, { gap: 2, children: [_jsx(Text, { weight: "medium", variant: "caption-1", children: "Base" }), _jsx(NeutralContrastTest, {})] }) }) }), _jsx(View.Item, { grow: true, children: _jsx(Color, { backgroundColor: "elevation-raised", borderColor: "neutral-faded", children: _jsxs(View, { gap: 2, children: [_jsx(Text, { weight: "medium", variant: "caption-1", children: "Raised" }), _jsx(NeutralContrastTest, {})] }) }) }), _jsx(View.Item, { grow: true, children: _jsx(Color, { backgroundColor: "elevation-overlay", borderColor: "neutral-faded", children: _jsxs(View, { gap: 2, children: [_jsx(Text, { weight: "medium", variant: "caption-1", children: "Overlay" }), _jsx(NeutralContrastTest, {})] }) }) }), _jsx(View.Item, { grow: true, children: _jsx(Color, { backgroundColor: "page", borderColor: "neutral-faded", children: _jsxs(View, { gap: 2, children: [_jsx(Text, { weight: "medium", variant: "caption-1", children: "Page" }), _jsx(NeutralContrastTest, {})] }) }) }), _jsx(View.Item, { grow: true, children: _jsx(Color, { backgroundColor: "page-faded", borderColor: "neutral-faded", children: _jsxs(View, { gap: 2, children: [_jsx(Text, { weight: "medium", variant: "caption-1", children: "Page faded" }), _jsx(NeutralContrastTest, {})] }) }) }), _jsx(View.Item, { grow: true, children: _jsx(Color, { backgroundColor: "neutral-faded", borderColor: "neutral-faded", children: _jsxs(View, { gap: 2, children: [_jsx(Text, { weight: "medium", variant: "caption-1", children: "Neutral faded" }), _jsx(NeutralContrastTest, {})] }) }) })] })] }));
35
+ };
36
+ const ExampleAnalytics = () => (_jsx(Card, { children: _jsxs(View, { gap: 1, children: [_jsxs(View, { gap: 4, direction: "row", align: "center", children: [_jsx(View, { gap: 1, direction: "row", align: "center", grow: true, children: _jsx(Text, { variant: "body-2", weight: "bold", children: "Audience" }) }), _jsx(Button.Aligner, { children: _jsx(Button, { variant: "ghost", color: "primary", size: "small", endIcon: IconChevronRight, children: "View" }) })] }), _jsx(Text, { variant: "featured-2", color: "positive", children: "+76.28%" }), _jsxs(View, { gap: 2, children: [_jsxs(View, { gap: 3, direction: "row", align: "baseline", children: [_jsx(View.Item, { grow: true, children: _jsx(Text, { color: "neutral-faded", children: "Signed up" }) }), _jsx(Text, { color: "neutral-faded", children: "320 users" }), _jsx(Text, { color: "primary", weight: "medium", children: "81%" })] }), _jsxs(View, { gap: 3, direction: "row", align: "baseline", children: [_jsx(View.Item, { grow: true, children: _jsx(Text, { color: "neutral-faded", children: "Onboarded" }) }), _jsx(Text, { color: "neutral-faded", children: "182 users" }), _jsx(Text, { color: "warning", weight: "medium", children: "62%" })] }), _jsxs(View, { gap: 3, direction: "row", align: "baseline", children: [_jsx(View.Item, { grow: true, children: _jsx(Text, { color: "neutral-faded", children: "Purchased" }) }), _jsx(Text, { color: "neutral-faded", children: "120 users" }), _jsx(Text, { color: "critical", weight: "medium", children: "47%" })] })] })] }) }));
37
+ const ExampleSocial = () => {
38
+ return (_jsx(Card, { height: "100%", children: _jsxs(View, { gap: 3, children: [_jsxs(View, { direction: "row", gap: 2, align: "center", children: [_jsx(Avatar, { size: 10, color: "neutral" }), _jsxs(View.Item, { grow: true, children: [_jsx(Text, { weight: "medium", children: "Esther Naomi" }), _jsx(Text, { color: "neutral-faded", variant: "caption-1", children: "31 Jan 2037, 10:28" })] }), _jsx(Badge, { variant: "faded", color: "warning", children: "Archived" })] }), _jsx(Text, { color: "neutral-faded", children: "I have been to 27 countries and only 5 of them call this social network X, while others named it Twitter for some reason. Why is this happening?!" }), _jsxs(View, { direction: "row", gap: 2, children: [_jsx(View, { grow: true, aspectRatio: 1, children: _jsx(Image, { src: "", borderRadius: "medium", fallback: true }) }), _jsx(View, { grow: true, aspectRatio: 1, children: _jsx(Image, { src: "", borderRadius: "medium", fallback: true }) }), _jsx(View, { grow: true, aspectRatio: 1, children: _jsx(Image, { src: "", borderRadius: "medium", fallback: true }) })] })] }) }));
39
+ };
40
+ const ExampleLogin = () => {
41
+ const passwordVisibility = useToggle();
42
+ return (_jsx(Card, { height: "100%", children: _jsxs(View, { gap: 3, children: [_jsx(Text, { variant: "featured-3", children: "Sign in to your account" }), _jsx(Button, { variant: "outline", fullWidth: true, children: "Sign in with Figma" }), _jsxs(View, { gap: 3, direction: "row", align: "center", children: [_jsx(View.Item, { grow: true, children: _jsx(Divider, {}) }), _jsx(Text, { children: "or" }), _jsx(View.Item, { grow: true, children: _jsx(Divider, {}) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Email" }), _jsx(TextField, { name: "email", placeholder: "hello@reshaped.so" })] }), _jsxs(FormControl, { children: [_jsxs(View, { direction: "row", align: "baseline", children: [_jsx(View.Item, { grow: true, children: _jsx(FormControl.Label, { children: "Password" }) }), _jsx(Text, { variant: "caption-1", weight: "medium", children: _jsx(Link, { variant: "plain", onClick: () => { }, children: "Forgot password?" }) })] }), _jsx(TextField, { name: "password", inputAttributes: { type: passwordVisibility.active ? "text" : "password" } })] }), _jsx(Button, { color: "primary", children: "Sign in" })] }) }));
43
+ };
44
+ const ExampleSettings = () => (_jsx(Card, { height: "100%", children: _jsxs(View, { gap: 6, height: "100%", children: [_jsx(Text, { variant: "body-1", weight: "medium", children: "Notification settings" }), _jsxs(View, { gap: 3, grow: true, children: [_jsxs(View, { as: "label", direction: "row", align: "center", gap: 2, children: [_jsx(View.Item, { grow: true, children: _jsx(Text, { weight: "medium", children: "Enable email notifications" }) }), _jsx(Switch, { name: "email" })] }), _jsxs(View, { as: "label", direction: "row", align: "center", gap: 2, children: [_jsx(View.Item, { grow: true, children: _jsx(Text, { weight: "medium", children: "Enable Slack notifications" }) }), _jsx(Switch, { name: "email" })] }), _jsxs(View, { as: "label", direction: "row", align: "center", gap: 2, children: [_jsx(View.Item, { grow: true, children: _jsx(Text, { weight: "medium", children: "Enable paper mail notifications" }) }), _jsx(Switch, { name: "email", defaultChecked: true })] }), _jsx(Text, { variant: "caption-1", color: "neutral-faded", children: "Enable all notifications to unsubscribe from our marketing and promotional materials." }), _jsx(View, { grow: true, justify: "end", children: _jsx(Button, { color: "primary", variant: "faded", children: "Save settings" }) })] })] }) }));
45
+ const ExampleAlert = () => (_jsx(Alert, { icon: IconZap, color: "primary", title: _jsxs(View, { direction: "row", gap: 2, align: "center", children: ["Auto-approve code reviews with AI", _jsx(Badge, { color: "primary", size: "small", children: "New" })] }), actionsSlot: [
46
+ _jsx(Button, { children: "Keep using" }, 1),
47
+ _jsx(Button, { variant: "ghost", color: "critical", children: "Opt-out" }, 2),
48
+ ], children: "Try our new GPT-powered code assistant features and never worry about reviewing other developer's pull requests again. Free for the first 5 pull requests." }));
49
+ const ExampleCheckboxCard = () => {
50
+ const [value, setValue] = React.useState(["hobby"]);
51
+ return (_jsx(CheckboxGroup, { name: "1", value: value, onChange: (args) => {
52
+ setValue(args.value);
53
+ }, children: _jsxs(View, { gap: 3, children: [_jsx(Card, { as: "label", selected: value.includes("pro"), children: _jsx(View, { direction: "row", gap: 2, align: "center", children: _jsx(Checkbox, { value: "pro", children: "Figma library" }) }) }), _jsx(Card, { as: "label", selected: value.includes("hobby"), children: _jsxs(View, { direction: "row", gap: 2, align: "center", children: [_jsx(View.Item, { grow: true, children: _jsx(Checkbox, { value: "hobby", children: "React library" }) }), _jsx(Badge, { color: "positive", children: "Free" })] }) })] }) }));
54
+ };
55
+ const ExampleTable = () => {
56
+ const rows = [
57
+ {
58
+ name: "File downloaded",
59
+ id: "rshpd_23jb237dh",
60
+ time: "Jul 4, 12:36",
61
+ },
62
+ {
63
+ name: "User registered",
64
+ id: "rshpd_27d223jfh",
65
+ time: "Jul 4, 12:36",
66
+ },
67
+ {
68
+ name: "New version released",
69
+ id: "rshpd_sjdniu223nj",
70
+ time: "Jul 4, 12:35",
71
+ },
72
+ ];
73
+ const [value, setValue] = React.useState([rows[1].id]);
74
+ const allSelected = value.length === rows.length;
75
+ return (_jsx(Card, { padding: 0, children: _jsxs(Table, { children: [_jsxs(Table.Row, { children: [_jsx(Table.Heading, { width: "auto", children: _jsx(Checkbox, { inputAttributes: { "aria-label": "Select all" }, name: "all", checked: allSelected, indeterminate: !!value.length && value.length < rows.length, onChange: () => {
76
+ setValue(allSelected ? [] : rows.map((row) => row.id));
77
+ } }) }), _jsx(Table.Heading, { minWidth: "180px", children: "Event type" }), _jsx(Table.Heading, { children: "Event ID" }), _jsx(Table.Heading, { align: "end", minWidth: "120px", children: "Triggered at" }), _jsx(Table.Heading, {})] }), rows.map((row) => (_jsxs(Table.Row, { highlighted: value.includes(row.id), children: [_jsx(Table.Cell, { children: _jsx(Checkbox, { name: "hey", value: row.id, inputAttributes: { "aria-label": "Select the row" }, checked: value.includes(row.id), onChange: (args) => {
78
+ setValue((prev) => {
79
+ if (!args.value)
80
+ return prev;
81
+ if (args.checked)
82
+ return [...prev, args.value];
83
+ return prev.filter((item) => item !== args.value);
84
+ });
85
+ } }) }), _jsx(Table.Cell, { children: row.name }), _jsx(Table.Cell, { children: row.id }), _jsx(Table.Cell, { align: "end", children: row.time }), _jsx(Table.Cell, { width: "auto", children: _jsxs(DropdownMenu, { position: "bottom-end", children: [_jsx(DropdownMenu.Trigger, { children: (attributes) => (_jsx(Button.Aligner, { children: _jsx(Button, { attributes: attributes, icon: IconChevronDown, variant: "ghost", size: "small" }) })) }), _jsxs(DropdownMenu.Content, { children: [_jsxs(DropdownMenu.Section, { children: [_jsx(DropdownMenu.Item, { children: "Trigger event" }), _jsx(DropdownMenu.Item, { children: "Log details" })] }), _jsx(DropdownMenu.Section, { children: _jsx(DropdownMenu.Item, { color: "critical", children: "Remote entry" }) })] })] }) })] }, row.id)))] }) }));
86
+ };
87
+ const ThemePlayground = () => {
88
+ return (_jsxs(Grid, { gap: 4, columns: 12, children: [_jsx(Grid.Item, { colSpan: 12, children: _jsx(Palette, {}) }), _jsx(Grid.Item, { colSpan: 6, children: _jsxs(View, { gap: 4, children: [_jsx(ExampleAnalytics, {}), _jsx(ExampleLogin, {}), _jsx(ExampleAlert, {})] }) }), _jsx(Grid.Item, { colSpan: 6, children: _jsxs(View, { gap: 4, children: [_jsx(ExampleSocial, {}), _jsx(ExampleSettings, {}), _jsx(ExampleCheckboxCard, {})] }) }), _jsx(Grid.Item, { colSpan: 12, children: _jsx(ExampleTable, {}) }), _jsx(Grid.Item, { colSpan: 12, children: _jsxs(View, { gap: 2, padding: 4, backgroundColor: "page-faded", borderRadius: "medium", children: [_jsxs(View, { direction: "row", gap: 2, children: [_jsx(Button, { color: "neutral", onClick: () => { }, children: "Neutral" }), _jsx(Button, { color: "primary", onClick: () => { }, children: "Primary" }), _jsx(Button, { color: "critical", onClick: () => { }, children: "Critical" }), _jsx(Button, { color: "positive", onClick: () => { }, children: "Positive" }), _jsx(Button, { color: "primary", disabled: true, onClick: () => { }, children: "Disabled" }), _jsx(Badge, { color: "warning", children: "Warning" })] }), _jsxs(View, { direction: "row", gap: 2, children: [_jsx(Button, { color: "neutral", variant: "faded", onClick: () => { }, children: "Neutral" }), _jsx(Button, { color: "primary", variant: "faded", onClick: () => { }, children: "Primary" }), _jsx(Button, { color: "critical", variant: "faded", onClick: () => { }, children: "Critical" }), _jsx(Button, { color: "positive", variant: "faded", onClick: () => { }, children: "Positive" }), _jsx(Button, { color: "primary", variant: "faded", disabled: true, onClick: () => { }, children: "Disabled" }), _jsx(Badge, { color: "warning", variant: "faded", children: "Warning" })] }), _jsxs(View, { direction: "row", gap: 2, children: [_jsx(Button, { color: "neutral", variant: "outline", onClick: () => { }, children: "Neutral" }), _jsx(Button, { color: "primary", variant: "outline", onClick: () => { }, children: "Primary" }), _jsx(Button, { color: "critical", variant: "outline", onClick: () => { }, children: "Critical" }), _jsx(Button, { color: "positive", variant: "outline", onClick: () => { }, children: "Positive" }), _jsx(Button, { color: "primary", variant: "outline", disabled: true, onClick: () => { }, children: "Disabled" }), _jsx(Badge, { color: "warning", variant: "outline", children: "Warning" })] })] }) })] }));
89
+ };
90
+ export default ThemePlayground;
@@ -0,0 +1,16 @@
1
+ declare const _default: {
2
+ title: string;
3
+ parameters: {
4
+ iframe: {
5
+ url: string;
6
+ };
7
+ a11y: {
8
+ disable: boolean;
9
+ };
10
+ };
11
+ };
12
+ export default _default;
13
+ export declare const test: () => import("react").JSX.Element;
14
+ export declare const base: () => import("react").JSX.Element;
15
+ export declare const generation: () => import("react").JSX.Element;
16
+ export declare const onColors: () => import("react").JSX.Element;
@@ -1,18 +1,21 @@
1
- import React from "react";
2
- import { Example } from "../../../utilities/storybook/index.js";
3
- import View from "../../../components/View/index.js";
4
- import Button from "../../../components/Button/index.js";
5
- import Badge from "../../../components/Badge/index.js";
6
- import Alert from "../../../components/Alert/index.js";
7
- import Card from "../../../components/Card/index.js";
8
- import Avatar from "../../../components/Avatar/index.js";
9
- import DropdownMenu from "../../../components/DropdownMenu/index.js";
10
- import TextField from "../../../components/TextField/index.js";
11
- import Theme from "../../../components/Theme/index.js";
12
- import IconZap from "../../../icons/Mic.js";
13
- import Link from "../../../components/Link/index.js";
14
- import Text from "../../../components/Text/index.js";
15
- import { getThemeCSS, generateThemeColors, baseThemeDefinition } from "../../index.js";
1
+ import { useLayoutEffect, useState } from "react";
2
+ import { Example } from "../utilities/storybook/index.js";
3
+ import View from "../components/View/index.js";
4
+ import Button from "../components/Button/index.js";
5
+ import Badge from "../components/Badge/index.js";
6
+ import Alert from "../components/Alert/index.js";
7
+ import Card from "../components/Card/index.js";
8
+ import Avatar from "../components/Avatar/index.js";
9
+ import DropdownMenu from "../components/DropdownMenu/index.js";
10
+ import TextField from "../components/TextField/index.js";
11
+ import Theme, { useTheme } from "../components/Theme/index.js";
12
+ import IconZap from "../icons/Mic.js";
13
+ import Link from "../components/Link/index.js";
14
+ import Text from "../components/Text/index.js";
15
+ import { getThemeCSS, generateThemeColors, baseThemeDefinition } from "../themes/index.js";
16
+ import ThemePlayground from "./ThemesPlayground.js";
17
+ import Actionable from "../components/Actionable/index.js";
18
+ import Switch from "../components/Switch/index.js";
16
19
  export default {
17
20
  title: "Internal/Themes",
18
21
  parameters: {
@@ -23,6 +26,62 @@ export default {
23
26
  },
24
27
  },
25
28
  };
29
+ const colors = [
30
+ "#2563eb",
31
+ "#4f39f6",
32
+ "#4a8200",
33
+ "#0891b2",
34
+ "#34d399",
35
+ "#fe9a00",
36
+ "#be185d",
37
+ "#ff2056",
38
+ "#000000",
39
+ ];
40
+ export const test = () => {
41
+ const { colorMode } = useTheme();
42
+ const [activeColor, setColor] = useState(colors[0]);
43
+ const [theme, setTheme] = useState("");
44
+ const [algo, setAlgo] = useState("wcag");
45
+ useLayoutEffect(() => {
46
+ setTheme(getThemeCSS("test", {
47
+ color: generateThemeColors({
48
+ primary: activeColor === colors[0] ? undefined : activeColor,
49
+ }),
50
+ }, {
51
+ colorContrastAlgorithm: algo,
52
+ }));
53
+ }, [activeColor, algo]);
54
+ return (<>
55
+ <style>{theme}</style>
56
+ <Theme name="test">
57
+ <View gap={4}>
58
+ <View direction="row" align="center" gap={4}>
59
+ {colors.map((color) => {
60
+ const hex = colorMode === "dark" && color === "#000000" ? "#ffffff" : color;
61
+ return (<Actionable key={color} onClick={() => setColor(color)} borderRadius="inherit">
62
+ <View width={5} height={5} borderRadius="circular" attributes={{
63
+ style: {
64
+ transition: `box-shadow var(--rs-duration-fast) var(--rs-easing-standard)`,
65
+ background: hex,
66
+ boxShadow: color === activeColor
67
+ ? `0 0 0 3px var(--rs-color-background-elevation-base), 0 0 0 5px ${hex}`
68
+ : undefined,
69
+ },
70
+ }}/>
71
+ </Actionable>);
72
+ })}
73
+ <View.Item gapBefore="auto">
74
+ <Switch name="algo" onChange={(args) => setAlgo(args.checked ? "apca" : "wcag")}>
75
+ Use APCA
76
+ </Switch>
77
+ </View.Item>
78
+ </View>
79
+
80
+ <ThemePlayground />
81
+ </View>
82
+ </Theme>
83
+ </>);
84
+ };
26
85
  const css = getThemeCSS("green", {
27
86
  color: {
28
87
  backgroundPrimary: { hex: "#1abc9c", hexDark: "#00E5C4" },
@@ -1,5 +1,5 @@
1
1
  import reshapedDefinition from "./reshaped.js";
2
- import generateColors from "../utilities/generateColors.js";
2
+ import generateColors from "../tokens/color/utilities/generateColors.js";
3
3
  const theme = {
4
4
  ...reshapedDefinition,
5
5
  color: generateColors(),
@@ -1,4 +1,4 @@
1
1
  import type * as T from "./color.types";
2
2
  import type { Transformer } from "../types";
3
- declare const transformedToken: Transformer<T.Token>;
4
- export default transformedToken;
3
+ declare const transformToken: Transformer<T.Token>;
4
+ export default transformToken;
@@ -1,18 +1,40 @@
1
- const transformedToken = (name, token) => {
2
- const hasDarkMode = !!token.hexDark;
3
- const defaultMode = hasDarkMode ? "light" : undefined;
1
+ const transformTokenForMode = (args) => {
2
+ const { hex, oklch } = args;
3
+ if (oklch) {
4
+ const components = `${Number(oklch.l.toFixed(4))} ${Number(oklch.c.toFixed(4))} ${Number(oklch.h?.toFixed(4) || 0)}`;
5
+ const alphaSuffix = oklch?.alpha === undefined ? "" : ` / ${Number(oklch.alpha.toFixed(4))}`;
6
+ return `oklch(${components}${alphaSuffix})`;
7
+ }
8
+ if (hex)
9
+ return hex;
10
+ throw new Error(`[Reshaped] ${JSON.stringify(args)} is missing a color value`);
11
+ };
12
+ const transformToken = (name, token) => {
13
+ const { hex, hexDark, oklch, oklchDark } = token;
14
+ // Apply color to both modes if dark mode is not available
15
+ const hasDark = !!hexDark || !!oklchDark;
16
+ const value = transformTokenForMode({ oklch, hex });
17
+ const darkValue = hasDark ? transformTokenForMode({ oklch: oklchDark, hex: hexDark }) : undefined;
18
+ const separateModes = hasDark && value !== darkValue;
19
+ const defaultMode = separateModes ? "light" : undefined;
4
20
  const result = [
5
- { name, tokenType: "color", type: "variable", value: token.hex, mode: defaultMode },
21
+ {
22
+ name,
23
+ tokenType: "color",
24
+ type: "variable",
25
+ value,
26
+ mode: defaultMode,
27
+ },
6
28
  ];
7
- if (token.hexDark) {
29
+ if (darkValue && separateModes) {
8
30
  result.push({
9
31
  name,
10
32
  tokenType: "color",
11
33
  type: "variable",
12
- value: token.hexDark,
34
+ value: darkValue,
13
35
  mode: "dark",
14
36
  });
15
37
  }
16
38
  return result;
17
39
  };
18
- export default transformedToken;
40
+ export default transformToken;
@@ -1,7 +1,32 @@
1
+ import type { Oklch } from "culori/fn";
2
+ export type Hue = "primary" | "positive" | "critical" | "warning" | "neutral" | "brand";
1
3
  export type Name = "foregroundNeutral" | "foregroundNeutralFaded" | "foregroundDisabled" | "foregroundPrimary" | "foregroundCritical" | "foregroundWarning" | "foregroundPositive" | "borderNeutral" | "borderNeutralFaded" | "borderDisabled" | "borderPrimary" | "borderPrimaryFaded" | "borderCritical" | "borderCriticalFaded" | "borderWarning" | "borderWarningFaded" | "borderPositive" | "borderPositiveFaded" | "backgroundNeutral" | "backgroundNeutralFaded" | "backgroundDisabled" | "backgroundDisabledFaded" | "backgroundPrimary" | "backgroundPrimaryFaded" | "backgroundCritical" | "backgroundCriticalFaded" | "backgroundWarning" | "backgroundWarningFaded" | "backgroundPositive" | "backgroundPositiveFaded" | "backgroundPage" | "backgroundPageFaded" | "backgroundElevationBase" | "backgroundElevationRaised" | "backgroundElevationOverlay" | "brand" | "white" | "black";
2
4
  export type GeneratedOnName = "onBackgroundNeutral" | "onBackgroundPrimary" | "onBackgroundPositive" | "onBackgroundWarning" | "onBackgroundCritical";
3
5
  export type GeneratedRGBName = "rgbBackgroundNeutral" | "rgbBackgroundNeutralFaded" | "rgbBackgroundDisabled" | "rgbBackgroundDisabledFaded" | "rgbBackgroundPrimary" | "rgbBackgroundPrimaryFaded" | "rgbBackgroundCritical" | "rgbBackgroundCriticalFaded" | "rgbBackgroundWarning" | "rgbBackgroundWarningFaded" | "rgbBackgroundPositive" | "rgbBackgroundPositiveFaded" | "rgbBackgroundPage" | "rgbBackgroundPageFaded" | "rgbBackgroundElevationBase" | "rgbBackgroundElevationRaised" | "rgbBackgroundElevationOverlay";
4
- export type Token = {
5
- hex: string;
6
- hexDark?: string;
6
+ export type RgbColor = {
7
+ r: number;
8
+ g: number;
9
+ b: number;
10
+ };
11
+ export type OklchColor = Omit<Oklch, "mode">;
12
+ export type HexColor = string;
13
+ export type ColorValue = HexColor | Token;
14
+ export type OklchOnlyToken = {
15
+ oklch: OklchColor;
16
+ oklchDark?: OklchColor;
17
+ };
18
+ export type OklchToken = OklchOnlyToken & {
19
+ hex?: never;
20
+ hexDark?: never;
21
+ };
22
+ export type HexToken = {
23
+ hex: HexColor;
24
+ hexDark?: HexColor;
25
+ oklch?: never;
26
+ oklchDark?: never;
27
+ };
28
+ export type Token = OklchToken | HexToken;
29
+ export type InternalToken = {
30
+ oklch: Oklch;
31
+ oklchDark?: Oklch;
7
32
  };
@@ -0,0 +1,20 @@
1
+ import { type Oklch } from "culori/fn";
2
+ export declare const getOnColorWCAG: (args: {
3
+ bgColor: Oklch;
4
+ lightColor: Oklch;
5
+ darkColor: Oklch;
6
+ }) => Oklch;
7
+ export declare const getOnColorAPCA: (args: {
8
+ bgColor: Oklch;
9
+ lightColor: Oklch;
10
+ darkColor: Oklch;
11
+ }) => Oklch;
12
+ /**
13
+ * General
14
+ */
15
+ export declare const getOnColor: (args: {
16
+ bgColor: Oklch;
17
+ lightColor: Oklch;
18
+ darkColor: Oklch;
19
+ algorithm?: "wcag" | "apca";
20
+ }) => Oklch;
@@ -0,0 +1,58 @@
1
+ import { oklchToRgb } from "./convert.js";
2
+ /**
3
+ * WCAG
4
+ */
5
+ const RED = 0.2126;
6
+ const GREEN = 0.7152;
7
+ const BLUE = 0.0722;
8
+ const GAMMA = 2.4;
9
+ function luminanceWCAG(r, g, b) {
10
+ const a = [r, g, b].map((v) => {
11
+ return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, GAMMA);
12
+ });
13
+ return a[0] * RED + a[1] * GREEN + a[2] * BLUE;
14
+ }
15
+ function contrastWCAG(rgb1, rgb2) {
16
+ const lum1 = luminanceWCAG(...rgb1);
17
+ const lum2 = luminanceWCAG(...rgb2);
18
+ const brightest = Math.max(lum1, lum2);
19
+ const darkest = Math.min(lum1, lum2);
20
+ return (brightest + 0.05) / (darkest + 0.05);
21
+ }
22
+ export const getOnColorWCAG = (args) => {
23
+ const { bgColor, lightColor, darkColor } = args;
24
+ const bgRgb = oklchToRgb(bgColor);
25
+ const lightRgb = oklchToRgb(lightColor);
26
+ return contrastWCAG([bgRgb.r, bgRgb.g, bgRgb.b], [lightRgb.r, lightRgb.g, lightRgb.b]) > 4.5
27
+ ? lightColor
28
+ : darkColor;
29
+ };
30
+ /**
31
+ * APCA
32
+ */
33
+ function luminanceAPCA(oklch) {
34
+ const { r, g, b } = oklchToRgb(oklch);
35
+ return (0.2126 * Math.pow(r / 255, 2.2) +
36
+ 0.7152 * Math.pow(g / 255, 2.2) +
37
+ 0.0722 * Math.pow(b / 255, 2.2));
38
+ }
39
+ export const getOnColorAPCA = (args) => {
40
+ const { bgColor, lightColor, darkColor } = args;
41
+ const backgroundLuminance = luminanceAPCA(bgColor);
42
+ const lightLuminance = luminanceAPCA(lightColor);
43
+ const darkLuminance = luminanceAPCA(darkColor);
44
+ const contrastWithLight = Math.abs(lightLuminance - backgroundLuminance);
45
+ const contrastWithDark = Math.abs(darkLuminance - backgroundLuminance);
46
+ return contrastWithLight > contrastWithDark ? lightColor : darkColor;
47
+ };
48
+ /**
49
+ * General
50
+ */
51
+ export const getOnColor = (args) => {
52
+ if (args.algorithm === "apca") {
53
+ return getOnColorAPCA(args);
54
+ }
55
+ else {
56
+ return getOnColorWCAG(args);
57
+ }
58
+ };