@kalink-ui/seedly 0.34.3 → 0.35.0

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 (144) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +34 -0
  3. package/docs/component-theming.md +295 -0
  4. package/docs/theming-strategy.md +69 -0
  5. package/docs/tone-system.md +139 -0
  6. package/docs/value-and-scope.md +65 -0
  7. package/package.json +4 -3
  8. package/src/components/alert-dialog/alert-dialog-action.tsx +0 -2
  9. package/src/components/alert-dialog/alert-dialog-cancel.tsx +0 -1
  10. package/src/components/alert-dialog/alert-dialog-content.css.ts +1 -1
  11. package/src/components/alert-dialog/alert-dialog-content.tsx +13 -6
  12. package/src/components/alert-dialog/alert-dialog-footer.css.ts +3 -3
  13. package/src/components/alert-dialog/alert-dialog-footer.tsx +2 -2
  14. package/src/components/alert-dialog/alert-dialog-header.tsx +4 -4
  15. package/src/components/alert-dialog/index.ts +9 -0
  16. package/src/components/box/box.css.ts +137 -39
  17. package/src/components/box/box.responsive.ts +2 -2
  18. package/src/components/box/box.tsx +2 -3
  19. package/src/components/box/index.ts +1 -1
  20. package/src/components/button/button.css.ts +142 -149
  21. package/src/components/button/button.responsive.ts +2 -2
  22. package/src/components/button/button.tsx +44 -31
  23. package/src/components/button/index.ts +2 -2
  24. package/src/components/button-icon/button-icon.css.ts +26 -62
  25. package/src/components/button-icon/button-icon.responsive.ts +2 -2
  26. package/src/components/button-icon/button-icon.tsx +5 -7
  27. package/src/components/card/card.css.ts +1 -5
  28. package/src/components/card/card.tsx +11 -11
  29. package/src/components/center/center.css.ts +61 -21
  30. package/src/components/center/center.responsive.ts +2 -2
  31. package/src/components/center/center.tsx +4 -6
  32. package/src/components/center/index.ts +1 -1
  33. package/src/components/cluster/cluster.css.ts +37 -99
  34. package/src/components/cluster/cluster.responsive.ts +13 -2
  35. package/src/components/cluster/cluster.tsx +6 -5
  36. package/src/components/cluster/index.ts +5 -1
  37. package/src/components/command/command-empty.tsx +36 -4
  38. package/src/components/command/command-group.css.ts +23 -7
  39. package/src/components/command/command-group.tsx +30 -6
  40. package/src/components/command/command-input.css.ts +2 -2
  41. package/src/components/command/command-item.tsx +26 -2
  42. package/src/components/command/command-list.css.ts +2 -2
  43. package/src/components/command/command-list.responsive.ts +2 -2
  44. package/src/components/command/command-list.tsx +1 -2
  45. package/src/components/command/command-separator.tsx +7 -5
  46. package/src/components/cover/cover.css.ts +29 -8
  47. package/src/components/cover/cover.tsx +13 -13
  48. package/src/components/cover/index.ts +2 -2
  49. package/src/components/divider/divider.css.ts +9 -4
  50. package/src/components/form-field/form-field-context.ts +3 -0
  51. package/src/components/form-field/form-field-item.tsx +3 -3
  52. package/src/components/form-field/form-field-message.tsx +34 -3
  53. package/src/components/form-field/form-field.css.ts +78 -16
  54. package/src/components/form-field/form-field.tsx +5 -0
  55. package/src/components/form-field/index.ts +1 -1
  56. package/src/components/frame/frame.css.ts +96 -59
  57. package/src/components/frame/frame.responsive.ts +9 -0
  58. package/src/components/frame/frame.tsx +11 -5
  59. package/src/components/frame/index.ts +1 -1
  60. package/src/components/grid/grid-child.tsx +14 -10
  61. package/src/components/grid/grid.css.ts +56 -148
  62. package/src/components/grid/grid.tsx +40 -18
  63. package/src/components/grid/index.ts +4 -3
  64. package/src/components/heading/heading.css.ts +4 -4
  65. package/src/components/heading/heading.responsive.ts +6 -6
  66. package/src/components/heading/heading.tsx +3 -4
  67. package/src/components/heading/index.ts +1 -1
  68. package/src/components/input/index.ts +4 -1
  69. package/src/components/input/input-wrapper.tsx +20 -8
  70. package/src/components/input/input.css.ts +121 -93
  71. package/src/components/input/input.responsive.ts +9 -0
  72. package/src/components/input/input.tsx +7 -1
  73. package/src/components/label/label.css.ts +2 -2
  74. package/src/components/label/label.tsx +23 -3
  75. package/src/components/layout-maps.ts +120 -0
  76. package/src/components/loader/index.ts +2 -1
  77. package/src/components/loader/loader.css.ts +91 -54
  78. package/src/components/loader/moon-loader.responsive.ts +2 -2
  79. package/src/components/loader/moon-loader.tsx +4 -5
  80. package/src/components/loader-overlay/loader-overlay.css.ts +3 -3
  81. package/src/components/loader-overlay/loader-overlay.tsx +5 -2
  82. package/src/components/menu/index.ts +2 -2
  83. package/src/components/menu/menu-item.css.ts +102 -46
  84. package/src/components/menu/menu-separator.css.ts +27 -15
  85. package/src/components/menu/menu-separator.responsive.ts +2 -2
  86. package/src/components/overlay/overlay.css.ts +1 -1
  87. package/src/components/popover/index.ts +1 -1
  88. package/src/components/popover/popover-content.css.ts +69 -52
  89. package/src/components/popover/popover-content.tsx +22 -6
  90. package/src/components/scroll-area/scroll-area.css.ts +3 -3
  91. package/src/components/scroll-area/scroll-bar.tsx +2 -2
  92. package/src/components/select/index.ts +4 -5
  93. package/src/components/select/select-content.css.ts +1 -1
  94. package/src/components/select/select-content.tsx +2 -2
  95. package/src/components/select/select-item.tsx +11 -3
  96. package/src/components/select/select-trigger.css.ts +14 -18
  97. package/src/components/select/select-trigger.tsx +18 -8
  98. package/src/components/select/select.tsx +10 -6
  99. package/src/components/sheet/index.ts +12 -0
  100. package/src/components/sheet/sheet-content.css.ts +2 -2
  101. package/src/components/sheet/sheet-content.tsx +25 -7
  102. package/src/components/sheet/sheet-description.tsx +5 -7
  103. package/src/components/sheet/sheet-footer.tsx +3 -1
  104. package/src/components/sheet/sheet-header.css.ts +1 -1
  105. package/src/components/sheet/sheet-header.tsx +3 -3
  106. package/src/components/sheet/sheet-overlay.tsx +3 -4
  107. package/src/components/sheet/sheet-title.tsx +1 -1
  108. package/src/components/sidebar/index.ts +5 -1
  109. package/src/components/sidebar/sidebar.css.ts +35 -9
  110. package/src/components/sidebar/sidebar.tsx +7 -10
  111. package/src/components/skeleton/skeleton.css.ts +23 -14
  112. package/src/components/skeleton/skeleton.tsx +26 -7
  113. package/src/components/stack/index.ts +1 -1
  114. package/src/components/stack/stack.css.ts +18 -46
  115. package/src/components/stack/stack.tsx +1 -2
  116. package/src/components/switcher/index.ts +5 -1
  117. package/src/components/switcher/switcher.css.ts +105 -72
  118. package/src/components/switcher/switcher.responsive.ts +2 -2
  119. package/src/components/switcher/switcher.tsx +5 -5
  120. package/src/components/text/text.css.ts +93 -105
  121. package/src/components/text/text.responsive.ts +3 -63
  122. package/src/components/text/text.tsx +16 -28
  123. package/src/components/text-field/index.ts +1 -2
  124. package/src/components/text-field/text-field.tsx +5 -7
  125. package/src/components/textarea/textarea-input.tsx +30 -3
  126. package/src/components/textarea/textarea.css.ts +12 -7
  127. package/src/components/textarea/textarea.tsx +9 -3
  128. package/src/components/visually-hidden/visually-hidden.css.ts +16 -10
  129. package/src/styles/define-responsive-properties.ts +5 -1
  130. package/src/styles/index.ts +12 -0
  131. package/src/styles/responsive.ts +72 -43
  132. package/src/styles/system-contract.css.ts +22 -3
  133. package/src/styles/theme/sprout-ref.css.ts +107 -0
  134. package/src/styles/theme/sprout.css.ts +259 -0
  135. package/src/styles/tone.ts +69 -0
  136. package/src/styles/typography.responsive.css.ts +35 -0
  137. package/src/styles/typography.responsive.ts +104 -0
  138. package/src/utils/arg-types/index.ts +1 -0
  139. package/src/utils/arg-types/responsive-arg.ts +28 -0
  140. package/src/utils/index.ts +1 -0
  141. package/src/components/command/command-item.css.ts +0 -32
  142. package/src/components/select/select.css.ts +0 -3
  143. package/src/components/sheet/sheet-body.css.ts +0 -68
  144. package/src/components/text-field/text-field.css.ts +0 -3
