@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.
- package/README.md +4 -2
- package/dist/components/Alert.d.ts.map +1 -1
- package/dist/components/Alert.js.map +1 -1
- package/dist/components/AlertDialog.d.ts.map +1 -1
- package/dist/components/AlertDialog.js.map +1 -1
- package/dist/components/Avatar.js.map +1 -1
- package/dist/components/Badge.js.map +1 -1
- package/dist/components/Button.d.ts.map +1 -1
- package/dist/components/Button.js.map +1 -1
- package/dist/components/Card.d.ts.map +1 -1
- package/dist/components/Card.js.map +1 -1
- package/dist/components/Dialog.d.ts.map +1 -1
- package/dist/components/Dialog.js.map +1 -1
- package/dist/components/Flex.d.ts.map +1 -1
- package/dist/components/Flex.js.map +1 -1
- package/dist/components/Grid.d.ts.map +1 -1
- package/dist/components/Grid.js.map +1 -1
- package/dist/components/Group.d.ts.map +1 -1
- package/dist/components/Group.js.map +1 -1
- package/dist/components/Menu.d.ts.map +1 -1
- package/dist/components/Menu.js.map +1 -1
- package/dist/components/Popover.js.map +1 -1
- package/dist/components/Progress.js.map +1 -1
- package/dist/components/ScrollArea.js.map +1 -1
- package/dist/components/Separator.js.map +1 -1
- package/dist/components/Sidebar.js.map +1 -1
- package/dist/components/Spinner.js.map +1 -1
- package/dist/components/Table.js.map +1 -1
- package/dist/components/Text.js.map +1 -1
- package/dist/components/Toast.d.ts.map +1 -1
- package/dist/components/Toast.js.map +1 -1
- package/dist/components/Tooltip.js.map +1 -1
- package/dist/components/XStack.d.ts.map +1 -1
- package/dist/components/XStack.js.map +1 -1
- package/dist/components/YStack.d.ts.map +1 -1
- package/dist/components/YStack.js.map +1 -1
- package/dist/components/_internal/dialog-styles.d.ts.map +1 -1
- package/dist/components/_internal/dialog-styles.js.map +1 -1
- package/dist/components/_internal/layout.js.map +1 -1
- package/dist/components/form/Checkbox.d.ts.map +1 -1
- package/dist/components/form/Checkbox.js.map +1 -1
- package/dist/components/form/ColorPickerPrimitive.d.ts +5 -5
- package/dist/components/form/ColorPickerPrimitive.d.ts.map +1 -1
- package/dist/components/form/ColorPickerPrimitive.js +6 -6
- package/dist/components/form/ColorPickerPrimitive.js.map +1 -1
- package/dist/components/form/Combobox.d.ts.map +1 -1
- package/dist/components/form/Combobox.js.map +1 -1
- package/dist/components/form/Form.d.ts +1 -1
- package/dist/components/form/Form.d.ts.map +1 -1
- package/dist/components/form/Form.js +1 -1
- package/dist/components/form/Form.js.map +1 -1
- package/dist/components/form/FormCheckbox.d.ts +2 -2
- package/dist/components/form/FormCheckbox.d.ts.map +1 -1
- package/dist/components/form/FormCheckbox.js +1 -1
- package/dist/components/form/FormCheckbox.js.map +1 -1
- package/dist/components/form/FormDescription.js.map +1 -1
- package/dist/components/form/FormError.js.map +1 -1
- package/dist/components/form/FormField.d.ts.map +1 -1
- package/dist/components/form/FormField.js.map +1 -1
- package/dist/components/form/FormInput.d.ts +1 -1
- package/dist/components/form/FormInput.d.ts.map +1 -1
- package/dist/components/form/FormInput.js +2 -2
- package/dist/components/form/FormInput.js.map +1 -1
- package/dist/components/form/FormInputGroup.d.ts +1 -1
- package/dist/components/form/FormInputGroup.d.ts.map +1 -1
- package/dist/components/form/FormInputGroup.js +1 -1
- package/dist/components/form/FormInputGroup.js.map +1 -1
- package/dist/components/form/FormLabel.js.map +1 -1
- package/dist/components/form/FormRadioGroup.d.ts.map +1 -1
- package/dist/components/form/FormRoot.js.map +1 -1
- package/dist/components/form/FormSwitch.d.ts +2 -2
- package/dist/components/form/FormSwitch.d.ts.map +1 -1
- package/dist/components/form/FormSwitch.js +1 -1
- package/dist/components/form/FormSwitch.js.map +1 -1
- package/dist/components/form/FormTextarea.d.ts +1 -1
- package/dist/components/form/FormTextarea.d.ts.map +1 -1
- package/dist/components/form/FormTextarea.js +1 -1
- package/dist/components/form/FormTextarea.js.map +1 -1
- package/dist/components/form/Input.d.ts.map +1 -1
- package/dist/components/form/Input.js.map +1 -1
- package/dist/components/form/InputGroup.d.ts.map +1 -1
- package/dist/components/form/InputGroup.js.map +1 -1
- package/dist/components/form/RadioGroup.d.ts.map +1 -1
- package/dist/components/form/RadioGroup.js.map +1 -1
- package/dist/components/form/Select.d.ts.map +1 -1
- package/dist/components/form/Select.js.map +1 -1
- package/dist/components/form/Slider.d.ts.map +1 -1
- package/dist/components/form/Slider.js.map +1 -1
- package/dist/components/form/Switch.d.ts.map +1 -1
- package/dist/components/form/Switch.js.map +1 -1
- package/dist/components/form/Textarea.d.ts.map +1 -1
- package/dist/components/form/Textarea.js.map +1 -1
- package/dist/entries/theme.d.ts +2 -2
- package/dist/entries/theme.d.ts.map +1 -1
- package/dist/entries/theme.js +1 -1
- package/dist/entries/theme.js.map +1 -1
- package/dist/hooks/use-as-ref.js +1 -1
- package/dist/hooks/use-as-ref.js.map +1 -1
- package/package.json +4 -2
- package/src/base.css +7 -25
- package/src/components/Alert.tsx +1 -2
- package/src/components/AlertDialog.tsx +1 -2
- package/src/components/Avatar.tsx +1 -1
- package/src/components/Badge.tsx +1 -1
- package/src/components/Button.tsx +1 -2
- package/src/components/Card.tsx +1 -2
- package/src/components/Dialog.tsx +1 -2
- package/src/components/Flex.tsx +1 -2
- package/src/components/Grid.tsx +1 -2
- package/src/components/Group.tsx +1 -2
- package/src/components/Menu.tsx +1 -2
- package/src/components/Popover.tsx +1 -1
- package/src/components/Progress.tsx +1 -1
- package/src/components/ScrollArea.tsx +1 -1
- package/src/components/Separator.tsx +1 -1
- package/src/components/Sidebar.tsx +1 -1
- package/src/components/Spinner.tsx +1 -1
- package/src/components/Table.tsx +1 -1
- package/src/components/Text.tsx +1 -1
- package/src/components/Toast.tsx +1 -2
- package/src/components/Tooltip.tsx +1 -1
- package/src/components/XStack.tsx +1 -2
- package/src/components/YStack.tsx +1 -2
- package/src/components/_internal/Story.tsx +1 -2
- package/src/components/_internal/dialog-styles.ts +1 -2
- package/src/components/_internal/layout.ts +1 -1
- package/src/components/form/Checkbox.tsx +1 -1
- package/src/components/form/ColorPickerPrimitive.tsx +18 -19
- package/src/components/form/Combobox.tsx +1 -2
- package/src/components/form/Form.tsx +1 -2
- package/src/components/form/FormCheckbox.tsx +1 -2
- package/src/components/form/FormDescription.tsx +1 -1
- package/src/components/form/FormError.tsx +1 -1
- package/src/components/form/FormField.tsx +1 -2
- package/src/components/form/FormInput.tsx +2 -3
- package/src/components/form/FormInputGroup.tsx +1 -2
- package/src/components/form/FormLabel.tsx +1 -1
- package/src/components/form/FormRoot.tsx +1 -1
- package/src/components/form/FormSwitch.tsx +1 -2
- package/src/components/form/FormTextarea.tsx +1 -2
- package/src/components/form/Input.tsx +1 -1
- package/src/components/form/InputGroup.tsx +1 -2
- package/src/components/form/RadioGroup.tsx +1 -1
- package/src/components/form/Select.stories.tsx +1 -2
- package/src/components/form/Select.tsx +1 -1
- package/src/components/form/Slider.tsx +1 -1
- package/src/components/form/Switch.tsx +1 -1
- package/src/components/form/Textarea.tsx +1 -1
- package/src/entries/theme.ts +2 -2
- package/src/hooks/use-as-ref.ts +1 -1
- package/dist/lib/theme.d.ts +0 -40
- package/dist/lib/theme.d.ts.map +0 -1
- package/dist/lib/theme.js +0 -82
- package/dist/lib/theme.js.map +0 -1
- package/src/lib/theme.test.ts +0 -64
- 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
|
|
|
@@ -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 '
|
|
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 '
|
|
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 '
|
|
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'>;
|
|
@@ -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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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. */
|
package/src/entries/theme.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { createTheme, themeToCss } from '
|
|
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 '
|
|
8
|
+
} from '@pickle-ui/tokens/theme';
|
package/src/hooks/use-as-ref.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
import { useIsomorphicLayoutEffect } from '
|
|
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);
|
package/dist/lib/theme.d.ts
DELETED
|
@@ -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
|
package/dist/lib/theme.d.ts.map
DELETED
|
@@ -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
|
package/dist/lib/theme.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/lib/theme.ts"],"names":[],"mappings":"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"}
|
package/src/lib/theme.test.ts
DELETED
|
@@ -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
|
-
};
|