@pickle-ui/react 1.0.0-beta.6 → 1.0.0-beta.8

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 (156) hide show
  1. package/README.md +4 -2
  2. package/dist/components/Alert.d.ts.map +1 -1
  3. package/dist/components/Alert.js.map +1 -1
  4. package/dist/components/AlertDialog.d.ts.map +1 -1
  5. package/dist/components/AlertDialog.js.map +1 -1
  6. package/dist/components/Avatar.js.map +1 -1
  7. package/dist/components/Badge.js.map +1 -1
  8. package/dist/components/Button.d.ts.map +1 -1
  9. package/dist/components/Button.js.map +1 -1
  10. package/dist/components/Card.d.ts.map +1 -1
  11. package/dist/components/Card.js.map +1 -1
  12. package/dist/components/Dialog.d.ts.map +1 -1
  13. package/dist/components/Dialog.js.map +1 -1
  14. package/dist/components/Flex.d.ts.map +1 -1
  15. package/dist/components/Flex.js.map +1 -1
  16. package/dist/components/Grid.d.ts.map +1 -1
  17. package/dist/components/Grid.js.map +1 -1
  18. package/dist/components/Group.d.ts.map +1 -1
  19. package/dist/components/Group.js.map +1 -1
  20. package/dist/components/Menu.d.ts.map +1 -1
  21. package/dist/components/Menu.js.map +1 -1
  22. package/dist/components/Popover.js.map +1 -1
  23. package/dist/components/Progress.js.map +1 -1
  24. package/dist/components/ScrollArea.js.map +1 -1
  25. package/dist/components/Separator.js.map +1 -1
  26. package/dist/components/Sidebar.js.map +1 -1
  27. package/dist/components/Spinner.js.map +1 -1
  28. package/dist/components/Table.js.map +1 -1
  29. package/dist/components/Text.js.map +1 -1
  30. package/dist/components/Toast.d.ts.map +1 -1
  31. package/dist/components/Toast.js.map +1 -1
  32. package/dist/components/Tooltip.js.map +1 -1
  33. package/dist/components/XStack.d.ts.map +1 -1
  34. package/dist/components/XStack.js.map +1 -1
  35. package/dist/components/YStack.d.ts.map +1 -1
  36. package/dist/components/YStack.js.map +1 -1
  37. package/dist/components/_internal/dialog-styles.d.ts.map +1 -1
  38. package/dist/components/_internal/dialog-styles.js.map +1 -1
  39. package/dist/components/_internal/layout.js.map +1 -1
  40. package/dist/components/form/Checkbox.d.ts.map +1 -1
  41. package/dist/components/form/Checkbox.js.map +1 -1
  42. package/dist/components/form/ColorPickerPrimitive.d.ts +5 -5
  43. package/dist/components/form/ColorPickerPrimitive.d.ts.map +1 -1
  44. package/dist/components/form/ColorPickerPrimitive.js +6 -6
  45. package/dist/components/form/ColorPickerPrimitive.js.map +1 -1
  46. package/dist/components/form/Combobox.d.ts.map +1 -1
  47. package/dist/components/form/Combobox.js.map +1 -1
  48. package/dist/components/form/Form.d.ts +1 -1
  49. package/dist/components/form/Form.d.ts.map +1 -1
  50. package/dist/components/form/Form.js +1 -1
  51. package/dist/components/form/Form.js.map +1 -1
  52. package/dist/components/form/FormCheckbox.d.ts +2 -2
  53. package/dist/components/form/FormCheckbox.d.ts.map +1 -1
  54. package/dist/components/form/FormCheckbox.js +1 -1
  55. package/dist/components/form/FormCheckbox.js.map +1 -1
  56. package/dist/components/form/FormDescription.js.map +1 -1
  57. package/dist/components/form/FormError.js.map +1 -1
  58. package/dist/components/form/FormField.d.ts.map +1 -1
  59. package/dist/components/form/FormField.js.map +1 -1
  60. package/dist/components/form/FormInput.d.ts +1 -1
  61. package/dist/components/form/FormInput.d.ts.map +1 -1
  62. package/dist/components/form/FormInput.js +2 -2
  63. package/dist/components/form/FormInput.js.map +1 -1
  64. package/dist/components/form/FormInputGroup.d.ts +1 -1
  65. package/dist/components/form/FormInputGroup.d.ts.map +1 -1
  66. package/dist/components/form/FormInputGroup.js +1 -1
  67. package/dist/components/form/FormInputGroup.js.map +1 -1
  68. package/dist/components/form/FormLabel.js.map +1 -1
  69. package/dist/components/form/FormRadioGroup.d.ts.map +1 -1
  70. package/dist/components/form/FormRoot.js.map +1 -1
  71. package/dist/components/form/FormSwitch.d.ts +2 -2
  72. package/dist/components/form/FormSwitch.d.ts.map +1 -1
  73. package/dist/components/form/FormSwitch.js +1 -1
  74. package/dist/components/form/FormSwitch.js.map +1 -1
  75. package/dist/components/form/FormTextarea.d.ts +1 -1
  76. package/dist/components/form/FormTextarea.d.ts.map +1 -1
  77. package/dist/components/form/FormTextarea.js +1 -1
  78. package/dist/components/form/FormTextarea.js.map +1 -1
  79. package/dist/components/form/Input.d.ts.map +1 -1
  80. package/dist/components/form/Input.js.map +1 -1
  81. package/dist/components/form/InputGroup.d.ts.map +1 -1
  82. package/dist/components/form/InputGroup.js.map +1 -1
  83. package/dist/components/form/RadioGroup.d.ts.map +1 -1
  84. package/dist/components/form/RadioGroup.js.map +1 -1
  85. package/dist/components/form/Select.d.ts.map +1 -1
  86. package/dist/components/form/Select.js.map +1 -1
  87. package/dist/components/form/Slider.d.ts.map +1 -1
  88. package/dist/components/form/Slider.js.map +1 -1
  89. package/dist/components/form/Switch.d.ts.map +1 -1
  90. package/dist/components/form/Switch.js.map +1 -1
  91. package/dist/components/form/Textarea.d.ts.map +1 -1
  92. package/dist/components/form/Textarea.js.map +1 -1
  93. package/dist/entries/theme.d.ts +2 -2
  94. package/dist/entries/theme.d.ts.map +1 -1
  95. package/dist/entries/theme.js +1 -1
  96. package/dist/entries/theme.js.map +1 -1
  97. package/dist/hooks/use-as-ref.js +1 -1
  98. package/dist/hooks/use-as-ref.js.map +1 -1
  99. package/package.json +4 -2
  100. package/src/base.css +7 -25
  101. package/src/components/Alert.tsx +1 -2
  102. package/src/components/AlertDialog.tsx +1 -2
  103. package/src/components/Avatar.tsx +1 -1
  104. package/src/components/Badge.tsx +1 -1
  105. package/src/components/Button.tsx +1 -2
  106. package/src/components/Card.tsx +1 -2
  107. package/src/components/Dialog.tsx +1 -2
  108. package/src/components/Flex.tsx +1 -2
  109. package/src/components/Grid.tsx +1 -2
  110. package/src/components/Group.tsx +1 -2
  111. package/src/components/Menu.tsx +1 -2
  112. package/src/components/Popover.tsx +1 -1
  113. package/src/components/Progress.tsx +1 -1
  114. package/src/components/ScrollArea.tsx +1 -1
  115. package/src/components/Separator.tsx +1 -1
  116. package/src/components/Sidebar.tsx +1 -1
  117. package/src/components/Spinner.tsx +1 -1
  118. package/src/components/Table.tsx +1 -1
  119. package/src/components/Text.tsx +1 -1
  120. package/src/components/Toast.tsx +1 -2
  121. package/src/components/Tooltip.tsx +1 -1
  122. package/src/components/XStack.tsx +1 -2
  123. package/src/components/YStack.tsx +1 -2
  124. package/src/components/_internal/Story.tsx +1 -2
  125. package/src/components/_internal/dialog-styles.ts +1 -2
  126. package/src/components/_internal/layout.ts +1 -1
  127. package/src/components/form/Checkbox.tsx +1 -1
  128. package/src/components/form/ColorPickerPrimitive.tsx +18 -19
  129. package/src/components/form/Combobox.tsx +1 -2
  130. package/src/components/form/Form.tsx +1 -2
  131. package/src/components/form/FormCheckbox.tsx +1 -2
  132. package/src/components/form/FormDescription.tsx +1 -1
  133. package/src/components/form/FormError.tsx +1 -1
  134. package/src/components/form/FormField.tsx +1 -2
  135. package/src/components/form/FormInput.tsx +2 -3
  136. package/src/components/form/FormInputGroup.tsx +1 -2
  137. package/src/components/form/FormLabel.tsx +1 -1
  138. package/src/components/form/FormRoot.tsx +1 -1
  139. package/src/components/form/FormSwitch.tsx +1 -2
  140. package/src/components/form/FormTextarea.tsx +1 -2
  141. package/src/components/form/Input.tsx +1 -1
  142. package/src/components/form/InputGroup.tsx +1 -2
  143. package/src/components/form/RadioGroup.tsx +1 -1
  144. package/src/components/form/Select.stories.tsx +1 -2
  145. package/src/components/form/Select.tsx +1 -1
  146. package/src/components/form/Slider.tsx +1 -1
  147. package/src/components/form/Switch.tsx +1 -1
  148. package/src/components/form/Textarea.tsx +1 -1
  149. package/src/entries/theme.ts +2 -2
  150. package/src/hooks/use-as-ref.ts +1 -1
  151. package/dist/lib/theme.d.ts +0 -40
  152. package/dist/lib/theme.d.ts.map +0 -1
  153. package/dist/lib/theme.js +0 -82
  154. package/dist/lib/theme.js.map +0 -1
  155. package/src/lib/theme.test.ts +0 -64
  156. package/src/lib/theme.ts +0 -143