@@ -1,11 +1,11 @@
1
- import { globalStyle, style } from '@vanilla-extract/css';
1
+ import { globalStyle } from '@vanilla-extract/css';
2
+ import { recipe, type RecipeVariants } from '@vanilla-extract/recipes';
2
3
 
3
4
  import { sys, typography } from '../../styles';
4
5
  import { components } from '../../styles/layers.css';
5
6
 
6
- export const commandGroup = style([
7
- typography.label.small,
8
- {
7
+ export const commandGroupRecipe = recipe({
8
+ base: {
9
9
  '@layer': {
10
10
  [components]: {
11
11
  display: 'flex',
@@ -22,17 +22,33 @@ export const commandGroup = style([
22
22
  },
23
23
  },
24
24
  },
25
- ]);
26
25
 
27
- globalStyle(`${commandGroup} [cmdk-group-heading]`, {
26
+ variants: {
27
+ size: {
28
+ sm: typography.label.small,
29
+ md: typography.label.medium,
30
+ lg: typography.label.large,
31
+ },
32
+ },
33
+
34
+ defaultVariants: {
35
+ size: 'sm',
36
+ },
37
+ });
38
+
39
+ globalStyle(`${commandGroupRecipe.classNames.base} [cmdk-group-heading]`, {
28
40
  '@layer': {
29
41
  [components]: {
30
42
  position: 'relative',
31
43
 
32
- color: `color-mix(in srgb, ${sys.color.foreground} calc(${sys.state.muted.light} * 100%), transparent)`,
44
+ color: `color-mix(in srgb, ${sys.surface.foreground} calc(${sys.state.muted.text} * 100%), transparent)`,
33
45
 
34
46
  cursor: 'default',
35
47
  userSelect: 'none',
36
48
  },
37
49
  },
38
50
  });
51
+
52
+ export type CommandGroupVariants = NonNullable<
53
+ RecipeVariants<typeof commandGroupRecipe>
54
+ >;
@@ -4,15 +4,39 @@ import { clsx } from 'clsx';
4
4
  import { Command as CommandPrimitive } from 'cmdk';
5
5
  import { ComponentPropsWithRef } from 'react';
6
6
 
7
- import { commandGroup } from './command-group.css';
7
+ import {
8
+ buildTypographyOverrides,
9
+ getResponsiveBase,
10
+ mapResponsiveSizeToTypography,
11
+ type Responsive,
12
+ } from '../../styles';
13
+
14
+ import {
15
+ commandGroupRecipe,
16
+ type CommandGroupVariants,
17
+ } from './command-group.css';
18
+
19
+ export type CommandGroupProps = ComponentPropsWithRef<
20
+ typeof CommandPrimitive.Group
21
+ > & {
22
+ size?: Responsive<NonNullable<CommandGroupVariants['size']>>;
23
+ };
24
+
25
+ export function CommandGroup({ className, size, ...props }: CommandGroupProps) {
26
+ const baseSize = getResponsiveBase(size) ?? 'sm';
27
+ const typographySize = mapResponsiveSizeToTypography(size);
28
+ const typographyOverrides = buildTypographyOverrides({
29
+ variant: 'label',
30
+ size: typographySize,
31
+ });
8
32
 
9
- export function CommandGroup({
10
- className,
11
- ...props
12
- }: ComponentPropsWithRef<typeof CommandPrimitive.Group>) {
13
33
  return (
14
34
  <CommandPrimitive.Group
15
- className={clsx(commandGroup, className)}
35
+ className={clsx(
36
+ commandGroupRecipe({ size: baseSize }),
37
+ typographyOverrides,
38
+ className,
39
+ )}
16
40
  {...props}
17
41
  />
18
42
  );
@@ -23,13 +23,13 @@ export const commandInputWrapper = style({
23
23
  boxShadow: 'unset',
24
24
 
25
25
  vars: {
26
- [outlineColor]: sys.color.foreground,
26
+ [outlineColor]: sys.surface.foreground,
27
27
  },
28
28
  },
29
29
  },
30
30
 
31
31
  vars: {
32
- [outlineColor]: sys.color.foreground,
32
+ [outlineColor]: sys.surface.foreground,
33
33
  },
34
34
  },
35
35
  },
@@ -4,14 +4,25 @@ import { clsx } from 'clsx';
4
4
  import { Command as CommandPrimitive } from 'cmdk';
5
5
  import { ComponentPropsWithRef, ComponentType } from 'react';
6
6
 
7
+ import {
8
+ buildTypographyOverrides,
9
+ getResponsiveBase,
10
+ mapResponsiveSizeToTypography,
11
+ type Responsive,
12
+ type Tone,
13
+ } from '../../styles';
7
14
  import { Cluster } from '../cluster';
8
- import { menuItem, menuItemIcon } from '../menu/menu-item.css';
15
+ import { menuItemRecipe, menuItemIcon } from '../menu/menu-item.css';
16
+
17
+ import type { MenuItemVariants } from '../menu/menu-item.css';
9
18
 
10
19
  export type CommandItemProps = ComponentPropsWithRef<
11
20
  typeof CommandPrimitive.Item
12
21
  > & {
13
22
  inset?: boolean;
14
23
  icon?: ComponentType<{ className?: string }>;
24
+ tone?: Tone;
25
+ size?: Responsive<NonNullable<MenuItemVariants['size']>>;
15
26
  };
16
27
 
17
28
  export function CommandItem({
@@ -19,11 +30,24 @@ export function CommandItem({
19
30
  inset,
20
31
  icon: IconComp,
21
32
  children,
33
+ tone,
34
+ size,
22
35
  ...props
23
36
  }: CommandItemProps) {
37
+ const baseSize = getResponsiveBase(size) ?? 'md';
38
+ const typographySize = mapResponsiveSizeToTypography(size);
39
+ const typographyOverrides = buildTypographyOverrides({
40
+ variant: 'body',
41
+ size: typographySize,
42
+ });
43
+
24
44
  return (
25
45
  <CommandPrimitive.Item
26
- className={clsx(menuItem({ inset }), className)}
46
+ className={clsx(
47
+ menuItemRecipe({ inset, tone, size: baseSize }),
48
+ typographyOverrides,
49
+ className,
50
+ )}
27
51
  {...props}
28
52
  >
29
53
  <Cluster spacing={2} align="center">
@@ -23,7 +23,7 @@ export const commandListSpacingStyles = mapContractVars(sys.spacing, (key) => ({
23
23
  },
24
24
  }));
25
25
 
26
- export const commandList = recipe({
26
+ export const commandListRecipe = recipe({
27
27
  base: {
28
28
  '@layer': {
29
29
  [components]: {
@@ -48,7 +48,7 @@ export const commandList = recipe({
48
48
  },
49
49
  });
50
50
 
51
- globalStyle(`${commandList.classNames.base} [cmdk-list-sizer]`, {
51
+ globalStyle(`${commandListRecipe.classNames.base} [cmdk-list-sizer]`, {
52
52
  '@layer': {
53
53
  [components]: {
54
54
  display: 'flex',
@@ -1,9 +1,9 @@
1
1
  import { defaultOrder, responsiveRecipe } from '../../styles/responsive';
2
2
 
3
- import { commandList, spacingAt } from './command-list.css';
3
+ import { commandListRecipe, spacingAt } from './command-list.css';
4
4
 
5
5
  export const commandListResponsive = responsiveRecipe({
6
- recipe: commandList,
6
+ recipe: commandListRecipe,
7
7
  at: { spacing: spacingAt },
8
8
  order: defaultOrder,
9
9
  });
@@ -1,6 +1,5 @@
1
1
  'use client';
2
2
 
3
- import { clsx } from 'clsx';
4
3
  import { Command as CommandPrimitive } from 'cmdk';
5
4
  import { ComponentPropsWithRef } from 'react';
6
5
 
@@ -19,7 +18,7 @@ export function CommandList({
19
18
  }: CommandListProps) {
20
19
  return (
21
20
  <CommandPrimitive.List
22
- className={clsx(commandListResponsive({ spacing }), className)}
21
+ className={commandListResponsive({ spacing }, className)}
23
22
  asChild
24
23
  {...props}
25
24
  />
@@ -6,7 +6,7 @@ import { ComponentPropsWithRef } from 'react';
6
6
 
7
7
  import {
8
8
  MenuSeparatorVariants,
9
- menuSeparator,
9
+ menuSeparatorRecipe,
10
10
  } from '../menu/menu-separator.css';
11
11
 
12
12
  export type CommandSeparatorProps = ComponentPropsWithRef<
@@ -20,9 +20,11 @@ export function CommandSeparator({
20
20
  ...props
21
21
  }: CommandSeparatorProps) {
22
22
  return (
23
- <CommandPrimitive.Separator
24
- className={clsx(menuSeparator({ offset }), className)}
25
- {...props}
26
- />
23
+ <CommandPrimitive.Separator {...props} asChild>
24
+ <div
25
+ className={clsx(menuSeparatorRecipe({ offset }), className)}
26
+ role="presentation"
27
+ />
28
+ </CommandPrimitive.Separator>
27
29
  );
28
30
  }
@@ -1,23 +1,43 @@
1
- import { createVar, globalStyle } from '@vanilla-extract/css';
1
+ import {
2
+ assignVars,
3
+ createThemeContract,
4
+ globalStyle,
5
+ } from '@vanilla-extract/css';
2
6
  import { recipe, type RecipeVariants } from '@vanilla-extract/recipes';
3
7
 
4
8
  import {
5
9
  createResponsiveVariants,
6
10
  defaultMedia,
7
- sys,
8
11
  mapContractVars,
12
+ sys,
9
13
  } from '../../styles';
10
14
  import { components } from '../../styles/layers.css';
11
15
 
12
- const spaceVar = createVar();
13
- export const minSizeVar = createVar();
16
+ export const coverVars = createThemeContract({
17
+ spacing: {
18
+ block: null,
19
+ },
20
+ layout: {
21
+ minBlockSize: null,
22
+ },
23
+ });
24
+
25
+ const coverSpacingDefaults = assignVars(coverVars.spacing, {
26
+ block: sys.spacing[0],
27
+ });
28
+
29
+ const coverLayoutDefaults = assignVars(coverVars.layout, {
30
+ minBlockSize: '100vh',
31
+ });
14
32
 
15
33
  // Shared variant style maps so we can reuse them for responsive overrides
16
34
  export const coverSpacingStyles = mapContractVars(sys.spacing, (key) => ({
17
35
  '@layer': {
18
36
  [components]: {
19
37
  vars: {
20
- [spaceVar]: sys.spacing[key],
38
+ ...assignVars(coverVars.spacing, {
39
+ block: sys.spacing[key],
40
+ }),
21
41
  },
22
42
  },
23
43
  },
@@ -30,10 +50,11 @@ export const coverRecipe = recipe({
30
50
  display: 'flex',
31
51
  flexDirection: 'column',
32
52
 
33
- minBlockSize: minSizeVar,
53
+ minBlockSize: coverVars.layout.minBlockSize,
34
54
 
35
55
  vars: {
36
- [minSizeVar]: '100vh',
56
+ ...coverSpacingDefaults,
57
+ ...coverLayoutDefaults,
37
58
  },
38
59
  },
39
60
  },
@@ -50,7 +71,7 @@ export const coverRecipe = recipe({
50
71
  globalStyle(`${coverRecipe.classNames.base} > *`, {
51
72
  '@layer': {
52
73
  [components]: {
53
- marginBlock: spaceVar,
74
+ marginBlock: coverVars.spacing.block,
54
75
  },
55
76
  },
56
77
  });
@@ -2,22 +2,22 @@
2
2
 
3
3
  import { PolymorphicComponentProps } from '@kalink-ui/dibbly';
4
4
  import { assignInlineVars } from '@vanilla-extract/dynamic';
5
- import { clsx } from 'clsx';
6
5
  import { ElementType } from 'react';
7
6
 
8
- import { CoverVariants, minSizeVar } from './cover.css';
7
+ import { coverVars, CoverVariants } from './cover.css';
9
8
  import { coverResponsive } from './cover.responsive';
10
9
 
11
10
  import type { Responsive } from '../../styles/responsive';
12
11
 
13
- type CoverProps<TUse extends ElementType> = PolymorphicComponentProps<TUse> &
14
- Omit<CoverVariants, 'spacing'> & {
15
- /**
16
- * The minimum height of the cover
17
- */
18
- minSize?: string;
19
- spacing?: Responsive<NonNullable<CoverVariants['spacing']>>;
20
- };
12
+ export type CoverProps<TUse extends ElementType> =
13
+ PolymorphicComponentProps<TUse> &
14
+ Omit<CoverVariants, 'spacing'> & {
15
+ /**
16
+ * The minimum height of the cover
17
+ */
18
+ minSize?: string;
19
+ spacing?: Responsive<NonNullable<CoverVariants['spacing']>>;
20
+ };
21
21
 
22
22
  /**
23
23
  * A custom element for covering a block-level element horizontally,
@@ -28,7 +28,7 @@ type CoverProps<TUse extends ElementType> = PolymorphicComponentProps<TUse> &
28
28
  *
29
29
  * https://every-layout.dev/layouts/cover
30
30
  */
31
- export function Cover<TUse extends ElementType>({
31
+ export function Cover<TUse extends ElementType = 'div'>({
32
32
  spacing,
33
33
  minSize,
34
34
  className,
@@ -38,9 +38,9 @@ export function Cover<TUse extends ElementType>({
38
38
 
39
39
  return (
40
40
  <Comp
41
- className={clsx(coverResponsive({ spacing }), className)}
41
+ className={coverResponsive({ spacing }, className)}
42
42
  style={assignInlineVars({
43
- ...(minSize && { [minSizeVar]: minSize }),
43
+ ...(minSize && { [coverVars.layout.minBlockSize]: minSize }),
44
44
  })}
45
45
  {...rest}
46
46
  />
@@ -1,2 +1,2 @@
1
- export { Cover } from './cover';
2
- export { coverRecipe, minSizeVar, type CoverVariants } from './cover.css';
1
+ export { Cover, type CoverProps } from './cover';
2
+ export { coverRecipe, coverVars, type CoverVariants } from './cover.css';
@@ -1,11 +1,16 @@
1
1
  import { style } from '@vanilla-extract/css';
2
2
 
3
3
  import { sys } from '../../styles';
4
+ import { components } from '../../styles/layers.css';
4
5
 
5
6
  export const divider = style({
6
- height: 1,
7
- width: '100%',
7
+ '@layer': {
8
+ [components]: {
9
+ height: 1,
10
+ width: '100%',
8
11
 
9
- border: 'none',
10
- backgroundColor: sys.color.foreground,
12
+ border: 'none',
13
+ backgroundColor: sys.surface.foreground,
14
+ },
15
+ },
11
16
  });
@@ -2,6 +2,8 @@
2
2
 
3
3
  import { createRequiredContext } from '@kalink-ui/dibbly';
4
4
 
5
+ import type { Tone } from '../../styles';
6
+
5
7
  interface FormFieldContext {
6
8
  name: string;
7
9
  registerMessageId: (id: string) => void;
@@ -12,6 +14,7 @@ interface FormFieldContext {
12
14
  label: string;
13
15
  disabled?: boolean;
14
16
  hideLabel: boolean;
17
+ tone?: Tone;
15
18
  }
16
19
 
17
20
  export const [useFormFieldContext, FormFieldContextProvider] =
@@ -6,7 +6,7 @@ import { ElementType, useId } from 'react';
6
6
 
7
7
  import { useFormFieldContext } from './form-field-context';
8
8
  import { FormFieldItemContextProvider } from './form-field-item-context';
9
- import { formFieldStyle } from './form-field.css';
9
+ import { formFieldRecipe } from './form-field.css';
10
10
 
11
11
  export type FormFieldItemProps<TUse extends ElementType = 'div'> =
12
12
  PolymorphicComponentProps<TUse>;
@@ -15,7 +15,7 @@ export function FormFieldItem<TUse extends ElementType = 'div'>(
15
15
  props: FormFieldItemProps<TUse>,
16
16
  ) {
17
17
  const id = useId();
18
- const { errors, disabled } = useFormFieldContext();
18
+ const { errors, disabled, tone } = useFormFieldContext();
19
19
 
20
20
  const { use: Comp = 'div', className, children, ...rest } = props;
21
21
 
@@ -23,7 +23,7 @@ export function FormFieldItem<TUse extends ElementType = 'div'>(
23
23
  <FormFieldItemContextProvider value={{ id }}>
24
24
  <Comp
25
25
  className={clsx(
26
- formFieldStyle({ error: !!errors, disabled }),
26
+ formFieldRecipe({ error: !!errors, disabled, tone }),
27
27
  className,
28
28
  )}
29
29
  {...rest}
@@ -1,13 +1,25 @@
1
1
  'use client';
2
2
 
3
+ import { clsx } from 'clsx';
3
4
  import { HTMLAttributes, useEffect } from 'react';
4
5
 
6
+ import {
7
+ buildTypographyOverrides,
8
+ getResponsiveBase,
9
+ mapResponsiveSizeToTypography,
10
+ type Responsive,
11
+ } from '../../styles';
12
+
5
13
  import { useFormFieldContext } from './form-field-context';
6
- import { formFieldMessageStyle } from './form-field.css';
14
+ import {
15
+ formFieldMessageRecipe,
16
+ type FormFieldMessageVariants,
17
+ } from './form-field.css';
7
18
 
8
19
  export type FormFieldMessageProps = HTMLAttributes<HTMLDivElement> & {
9
20
  id: string;
10
21
  error?: boolean;
22
+ size?: Responsive<NonNullable<FormFieldMessageVariants['size']>>;
11
23
  };
12
24
 
13
25
  export function FormFieldMessage({
@@ -15,9 +27,11 @@ export function FormFieldMessage({
15
27
  children,
16
28
  id,
17
29
  error,
30
+ size,
18
31
  ...props
19
32
  }: FormFieldMessageProps) {
20
- const { registerMessageId, unRegisterMessageId } = useFormFieldContext();
33
+ const { registerMessageId, unRegisterMessageId, tone, errors } =
34
+ useFormFieldContext();
21
35
 
22
36
  useEffect(() => {
23
37
  registerMessageId(id);
@@ -25,8 +39,25 @@ export function FormFieldMessage({
25
39
  return () => unRegisterMessageId(id);
26
40
  }, [id, registerMessageId, unRegisterMessageId]);
27
41
 
42
+ const messageColor = error || errors ? 'destructive' : (tone ?? 'neutral');
43
+ const baseSize = getResponsiveBase(size) ?? 'sm';
44
+ const typographySize = mapResponsiveSizeToTypography(size);
45
+ const typographyOverrides = buildTypographyOverrides({
46
+ variant: 'body',
47
+ size: typographySize,
48
+ });
49
+
28
50
  return (
29
- <div id={id} className={formFieldMessageStyle} {...props}>
51
+ <div
52
+ id={id}
53
+ className={clsx(
54
+ formFieldMessageRecipe({ size: baseSize }),
55
+ typographyOverrides,
56
+ className,
57
+ )}
58
+ data-tone={messageColor}
59
+ {...props}
60
+ >
30
61
  {children}
31
62
  </div>
32
63
  );
@@ -1,7 +1,12 @@
1
- import { assignVars, createThemeContract, style } from '@vanilla-extract/css';
1
+ import { assignVars, createThemeContract } from '@vanilla-extract/css';
2
2
  import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
3
3
 
4
- import { sys, typography } from '../../styles';
4
+ import {
5
+ createToneAssignments,
6
+ createToneStyles,
7
+ sys,
8
+ typography,
9
+ } from '../../styles';
5
10
  import { components } from '../../styles/layers.css';
6
11
 
7
12
  export const formFieldVars = createThemeContract({
@@ -16,7 +21,20 @@ export const formFieldVars = createThemeContract({
16
21
  },
17
22
  });
18
23
 
19
- export const formFieldStyle = recipe({
24
+ const formFieldToneVars = createThemeContract({
25
+ base: null,
26
+ onBase: null,
27
+ });
28
+
29
+ const formFieldToneAssignments = createToneAssignments(formFieldToneVars);
30
+ const formFieldToneDefaults = formFieldToneAssignments.neutral;
31
+
32
+ const formFieldToneStyles = createToneStyles(formFieldToneVars, ({ base }) => ({
33
+ [formFieldVars.color.foreground]: base,
34
+ [formFieldVars.color.outline]: base,
35
+ }));
36
+
37
+ export const formFieldRecipe = recipe({
20
38
  base: {
21
39
  '@layer': {
22
40
  [components]: {
@@ -31,11 +49,11 @@ export const formFieldStyle = recipe({
31
49
 
32
50
  vars: {
33
51
  ...assignVars(formFieldVars.color, {
34
- foreground: sys.color.foreground,
35
- background: sys.color.background,
36
- outline: sys.color.foreground,
52
+ foreground: sys.surface.foreground,
53
+ background: sys.surface.background,
54
+ outline: sys.surface.foreground,
37
55
  }),
38
-
56
+ ...formFieldToneDefaults,
39
57
  ...assignVars(formFieldVars.spacing, {
40
58
  vertical: sys.spacing[2],
41
59
  }),
@@ -47,13 +65,17 @@ export const formFieldStyle = recipe({
47
65
 
48
66
  vars: {
49
67
  [formFieldVars.color.foreground]:
50
- `color-mix(in srgb, ${sys.color.foreground} calc(${sys.state.muted.light} * 75%), transparent)`,
68
+ `color-mix(in srgb, ${sys.surface.foreground} calc(${sys.state.disabled.text} * 100%), transparent)`,
69
+ [formFieldVars.color.outline]:
70
+ `color-mix(in srgb, ${sys.surface.foreground} calc(${sys.state.disabled.border} * 100%), transparent)`,
51
71
  },
52
72
  },
53
73
 
54
74
  '&[aria-invalid], &:has([aria-invalid])': {
55
75
  vars: {
56
- [formFieldVars.color.foreground]: '#d80000',
76
+ [formFieldVars.color.foreground]: sys.tone.destructive,
77
+ [formFieldVars.color.outline]: sys.tone.destructive,
78
+ ...formFieldToneAssignments.destructive,
57
79
  },
58
80
  },
59
81
  },
@@ -62,12 +84,16 @@ export const formFieldStyle = recipe({
62
84
  },
63
85
 
64
86
  variants: {
87
+ tone: formFieldToneStyles,
88
+
65
89
  error: {
66
90
  true: {
67
91
  '@layer': {
68
92
  [components]: {
69
93
  vars: {
70
- [formFieldVars.color.foreground]: '#d80000',
94
+ [formFieldVars.color.foreground]: sys.tone.destructive,
95
+ [formFieldVars.color.outline]: sys.tone.destructive,
96
+ ...formFieldToneAssignments.destructive,
71
97
  },
72
98
  },
73
99
  },
@@ -82,26 +108,62 @@ export const formFieldStyle = recipe({
82
108
 
83
109
  vars: {
84
110
  [formFieldVars.color.foreground]:
85
- `color(from ${sys.color.foreground} srgb r g b / ${sys.state.muted.light})`,
111
+ `color-mix(in srgb, ${sys.surface.foreground} calc(${sys.state.disabled.text} * 100%), transparent)`,
112
+ [formFieldVars.color.outline]:
113
+ `color-mix(in srgb, ${sys.surface.foreground} calc(${sys.state.disabled.border} * 100%), transparent)`,
86
114
  },
87
115
  },
88
116
  },
89
117
  },
90
118
  },
91
119
  },
120
+
121
+ defaultVariants: {
122
+ tone: 'neutral',
123
+ },
92
124
  });
93
125
 
94
- export const formFieldMessageStyle = style([
95
- typography.body.small,
96
- {
126
+ export const formFieldMessageRecipe = recipe({
127
+ base: {
97
128
  '@layer': {
98
129
  [components]: {
99
130
  display: 'block',
131
+
132
+ selectors: {
133
+ '&[data-tone="neutral"]': {
134
+ color: sys.tone.neutral,
135
+ },
136
+ '&[data-tone="primary"]': {
137
+ color: sys.tone.primary,
138
+ },
139
+ '&[data-tone="destructive"]': {
140
+ color: sys.tone.destructive,
141
+ },
142
+ '&[data-tone="success"]': {
143
+ color: sys.tone.success,
144
+ },
145
+ },
100
146
  },
101
147
  },
102
148
  },
103
- ]);
149
+
150
+ variants: {
151
+ size: {
152
+ sm: typography.body.small,
153
+ md: typography.body.medium,
154
+ lg: typography.body.large,
155
+ },
156
+ },
157
+
158
+ defaultVariants: {
159
+ size: 'sm',
160
+ },
161
+ });
104
162
 
105
163
  export type FormFieldVariants = NonNullable<
106
- RecipeVariants<typeof formFieldStyle>
164
+ RecipeVariants<typeof formFieldRecipe>
165
+ >;
166
+
167
+ export type FormFieldMessageVariants = NonNullable<
168
+ RecipeVariants<typeof formFieldMessageRecipe>
107
169
  >;