@@ -2,10 +2,9 @@
2
2
 
3
3
  import * as React from 'react';
4
4
 
5
- import { InputGroup } from '@/components/form/InputGroup';
6
-
7
5
  import { FormFieldContext, FormInputGroupContext } from './Form.context';
8
6
  import { FormInput, type FormInputProps } from './FormInput';
7
+ import { InputGroup } from './InputGroup';
9
8
 
10
9
  type FormInputGroupProps = React.ComponentProps<typeof InputGroup>;
11
10
 
@@ -3,7 +3,7 @@
3
3
  import { Field } from '@base-ui/react/field';
4
4
  import * as React from 'react';
5
5
 
6
- import { cn } from '@/lib/utils';
6
+ import { cn } from '../../lib/utils';
7
7
 
8
8
  type FormLabelProps = React.ComponentProps<typeof Field.Label>;
9
9
 
@@ -8,7 +8,7 @@ import {
8
8
  type UseFormReturn,
9
9
  } from 'react-hook-form';
10
10
 
11
- import { cn } from '@/lib/utils';
11
+ import { cn } from '../../lib/utils';
12
12
 
13
13
  type FormProps<FormValues extends FieldValues = FieldValues> = Omit<
14
14
  React.ComponentProps<'form'>,
@@ -2,14 +2,13 @@
2
2
 
3
3
  import { useController } from 'react-hook-form';
4
4
 
5
- import { Switch, type SwitchProps } from '@/components/form/Switch';
6
-
7
5
  import {
8
6
  useFormControlProps,
9
7
  useFormMethods,
10
8
  useRequiredFieldName,
11
9
  type WithoutAriaInvalid,
12
10
  } from './Form.context';
11
+ import { Switch, type SwitchProps } from './Switch';
13
12
 
14
13
  type FormSwitchProps = WithoutAriaInvalid<SwitchProps>;
15
14
 
@@ -2,14 +2,13 @@
2
2
 
3
3
  import * as React from 'react';
4
4
 
5
- import { Textarea } from '@/components/form/Textarea';
6
-
7
5
  import {
8
6
  useFormControlProps,
9
7
  useFormMethods,
10
8
  useRequiredFieldName,
11
9
  type WithoutAriaInvalid,
12
10
  } from './Form.context';
11
+ import { Textarea } from './Textarea';
13
12
 
14
13
  type FormTextareaProps = WithoutAriaInvalid<
15
14
  React.ComponentProps<typeof Textarea>
@@ -3,7 +3,7 @@
3
3
  import { Input as InputPrimitive } from '@base-ui/react/input';
4
4
  import * as React from 'react';
5
5
 
6
- import { cn, type SlotClassNames } from '@/lib/utils';
6
+ import { cn, type SlotClassNames } from '../../lib/utils';
7
7
 
8
8
  // Matches the `Button` height for the same size, so they line up side by side.
9
9
  const inputControlHeights = {
@@ -3,8 +3,7 @@
3
3
  import { cva, type VariantProps } from 'class-variance-authority';
4
4
  import * as React from 'react';
5
5
 
6
- import { cn } from '@/lib/utils';
7
-
6
+ import { cn } from '../../lib/utils';
8
7
  import { Button, type ButtonProps } from '../Button';
9
8
  import { Input, type InputProps } from './Input';
10
9
 
@@ -4,7 +4,7 @@ import { Radio as RadioPrimitive } from '@base-ui/react/radio';
4
4
  import { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group';
5
5
  import * as React from 'react';
6
6
 
7
- import { cn, type SlotClassNames } from '@/lib/utils';
7
+ import { cn, type SlotClassNames } from '../../lib/utils';
8
8
 
9
9
  type RadioGroupItemProps = React.ComponentProps<typeof RadioPrimitive.Root> & {
10
10
  classNames?: SlotClassNames<'indicator' | 'label'>;
@@ -1,7 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
 
3
- import { cn } from '@/lib/utils';
4
-
3
+ import { cn } from '../../lib/utils';
5
4
  import { type ComponentMatrixCellProps, Story } from '../_internal/Story';
6
5
  import { Select } from './Select';
7
6
 
@@ -9,7 +9,7 @@ import {
9
9
  } from '@tabler/icons-react';
10
10
  import * as React from 'react';
11
11
 
12
- import { cn, type SlotClassNames } from '@/lib/utils';
12
+ import { cn, type SlotClassNames } from '../../lib/utils';
13
13
 
14
14
  type SelectContentProps = Pick<
15
15
  React.ComponentProps<typeof SelectPrimitive.Positioner>,
@@ -2,7 +2,7 @@ import type { CSSProperties, Ref } from 'react';
2
2
 
3
3
  import { Slider as SliderPrimitive } from '@base-ui/react/slider';
4
4
 
5
- import { cn, type SlotClassNames } from '@/lib/utils';
5
+ import { cn, type SlotClassNames } from '../../lib/utils';
6
6
 
7
7
  type SliderLabelProps = SliderPrimitive.Label.Props & {
8
8
  ref?: Ref<HTMLDivElement>;
@@ -3,7 +3,7 @@
3
3
  import { Switch as SwitchPrimitive } from '@base-ui/react/switch';
4
4
  import * as React from 'react';
5
5
 
6
- import { cn, type SlotClassNames } from '@/lib/utils';
6
+ import { cn, type SlotClassNames } from '../../lib/utils';
7
7
 
8
8
  type SwitchControlProps = React.ComponentProps<typeof SwitchPrimitive.Root> & {
9
9
  classNames?: SlotClassNames<'thumb'>;
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
 
3
- import { cn, type SlotClassNames } from '@/lib/utils';
3
+ import { cn, type SlotClassNames } from '../../lib/utils';
4
4
 
5
5
  type TextareaProps = React.ComponentProps<'textarea'> & {
6
6
  /** Class overrides for the inner `textarea`; `className` styles the control wrapper. */
@@ -1,8 +1,8 @@
1
- export { createTheme, themeToCss } from '../lib/theme';
1
+ export { createTheme, themeToCss } from '@pickle-ui/tokens/theme';
2
2
  export type {
3
3
  Theme,
4
4
  ThemeCssOptions,
5
5
  ThemeFonts,
6
6
  ThemeOptions,
7
7
  ThemeVariables,
8
- } from '../lib/theme';
8
+ } from '@pickle-ui/tokens/theme';
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
 
3
- import { useIsomorphicLayoutEffect } from '@/hooks/use-isomorphic-layout-effect';
3
+ import { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect';
4
4
 
5
5
  function useAsRef<T>(props: T) {
6
6
  const ref = React.useRef<T>(props);
@@ -1,40 +0,0 @@
1
- /**
2
- * Theme generator: derives Pickle's brand-dependent tokens from a single
3
- * primary color so consumers can theme the library without hand-writing CSS
4
- * variables. Tokens that do not depend on the brand (neutrals, status colors)
5
- * keep the values from `@pickle-ui/react/theme.css`.
6
- */
7
- type Theme = {
8
- dark: ThemeVariables;
9
- light: ThemeVariables;
10
- };
11
- type ThemeCssOptions = {
12
- darkSelector?: string;
13
- lightSelector?: string;
14
- };
15
- type ThemeFonts = {
16
- heading?: string;
17
- mono?: string;
18
- sans?: string;
19
- };
20
- type ThemeOptions = {
21
- /** Font stacks, written to `--font-sans`, `--font-heading` and `--font-mono`. */
22
- fonts?: ThemeFonts;
23
- /** Brand color in any CSS notation (hex, rgb, hsl, oklch, …). */
24
- primary: string;
25
- /** Brand color for the dark scheme. Defaults to a lighter version of `primary`. */
26
- primaryDark?: string;
27
- /** Base radius, e.g. `'0.5rem'`. Every `rounded-*` scale derives from it. */
28
- radius?: string;
29
- };
30
- type ThemeVariables = Record<`--${string}`, string>;
31
- /**
32
- * Build the light and dark token sets for a brand color. Apply the result
33
- * with `themeToCss` in a stylesheet, or spread `theme.light` / `theme.dark`
34
- * as the `style` of a wrapper element.
35
- */
36
- export declare function createTheme(options: ThemeOptions): Theme;
37
- /** Serialize a theme as CSS rules for the light and dark selectors. */
38
- export declare function themeToCss(theme: Theme, { darkSelector, lightSelector }?: ThemeCssOptions): string;
39
- export type { Theme, ThemeCssOptions, ThemeFonts, ThemeOptions, ThemeVariables, };
40
- //# sourceMappingURL=theme.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/lib/theme.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,UAAU,GAAG;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,iFAAiF;IACjF,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,KAAK,cAAc,GAAG,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC;AAOpD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CAWxD;AAED,uEAAuE;AACvE,wBAAgB,UAAU,CACxB,KAAK,EAAE,KAAK,EACZ,EAAE,YAAsB,EAAE,aAAuB,EAAE,GAAE,eAAoB,GACxE,MAAM,CAOR;AAyDD,YAAY,EACV,KAAK,EACL,eAAe,EACf,UAAU,EACV,YAAY,EACZ,cAAc,GACf,CAAC"}
package/dist/lib/theme.js DELETED
@@ -1,82 +0,0 @@
1
- /**
2
- * Theme generator: derives Pickle's brand-dependent tokens from a single
3
- * primary color so consumers can theme the library without hand-writing CSS
4
- * variables. Tokens that do not depend on the brand (neutrals, status colors)
5
- * keep the values from `@pickle-ui/react/theme.css`.
6
- */
7
- import { clampChroma, converter, formatCss, parse, wcagContrast, } from 'culori';
8
- const toOklch = converter('oklch');
9
- const LIGHT_FOREGROUND = { c: 0, h: 0, l: 1, mode: 'oklch' };
10
- const DARK_FOREGROUND = { c: 0.006, h: 285.9, l: 0.21, mode: 'oklch' };
11
- /**
12
- * Build the light and dark token sets for a brand color. Apply the result
13
- * with `themeToCss` in a stylesheet, or spread `theme.light` / `theme.dark`
14
- * as the `style` of a wrapper element.
15
- */
16
- export function createTheme(options) {
17
- const primary = parseColor(options.primary, 'primary');
18
- const primaryDark = options.primaryDark
19
- ? parseColor(options.primaryDark, 'primaryDark')
20
- : lightenForDark(primary);
21
- const shared = sharedVariables(options);
22
- return {
23
- dark: { ...shared, ...brandVariables(primaryDark) },
24
- light: { ...shared, ...brandVariables(primary) },
25
- };
26
- }
27
- /** Serialize a theme as CSS rules for the light and dark selectors. */
28
- export function themeToCss(theme, { darkSelector = '.dark', lightSelector = ':root' } = {}) {
29
- const block = (selector, variables) => `${selector} {\n${Object.entries(variables)
30
- .map(([name, value]) => ` ${name}: ${value};`)
31
- .join('\n')}\n}`;
32
- return `${block(lightSelector, theme.light)}\n\n${block(darkSelector, theme.dark)}\n`;
33
- }
34
- function brandVariables(primary) {
35
- const foreground = foregroundFor(primary);
36
- return {
37
- '--primary': format(primary),
38
- '--primary-foreground': format(foreground),
39
- '--ring': format({ ...primary, c: primary.c * 0.6 }),
40
- '--sidebar-primary': format(primary),
41
- '--sidebar-primary-foreground': format(foreground),
42
- '--sidebar-ring': format({ ...primary, c: primary.c * 0.6 }),
43
- };
44
- }
45
- /** White or near-black, whichever reads better on the given fill. */
46
- function foregroundFor(fill) {
47
- return wcagContrast(fill, LIGHT_FOREGROUND) >=
48
- wcagContrast(fill, DARK_FOREGROUND)
49
- ? LIGHT_FOREGROUND
50
- : DARK_FOREGROUND;
51
- }
52
- function format(color) {
53
- return formatCss(clampChroma(color, 'oklch'));
54
- }
55
- /** Lift a brand color so it stays vivid on dark surfaces. */
56
- function lightenForDark(color) {
57
- return {
58
- ...color,
59
- c: color.c * 0.92,
60
- l: Math.min(Math.max(color.l + 0.12, 0.6), 0.85),
61
- };
62
- }
63
- function parseColor(value, name) {
64
- const color = parse(value);
65
- if (!color) {
66
- throw new Error(`createTheme: ${name} is not a valid CSS color: ${value}`);
67
- }
68
- return toOklch(color);
69
- }
70
- function sharedVariables({ fonts, radius }) {
71
- const variables = {};
72
- if (radius)
73
- variables['--radius'] = radius;
74
- if (fonts?.sans)
75
- variables['--font-sans'] = fonts.sans;
76
- if (fonts?.heading)
77
- variables['--font-heading'] = fonts.heading;
78
- if (fonts?.mono)
79
- variables['--font-mono'] = fonts.mono;
80
- return variables;
81
- }
82
- //# sourceMappingURL=theme.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/lib/theme.ts"],"names":[],"mappings":"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"}
@@ -1,64 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { createTheme, themeToCss } from './theme';
4
-
5
- describe('createTheme', () => {
6
- it('derives primary tokens for both schemes', () => {
7
- const theme = createTheme({ primary: '#16a34a' });
8
-
9
- expect(theme.light['--primary']).toMatch(/^oklch\(/);
10
- expect(theme.dark['--primary']).toMatch(/^oklch\(/);
11
- expect(theme.light['--primary']).not.toBe(theme.dark['--primary']);
12
- expect(theme.light['--sidebar-primary']).toBe(theme.light['--primary']);
13
- });
14
-
15
- it('picks a readable foreground for light and dark fills', () => {
16
- const onDarkFill = createTheme({ primary: '#1e3a8a' });
17
- const onLightFill = createTheme({ primary: '#fde047' });
18
-
19
- expect(onDarkFill.light['--primary-foreground']).toBe('oklch(1 0 0)');
20
- expect(onLightFill.light['--primary-foreground']).not.toBe('oklch(1 0 0)');
21
- });
22
-
23
- it('uses primaryDark as given and passes radius and fonts through', () => {
24
- const theme = createTheme({
25
- fonts: { mono: 'Menlo', sans: 'Inter' },
26
- primary: 'rebeccapurple',
27
- primaryDark: 'oklch(0.8 0.1 300)',
28
- radius: '1rem',
29
- });
30
-
31
- expect(theme.dark['--primary']).toBe('oklch(0.8 0.1 300)');
32
- expect(theme.light['--radius']).toBe('1rem');
33
- expect(theme.dark['--radius']).toBe('1rem');
34
- expect(theme.light['--font-sans']).toBe('Inter');
35
- expect(theme.light['--font-mono']).toBe('Menlo');
36
- expect(theme.light['--font-heading']).toBeUndefined();
37
- });
38
-
39
- it('rejects colors it cannot parse', () => {
40
- expect(() => createTheme({ primary: 'not-a-color' })).toThrow(
41
- /not a valid CSS color/,
42
- );
43
- });
44
- });
45
-
46
- describe('themeToCss', () => {
47
- it('serializes both schemes under their selectors', () => {
48
- const css = themeToCss(createTheme({ primary: '#000', radius: '4px' }));
49
-
50
- expect(css).toMatch(/^:root \{\n {2}--radius: 4px;\n/);
51
- expect(css).toContain('\n.dark {\n');
52
- expect(css).toContain('--primary-foreground: oklch(1 0 0);');
53
- });
54
-
55
- it('accepts custom selectors', () => {
56
- const css = themeToCss(createTheme({ primary: '#000' }), {
57
- darkSelector: '[data-theme="dark"]',
58
- lightSelector: 'html',
59
- });
60
-
61
- expect(css.startsWith('html {')).toBe(true);
62
- expect(css).toContain('[data-theme="dark"] {');
63
- });
64
- });
package/src/lib/theme.ts DELETED
@@ -1,143 +0,0 @@
1
- /**
2
- * Theme generator: derives Pickle's brand-dependent tokens from a single
3
- * primary color so consumers can theme the library without hand-writing CSS
4
- * variables. Tokens that do not depend on the brand (neutrals, status colors)
5
- * keep the values from `@pickle-ui/react/theme.css`.
6
- */
7
-
8
- import {
9
- clampChroma,
10
- converter,
11
- formatCss,
12
- type Oklch,
13
- parse,
14
- wcagContrast,
15
- } from 'culori';
16
-
17
- type Theme = {
18
- dark: ThemeVariables;
19
- light: ThemeVariables;
20
- };
21
-
22
- type ThemeCssOptions = {
23
- darkSelector?: string;
24
- lightSelector?: string;
25
- };
26
-
27
- type ThemeFonts = {
28
- heading?: string;
29
- mono?: string;
30
- sans?: string;
31
- };
32
-
33
- type ThemeOptions = {
34
- /** Font stacks, written to `--font-sans`, `--font-heading` and `--font-mono`. */
35
- fonts?: ThemeFonts;
36
- /** Brand color in any CSS notation (hex, rgb, hsl, oklch, …). */
37
- primary: string;
38
- /** Brand color for the dark scheme. Defaults to a lighter version of `primary`. */
39
- primaryDark?: string;
40
- /** Base radius, e.g. `'0.5rem'`. Every `rounded-*` scale derives from it. */
41
- radius?: string;
42
- };
43
-
44
- type ThemeVariables = Record<`--${string}`, string>;
45
-
46
- const toOklch = converter('oklch');
47
-
48
- const LIGHT_FOREGROUND: Oklch = { c: 0, h: 0, l: 1, mode: 'oklch' };
49
- const DARK_FOREGROUND: Oklch = { c: 0.006, h: 285.9, l: 0.21, mode: 'oklch' };
50
-
51
- /**
52
- * Build the light and dark token sets for a brand color. Apply the result
53
- * with `themeToCss` in a stylesheet, or spread `theme.light` / `theme.dark`
54
- * as the `style` of a wrapper element.
55
- */
56
- export function createTheme(options: ThemeOptions): Theme {
57
- const primary = parseColor(options.primary, 'primary');
58
- const primaryDark = options.primaryDark
59
- ? parseColor(options.primaryDark, 'primaryDark')
60
- : lightenForDark(primary);
61
- const shared = sharedVariables(options);
62
-
63
- return {
64
- dark: { ...shared, ...brandVariables(primaryDark) },
65
- light: { ...shared, ...brandVariables(primary) },
66
- };
67
- }
68
-
69
- /** Serialize a theme as CSS rules for the light and dark selectors. */
70
- export function themeToCss(
71
- theme: Theme,
72
- { darkSelector = '.dark', lightSelector = ':root' }: ThemeCssOptions = {},
73
- ): string {
74
- const block = (selector: string, variables: ThemeVariables) =>
75
- `${selector} {\n${Object.entries(variables)
76
- .map(([name, value]) => ` ${name}: ${value};`)
77
- .join('\n')}\n}`;
78
-
79
- return `${block(lightSelector, theme.light)}\n\n${block(darkSelector, theme.dark)}\n`;
80
- }
81
-
82
- function brandVariables(primary: Oklch): ThemeVariables {
83
- const foreground = foregroundFor(primary);
84
-
85
- return {
86
- '--primary': format(primary),
87
- '--primary-foreground': format(foreground),
88
- '--ring': format({ ...primary, c: primary.c * 0.6 }),
89
- '--sidebar-primary': format(primary),
90
- '--sidebar-primary-foreground': format(foreground),
91
- '--sidebar-ring': format({ ...primary, c: primary.c * 0.6 }),
92
- };
93
- }
94
-
95
- /** White or near-black, whichever reads better on the given fill. */
96
- function foregroundFor(fill: Oklch) {
97
- return wcagContrast(fill, LIGHT_FOREGROUND) >=
98
- wcagContrast(fill, DARK_FOREGROUND)
99
- ? LIGHT_FOREGROUND
100
- : DARK_FOREGROUND;
101
- }
102
-
103
- function format(color: Oklch) {
104
- return formatCss(clampChroma(color, 'oklch'));
105
- }
106
-
107
- /** Lift a brand color so it stays vivid on dark surfaces. */
108
- function lightenForDark(color: Oklch): Oklch {
109
- return {
110
- ...color,
111
- c: color.c * 0.92,
112
- l: Math.min(Math.max(color.l + 0.12, 0.6), 0.85),
113
- };
114
- }
115
-
116
- function parseColor(value: string, name: string): Oklch {
117
- const color = parse(value);
118
-
119
- if (!color) {
120
- throw new Error(`createTheme: ${name} is not a valid CSS color: ${value}`);
121
- }
122
-
123
- return toOklch(color);
124
- }
125
-
126
- function sharedVariables({ fonts, radius }: ThemeOptions): ThemeVariables {
127
- const variables: ThemeVariables = {};
128
-
129
- if (radius) variables['--radius'] = radius;
130
- if (fonts?.sans) variables['--font-sans'] = fonts.sans;
131
- if (fonts?.heading) variables['--font-heading'] = fonts.heading;
132
- if (fonts?.mono) variables['--font-mono'] = fonts.mono;
133
-
134
- return variables;
135
- }
136
-
137
- export type {
138
- Theme,
139
- ThemeCssOptions,
140
- ThemeFonts,
141
- ThemeOptions,
142
- ThemeVariables,
143
- };