@discourser/design-system 0.13.0 → 0.14.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 (57) hide show
  1. package/dist/{chunk-BQAXPMOR.js → chunk-F7LHARS4.js} +2 -2
  2. package/dist/{chunk-BQAXPMOR.js.map → chunk-F7LHARS4.js.map} +1 -1
  3. package/dist/{chunk-ZNAYN5UV.js → chunk-M7J7WKJY.js} +178 -242
  4. package/dist/chunk-M7J7WKJY.js.map +1 -0
  5. package/dist/{chunk-XSX6TKJZ.cjs → chunk-QC44JPCA.cjs} +178 -242
  6. package/dist/chunk-QC44JPCA.cjs.map +1 -0
  7. package/dist/{chunk-MIBEMJNS.cjs → chunk-QP4EJI3G.cjs} +2 -2
  8. package/dist/{chunk-MIBEMJNS.cjs.map → chunk-QP4EJI3G.cjs.map} +1 -1
  9. package/dist/components/Badge.d.ts +1 -1
  10. package/dist/components/Badge.d.ts.map +1 -1
  11. package/dist/components/Checkbox.d.ts +2 -2
  12. package/dist/components/index.cjs +33 -33
  13. package/dist/components/index.js +1 -1
  14. package/dist/index.cjs +37 -37
  15. package/dist/index.js +2 -2
  16. package/dist/preset/colors/create-palette-bridge.d.ts +45 -0
  17. package/dist/preset/colors/create-palette-bridge.d.ts.map +1 -0
  18. package/dist/preset/colors/index.d.ts +13 -926
  19. package/dist/preset/colors/index.d.ts.map +1 -1
  20. package/dist/preset/colors/m3-error.d.ts +4 -306
  21. package/dist/preset/colors/m3-error.d.ts.map +1 -1
  22. package/dist/preset/colors/m3-primary.d.ts +1 -306
  23. package/dist/preset/colors/m3-primary.d.ts.map +1 -1
  24. package/dist/preset/colors/m3-secondary.d.ts +7 -0
  25. package/dist/preset/colors/m3-secondary.d.ts.map +1 -0
  26. package/dist/preset/colors/m3-tertiary.d.ts +7 -0
  27. package/dist/preset/colors/m3-tertiary.d.ts.map +1 -0
  28. package/dist/preset/index.cjs +2 -2
  29. package/dist/preset/index.d.ts.map +1 -1
  30. package/dist/preset/index.js +1 -1
  31. package/dist/preset/recipes/radio-group.d.ts.map +1 -1
  32. package/dist/preset/semantic-tokens.d.ts +4 -4
  33. package/dist/preset/shadows.d.ts +12 -26
  34. package/dist/preset/shadows.d.ts.map +1 -1
  35. package/package.json +8 -7
  36. package/src/components/Badge.tsx +1 -1
  37. package/src/languages/__tests__/transform.test.ts +100 -0
  38. package/src/preset/__tests__/shadows.test.ts +83 -0
  39. package/src/preset/__tests__/token-resolution.test.ts +247 -0
  40. package/src/preset/colors/create-palette-bridge.ts +156 -0
  41. package/src/preset/colors/index.ts +4 -0
  42. package/src/preset/colors/m3-error.ts +12 -77
  43. package/src/preset/colors/m3-primary.ts +5 -90
  44. package/src/preset/colors/m3-secondary.ts +13 -0
  45. package/src/preset/colors/m3-tertiary.ts +13 -0
  46. package/src/preset/index.ts +1 -6
  47. package/src/preset/recipes/__tests__/recipe-shadows.test.ts +238 -0
  48. package/src/preset/recipes/button.ts +3 -3
  49. package/src/preset/recipes/radio-group.ts +12 -11
  50. package/src/preset/semantic-tokens.ts +6 -6
  51. package/src/preset/shadows.ts +12 -26
  52. package/src/stories/foundations/Colors.mdx +1 -1
  53. package/src/stories/foundations/Elevation.mdx +37 -8
  54. package/src/stories/foundations/Spacing.mdx +1 -1
  55. package/src/stories/foundations/Typography.mdx +1 -1
  56. package/dist/chunk-XSX6TKJZ.cjs.map +0 -1
  57. package/dist/chunk-ZNAYN5UV.js.map +0 -1
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Factory for creating Radix-scale bridges from M3 tonal palettes
3
+ *
4
+ * Converts Material 3 tonal palettes (0-100 scale) to Park UI Radix scale (1-12 + alpha + semantic variants)
5
+ *
6
+ * Mapping Strategy:
7
+ * - Radix 1-12 represents light → dark in light mode
8
+ * - M3 tonal palette 100 → 0 (white to black)
9
+ *
10
+ * Radix Scale Semantics:
11
+ * 1-2: App background
12
+ * 3-4: Subtle backgrounds
13
+ * 5-6: UI element backgrounds
14
+ * 7-8: Borders and separators
15
+ * 9: Solid backgrounds (primary action color)
16
+ * 10: Hovered solid backgrounds
17
+ * 11: Low-contrast text
18
+ * 12: High-contrast text
19
+ */
20
+
21
+ import { defineSemanticTokens } from '@pandacss/dev';
22
+ import type { TonalPalette } from '../../contracts/design-language.contract';
23
+
24
+ interface PaletteBridgeOptions {
25
+ /**
26
+ * Palette name for self-referencing semantic tokens (e.g., 'primary', 'error')
27
+ */
28
+ name: string;
29
+
30
+ /**
31
+ * M3 tonal palette object with keys 0-100
32
+ */
33
+ palette: TonalPalette;
34
+
35
+ /**
36
+ * Whether to include a DEFAULT key (used for error palette)
37
+ */
38
+ includeDefault?: boolean;
39
+
40
+ /**
41
+ * Override for solid.fg token (default uses white/palette[1] pattern)
42
+ * If true, uses 'white' for both light and dark modes (error pattern)
43
+ */
44
+ solidFgWhiteBoth?: boolean;
45
+ }
46
+
47
+ /**
48
+ * Creates a complete Radix-scale bridge from an M3 tonal palette
49
+ */
50
+ export function createPaletteBridge({
51
+ name,
52
+ palette: m3,
53
+ includeDefault = false,
54
+ solidFgWhiteBoth = false,
55
+ }: PaletteBridgeOptions) {
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ const tokens: any = {
58
+ // Base scale mapping M3 tonal to Radix (same for all chromatic colors)
59
+ '1': { value: { base: m3[99], _dark: m3[10] } },
60
+ '2': { value: { base: m3[95], _dark: m3[20] } },
61
+ '3': { value: { base: m3[90], _dark: m3[30] } },
62
+ '4': { value: { base: m3[80], _dark: m3[30] } },
63
+ '5': { value: { base: m3[70], _dark: m3[40] } },
64
+ '6': { value: { base: m3[60], _dark: m3[40] } },
65
+ '7': { value: { base: m3[50], _dark: m3[50] } },
66
+ '8': { value: { base: m3[40], _dark: m3[60] } },
67
+ '9': { value: { base: m3[40], _dark: m3[80] } }, // Primary action color
68
+ '10': { value: { base: m3[30], _dark: m3[70] } }, // Hover state
69
+ '11': { value: { base: m3[30], _dark: m3[90] } }, // Low-contrast text
70
+ '12': { value: { base: m3[10], _dark: m3[95] } }, // High-contrast text
71
+
72
+ // Alpha variants (for overlays/transparency)
73
+ // Increased opacity for better visibility in UI components
74
+ a1: { value: { base: `${m3[40]}08`, _dark: `${m3[80]}08` } },
75
+ a2: { value: { base: `${m3[40]}20`, _dark: `${m3[80]}20` } }, // ~13% for surface bg
76
+ a3: { value: { base: `${m3[40]}30`, _dark: `${m3[80]}30` } }, // ~19% for subtle bg
77
+ a4: { value: { base: `${m3[40]}40`, _dark: `${m3[80]}40` } }, // ~25% for hover states
78
+ a5: { value: { base: `${m3[40]}50`, _dark: `${m3[80]}50` } }, // ~31% for active states
79
+ a6: { value: { base: `${m3[40]}80`, _dark: `${m3[80]}80` } }, // ~50% for visible borders
80
+ a7: { value: { base: `${m3[40]}A0`, _dark: `${m3[80]}A0` } }, // ~63% for prominent borders
81
+ a8: { value: { base: `${m3[40]}B0`, _dark: `${m3[80]}B0` } }, // ~69%
82
+ a9: { value: { base: `${m3[40]}C0`, _dark: `${m3[80]}C0` } }, // ~75%
83
+ a10: { value: { base: `${m3[40]}D0`, _dark: `${m3[80]}D0` } }, // ~81%
84
+ a11: { value: { base: `${m3[40]}E0`, _dark: `${m3[80]}E0` } }, // ~88% for text colors
85
+ a12: { value: { base: `${m3[40]}F0`, _dark: `${m3[80]}F0` } }, // ~94% for high contrast
86
+
87
+ // Semantic variants (Park UI component styling)
88
+ solid: {
89
+ bg: {
90
+ DEFAULT: { value: { base: `{colors.${name}.9}`, _dark: `{colors.${name}.9}` } },
91
+ hover: { value: { base: `{colors.${name}.10}`, _dark: `{colors.${name}.10}` } },
92
+ },
93
+ fg: {
94
+ DEFAULT: solidFgWhiteBoth
95
+ ? { value: { base: 'white', _dark: 'white' } }
96
+ : { value: { base: '{colors.white}', _dark: `{colors.${name}.1}` } },
97
+ },
98
+ },
99
+ subtle: {
100
+ bg: {
101
+ DEFAULT: { value: { base: `{colors.${name}.a3}`, _dark: `{colors.${name}.a3}` } },
102
+ hover: { value: { base: `{colors.${name}.a4}`, _dark: `{colors.${name}.a4}` } },
103
+ active: { value: { base: `{colors.${name}.a5}`, _dark: `{colors.${name}.a5}` } },
104
+ },
105
+ fg: {
106
+ DEFAULT: { value: { base: `{colors.${name}.a11}`, _dark: `{colors.${name}.a11}` } },
107
+ },
108
+ },
109
+ surface: {
110
+ bg: {
111
+ DEFAULT: { value: { base: `{colors.${name}.a2}`, _dark: `{colors.${name}.a2}` } },
112
+ active: { value: { base: `{colors.${name}.a3}`, _dark: `{colors.${name}.a3}` } },
113
+ },
114
+ border: {
115
+ DEFAULT: { value: { base: `{colors.${name}.a6}`, _dark: `{colors.${name}.a6}` } },
116
+ hover: { value: { base: `{colors.${name}.a7}`, _dark: `{colors.${name}.a7}` } },
117
+ },
118
+ fg: {
119
+ DEFAULT: { value: { base: `{colors.${name}.a11}`, _dark: `{colors.${name}.a11}` } },
120
+ },
121
+ },
122
+ outline: {
123
+ bg: {
124
+ DEFAULT: { value: { base: 'transparent', _dark: 'transparent' } },
125
+ hover: { value: { base: `{colors.${name}.a2}`, _dark: `{colors.${name}.a2}` } },
126
+ active: { value: { base: `{colors.${name}.a3}`, _dark: `{colors.${name}.a3}` } },
127
+ },
128
+ border: {
129
+ DEFAULT: { value: { base: `{colors.${name}.a7}`, _dark: `{colors.${name}.a7}` } },
130
+ },
131
+ fg: {
132
+ DEFAULT: { value: { base: `{colors.${name}.a11}`, _dark: `{colors.${name}.a11}` } },
133
+ },
134
+ },
135
+ plain: {
136
+ bg: {
137
+ DEFAULT: { value: { base: 'transparent', _dark: 'transparent' } },
138
+ hover: { value: { base: `{colors.${name}.a3}`, _dark: `{colors.${name}.a3}` } },
139
+ active: { value: { base: `{colors.${name}.a4}`, _dark: `{colors.${name}.a4}` } },
140
+ },
141
+ fg: {
142
+ DEFAULT: { value: { base: `{colors.${name}.a11}`, _dark: `{colors.${name}.a11}` } },
143
+ },
144
+ },
145
+ };
146
+
147
+ // Add DEFAULT key if requested (for error palette)
148
+ if (includeDefault) {
149
+ return defineSemanticTokens.colors({
150
+ DEFAULT: { value: { base: m3[40], _dark: m3[80] } },
151
+ ...tokens,
152
+ });
153
+ }
154
+
155
+ return defineSemanticTokens.colors(tokens);
156
+ }
@@ -1,10 +1,14 @@
1
1
  import { primary } from './m3-primary';
2
+ import { secondary } from './m3-secondary';
3
+ import { tertiary } from './m3-tertiary';
2
4
  import { neutral } from './m3-neutral';
3
5
  import { error } from './m3-error';
4
6
 
5
7
  // Export all M3 colors mapped to Park UI structure
6
8
  export const colors = {
7
9
  primary,
10
+ secondary,
11
+ tertiary,
8
12
  neutral,
9
13
  error,
10
14
  // Alias gray to neutral (Park UI expects this)
@@ -1,78 +1,13 @@
1
- import { defineSemanticTokens } from '@pandacss/dev';
2
- import { material3Language } from '../../languages/material3.language';
3
-
4
- const m3 = material3Language.colors.error;
5
-
6
- export const error = defineSemanticTokens.colors({
7
- '1': { value: { base: m3[99], _dark: m3[10] } },
8
- '2': { value: { base: m3[95], _dark: m3[20] } },
9
- '3': { value: { base: m3[90], _dark: m3[30] } },
10
- '4': { value: { base: m3[80], _dark: m3[30] } },
11
- '5': { value: { base: m3[70], _dark: m3[40] } },
12
- '6': { value: { base: m3[60], _dark: m3[40] } },
13
- '7': { value: { base: m3[50], _dark: m3[50] } },
14
- '8': { value: { base: m3[40], _dark: m3[60] } },
15
- '9': { value: { base: m3[40], _dark: m3[80] } },
16
- '10': { value: { base: m3[30], _dark: m3[70] } },
17
- '11': { value: { base: m3[30], _dark: m3[90] } },
18
- '12': { value: { base: m3[10], _dark: m3[95] } },
1
+ /**
2
+ * Material 3 Error Color → Park UI Radix Scale
3
+ */
19
4
 
20
- // Alpha variants (increased opacity for better visibility)
21
- a1: { value: { base: `${m3[40]}08`, _dark: `${m3[80]}08` } },
22
- a2: { value: { base: `${m3[40]}20`, _dark: `${m3[80]}20` } }, // ~13% for surface bg
23
- a3: { value: { base: `${m3[40]}30`, _dark: `${m3[80]}30` } }, // ~19% for subtle bg
24
- a4: { value: { base: `${m3[40]}40`, _dark: `${m3[80]}40` } }, // ~25% for hover states
25
- a5: { value: { base: `${m3[40]}50`, _dark: `${m3[80]}50` } }, // ~31% for active states
26
- a6: { value: { base: `${m3[40]}80`, _dark: `${m3[80]}80` } }, // ~50% for visible borders
27
- a7: { value: { base: `${m3[40]}A0`, _dark: `${m3[80]}A0` } }, // ~63% for prominent borders
28
- a8: { value: { base: `${m3[40]}B0`, _dark: `${m3[80]}B0` } }, // ~69%
29
- a9: { value: { base: `${m3[40]}C0`, _dark: `${m3[80]}C0` } }, // ~75%
30
- a10: { value: { base: `${m3[40]}D0`, _dark: `${m3[80]}D0` } }, // ~81%
31
- a11: { value: { base: `${m3[40]}E0`, _dark: `${m3[80]}E0` } }, // ~88% for text colors
32
- a12: { value: { base: `${m3[40]}F0`, _dark: `${m3[80]}F0` } }, // ~94% for high contrast
33
-
34
- // Semantic variants
35
- solid: {
36
- bg: {
37
- DEFAULT: { value: { base: '{colors.error.9}', _dark: '{colors.error.9}' } },
38
- hover: { value: { base: '{colors.error.10}', _dark: '{colors.error.10}' } },
39
- },
40
- fg: { DEFAULT: { value: { base: 'white', _dark: 'white' } } },
41
- },
42
- subtle: {
43
- bg: {
44
- DEFAULT: { value: { base: '{colors.error.a3}', _dark: '{colors.error.a3}' } },
45
- hover: { value: { base: '{colors.error.a4}', _dark: '{colors.error.a4}' } },
46
- active: { value: { base: '{colors.error.a5}', _dark: '{colors.error.a5}' } },
47
- },
48
- fg: { DEFAULT: { value: { base: '{colors.error.a11}', _dark: '{colors.error.a11}' } } },
49
- },
50
- surface: {
51
- bg: {
52
- DEFAULT: { value: { base: '{colors.error.a2}', _dark: '{colors.error.a2}' } },
53
- active: { value: { base: '{colors.error.a3}', _dark: '{colors.error.a3}' } },
54
- },
55
- border: {
56
- DEFAULT: { value: { base: '{colors.error.a6}', _dark: '{colors.error.a6}' } },
57
- hover: { value: { base: '{colors.error.a7}', _dark: '{colors.error.a7}' } },
58
- },
59
- fg: { DEFAULT: { value: { base: '{colors.error.a11}', _dark: '{colors.error.a11}' } } },
60
- },
61
- outline: {
62
- bg: {
63
- DEFAULT: { value: { base: 'transparent', _dark: 'transparent' } },
64
- hover: { value: { base: '{colors.error.a2}', _dark: '{colors.error.a2}' } },
65
- active: { value: { base: '{colors.error.a3}', _dark: '{colors.error.a3}' } },
66
- },
67
- border: { DEFAULT: { value: { base: '{colors.error.a7}', _dark: '{colors.error.a7}' } } },
68
- fg: { DEFAULT: { value: { base: '{colors.error.a11}', _dark: '{colors.error.a11}' } } },
69
- },
70
- plain: {
71
- bg: {
72
- DEFAULT: { value: { base: 'transparent', _dark: 'transparent' } },
73
- hover: { value: { base: '{colors.error.a3}', _dark: '{colors.error.a3}' } },
74
- active: { value: { base: '{colors.error.a4}', _dark: '{colors.error.a4}' } },
75
- },
76
- fg: { DEFAULT: { value: { base: '{colors.error.a11}', _dark: '{colors.error.a11}' } } },
77
- },
78
- });
5
+ import { material3Language } from '../../languages/material3.language';
6
+ import { createPaletteBridge } from './create-palette-bridge';
7
+
8
+ export const error = createPaletteBridge({
9
+ name: 'error',
10
+ palette: material3Language.colors.error,
11
+ includeDefault: true,
12
+ solidFgWhiteBoth: true,
13
+ });
@@ -16,95 +16,10 @@
16
16
  * 12: High-contrast text
17
17
  */
18
18
 
19
- import { defineSemanticTokens } from '@pandacss/dev';
20
19
  import { material3Language } from '../../languages/material3.language';
20
+ import { createPaletteBridge } from './create-palette-bridge';
21
21
 
22
- const m3 = material3Language.colors.primary;
23
-
24
- export const primary = defineSemanticTokens.colors({
25
- // Base scale mapping M3 tonal to Radix
26
- '1': { value: { base: m3[99], _dark: m3[10] } },
27
- '2': { value: { base: m3[95], _dark: m3[20] } },
28
- '3': { value: { base: m3[90], _dark: m3[30] } },
29
- '4': { value: { base: m3[80], _dark: m3[30] } },
30
- '5': { value: { base: m3[70], _dark: m3[40] } },
31
- '6': { value: { base: m3[60], _dark: m3[40] } },
32
- '7': { value: { base: m3[50], _dark: m3[50] } },
33
- '8': { value: { base: m3[40], _dark: m3[60] } },
34
- '9': { value: { base: m3[40], _dark: m3[80] } }, // Primary action color
35
- '10': { value: { base: m3[30], _dark: m3[70] } }, // Hover state
36
- '11': { value: { base: m3[30], _dark: m3[90] } }, // Low-contrast text
37
- '12': { value: { base: m3[10], _dark: m3[95] } }, // High-contrast text
38
-
39
- // Alpha variants (for overlays/transparency)
40
- // Increased opacity for better visibility in UI components
41
- a1: { value: { base: `${m3[40]}08`, _dark: `${m3[80]}08` } },
42
- a2: { value: { base: `${m3[40]}20`, _dark: `${m3[80]}20` } }, // ~13% for surface bg
43
- a3: { value: { base: `${m3[40]}30`, _dark: `${m3[80]}30` } }, // ~19% for subtle bg
44
- a4: { value: { base: `${m3[40]}40`, _dark: `${m3[80]}40` } }, // ~25% for hover states
45
- a5: { value: { base: `${m3[40]}50`, _dark: `${m3[80]}50` } }, // ~31% for active states
46
- a6: { value: { base: `${m3[40]}80`, _dark: `${m3[80]}80` } }, // ~50% for visible borders
47
- a7: { value: { base: `${m3[40]}A0`, _dark: `${m3[80]}A0` } }, // ~63% for prominent borders
48
- a8: { value: { base: `${m3[40]}B0`, _dark: `${m3[80]}B0` } }, // ~69%
49
- a9: { value: { base: `${m3[40]}C0`, _dark: `${m3[80]}C0` } }, // ~75%
50
- a10: { value: { base: `${m3[40]}D0`, _dark: `${m3[80]}D0` } }, // ~81%
51
- a11: { value: { base: `${m3[40]}E0`, _dark: `${m3[80]}E0` } }, // ~88% for text colors
52
- a12: { value: { base: `${m3[40]}F0`, _dark: `${m3[80]}F0` } }, // ~94% for high contrast
53
-
54
- // Semantic variants (Park UI component styling)
55
- solid: {
56
- bg: {
57
- DEFAULT: { value: { base: '{colors.primary.9}', _dark: '{colors.primary.9}' } },
58
- hover: { value: { base: '{colors.primary.10}', _dark: '{colors.primary.10}' } },
59
- },
60
- fg: {
61
- DEFAULT: { value: { base: '{colors.white}', _dark: '{colors.primary.1}' } }
62
- },
63
- },
64
- subtle: {
65
- bg: {
66
- DEFAULT: { value: { base: '{colors.primary.a3}', _dark: '{colors.primary.a3}' } },
67
- hover: { value: { base: '{colors.primary.a4}', _dark: '{colors.primary.a4}' } },
68
- active: { value: { base: '{colors.primary.a5}', _dark: '{colors.primary.a5}' } },
69
- },
70
- fg: {
71
- DEFAULT: { value: { base: '{colors.primary.a11}', _dark: '{colors.primary.a11}' } }
72
- },
73
- },
74
- surface: {
75
- bg: {
76
- DEFAULT: { value: { base: '{colors.primary.a2}', _dark: '{colors.primary.a2}' } },
77
- active: { value: { base: '{colors.primary.a3}', _dark: '{colors.primary.a3}' } },
78
- },
79
- border: {
80
- DEFAULT: { value: { base: '{colors.primary.a6}', _dark: '{colors.primary.a6}' } },
81
- hover: { value: { base: '{colors.primary.a7}', _dark: '{colors.primary.a7}' } },
82
- },
83
- fg: {
84
- DEFAULT: { value: { base: '{colors.primary.a11}', _dark: '{colors.primary.a11}' } }
85
- },
86
- },
87
- outline: {
88
- bg: {
89
- DEFAULT: { value: { base: 'transparent', _dark: 'transparent' } },
90
- hover: { value: { base: '{colors.primary.a2}', _dark: '{colors.primary.a2}' } },
91
- active: { value: { base: '{colors.primary.a3}', _dark: '{colors.primary.a3}' } },
92
- },
93
- border: {
94
- DEFAULT: { value: { base: '{colors.primary.a7}', _dark: '{colors.primary.a7}' } }
95
- },
96
- fg: {
97
- DEFAULT: { value: { base: '{colors.primary.a11}', _dark: '{colors.primary.a11}' } }
98
- },
99
- },
100
- plain: {
101
- bg: {
102
- DEFAULT: { value: { base: 'transparent', _dark: 'transparent' } },
103
- hover: { value: { base: '{colors.primary.a3}', _dark: '{colors.primary.a3}' } },
104
- active: { value: { base: '{colors.primary.a4}', _dark: '{colors.primary.a4}' } },
105
- },
106
- fg: {
107
- DEFAULT: { value: { base: '{colors.primary.a11}', _dark: '{colors.primary.a11}' } }
108
- },
109
- },
110
- });
22
+ export const primary = createPaletteBridge({
23
+ name: 'primary',
24
+ palette: material3Language.colors.primary,
25
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Material 3 Secondary Color → Park UI Radix Scale
3
+ *
4
+ * Secondary palette: muted olive-green accent color
5
+ */
6
+
7
+ import { material3Language } from '../../languages/material3.language';
8
+ import { createPaletteBridge } from './create-palette-bridge';
9
+
10
+ export const secondary = createPaletteBridge({
11
+ name: 'secondary',
12
+ palette: material3Language.colors.secondary,
13
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Material 3 Tertiary Color → Park UI Radix Scale
3
+ *
4
+ * Tertiary palette: teal accent color
5
+ */
6
+
7
+ import { material3Language } from '../../languages/material3.language';
8
+ import { createPaletteBridge } from './create-palette-bridge';
9
+
10
+ export const tertiary = createPaletteBridge({
11
+ name: 'tertiary',
12
+ palette: material3Language.colors.tertiary,
13
+ });
@@ -108,12 +108,7 @@ export const discourserPandaPreset = definePreset({
108
108
  value: { base: '{colors.gray.1}', _dark: '{colors.gray.1}' },
109
109
  },
110
110
  border: {
111
- default: {
112
- value: { base: '{colors.gray.6}', _dark: '{colors.gray.6}' },
113
- },
114
- muted: {
115
- value: { base: '{colors.gray.4}', _dark: '{colors.gray.4}' },
116
- },
111
+ value: { base: '{colors.gray.6}', _dark: '{colors.gray.6}' },
117
112
  },
118
113
 
119
114
  // M3 semantic tokens (surface, onSurface, etc.)
@@ -0,0 +1,238 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { button } from '../button';
3
+ import { card } from '../card';
4
+ import { dialog } from '../dialog';
5
+ import { drawer } from '../drawer';
6
+ import { popover } from '../popover';
7
+ import { select } from '../select';
8
+ import { slider } from '../slider';
9
+ import { switchRecipe } from '../switch';
10
+ import { toast } from '../toast';
11
+ import { tooltip } from '../tooltip';
12
+ import { input } from '../input';
13
+ import { textarea } from '../textarea';
14
+ import { radioGroup } from '../radio-group';
15
+
16
+ /**
17
+ * Tests to verify component recipes use semantic tokens (xs-2xl)
18
+ * and NOT base tokens (level0-5)
19
+ */
20
+ describe('Recipe Shadow Token Usage', () => {
21
+ describe('Recipes must use semantic tokens only', () => {
22
+ it('button recipe should use semantic tokens (xs, sm, md)', () => {
23
+ // Panda CSS conditional styles (_hover, _active) are not in SystemStyleObject type
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ const elevated = button.variants?.variant?.elevated as any;
26
+
27
+ expect(elevated?.boxShadow).toBe('sm');
28
+ expect(elevated?._hover?.boxShadow).toBe('md');
29
+ expect(elevated?._active?.boxShadow).toBe('xs');
30
+ });
31
+
32
+ it('card recipe should use semantic tokens', () => {
33
+ const recipeConfig = card.variants?.variant;
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ const shadowVariant = Object.values(recipeConfig || {}).find((variant: any) => variant.boxShadow);
36
+
37
+ if (shadowVariant?.boxShadow) {
38
+ expect(shadowVariant.boxShadow).toMatch(/^(xs|sm|md|lg|xl|2xl)$/);
39
+ }
40
+ });
41
+
42
+ it('dialog recipe should use semantic tokens', () => {
43
+ // Slot styles are in base, not slots (slots is array of slot names)
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ const content = (dialog.base as any)?.content;
46
+
47
+ if (content?.boxShadow) {
48
+ expect(content.boxShadow).toMatch(/^(xs|sm|md|lg|xl|2xl)$/);
49
+ }
50
+ });
51
+
52
+ it('drawer recipe should use semantic tokens', () => {
53
+ // Slot styles are in base, not slots (slots is array of slot names)
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ const content = (drawer.base as any)?.content;
56
+
57
+ if (content?.boxShadow) {
58
+ expect(content.boxShadow).toMatch(/^(xs|sm|md|lg|xl|2xl)$/);
59
+ }
60
+ });
61
+
62
+ it('popover recipe should use semantic tokens', () => {
63
+ // Slot styles are in base, not slots (slots is array of slot names)
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ const content = (popover.base as any)?.content;
66
+
67
+ if (content?.boxShadow) {
68
+ expect(content.boxShadow).toMatch(/^(xs|sm|md|lg|xl|2xl)$/);
69
+ }
70
+ });
71
+
72
+ it('select recipe should use semantic tokens', () => {
73
+ // Slot styles are in base, not slots (slots is array of slot names)
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ const content = (select.base as any)?.content;
76
+
77
+ if (content?.boxShadow) {
78
+ expect(content.boxShadow).toMatch(/^(xs|sm|md|lg|xl|2xl)$/);
79
+ }
80
+ });
81
+
82
+ it('slider recipe should use semantic tokens', () => {
83
+ // Slot styles are in base, not slots (slots is array of slot names)
84
+ // Cast to any for _hover access (conditional styles not in type)
85
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
+ const thumb = (slider.base as any)?.thumb;
87
+
88
+ if (thumb?.boxShadow || thumb?._hover?.boxShadow) {
89
+ const shadowValue = thumb.boxShadow || thumb._hover?.boxShadow;
90
+ expect(shadowValue).toMatch(/^(xs|sm|md|lg|xl|2xl)$/);
91
+ }
92
+ });
93
+
94
+ it('switch recipe should use semantic tokens', () => {
95
+ // Slot styles are in base, not slots (slots is array of slot names)
96
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
+ const thumb = (switchRecipe.base as any)?.thumb;
98
+
99
+ if (thumb?.boxShadow) {
100
+ expect(thumb.boxShadow).toMatch(/^(xs|sm|md|lg|xl|2xl)$/);
101
+ }
102
+ });
103
+
104
+ it('toast recipe should use semantic tokens', () => {
105
+ // Slot styles are in base, not slots (slots is array of slot names)
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ const root = (toast.base as any)?.root;
108
+
109
+ if (root?.boxShadow) {
110
+ expect(root.boxShadow).toMatch(/^(xs|sm|md|lg|xl|2xl)$/);
111
+ }
112
+ });
113
+
114
+ it('tooltip recipe should use semantic tokens', () => {
115
+ // Slot styles are in base, not slots (slots is array of slot names)
116
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
+ const content = (tooltip.base as any)?.content;
118
+
119
+ if (content?.boxShadow) {
120
+ expect(content.boxShadow).toMatch(/^(xs|sm|md|lg|xl|2xl)$/);
121
+ }
122
+ });
123
+ });
124
+
125
+ describe('Recipes must NOT use base tokens', () => {
126
+ const recipes = {
127
+ button,
128
+ card,
129
+ dialog,
130
+ drawer,
131
+ popover,
132
+ select,
133
+ slider,
134
+ switchRecipe,
135
+ toast,
136
+ tooltip,
137
+ };
138
+
139
+ it('no recipe should reference level0-5 base tokens', () => {
140
+ Object.entries(recipes).forEach(([name, recipe]) => {
141
+ if (!recipe) {
142
+ console.warn(`Recipe ${name} is undefined`);
143
+ return;
144
+ }
145
+
146
+ const recipeString = JSON.stringify(recipe);
147
+
148
+ // Check for direct level token references
149
+ expect(recipeString).not.toMatch(/['"]level[0-5]['"]/);
150
+
151
+ // Specific check for boxShadow values
152
+ expect(recipeString).not.toMatch(/boxShadow['"]?\s*:\s*['"]level\d/);
153
+ });
154
+ });
155
+ });
156
+
157
+ describe('Custom shadows are allowed', () => {
158
+ it('utility components can use custom shadow values (not semantic or base)', () => {
159
+ // Some components use custom shadows for specific effects (borders, insets, etc.)
160
+ // These should not match semantic or base token patterns
161
+
162
+ const customShadowPattern = /^((?!xs|sm|md|lg|xl|2xl|level\d).)*$/;
163
+
164
+ // This test documents that custom shadows are intentional
165
+ // Examples: input field underlines, radio button rings, etc.
166
+ expect('0 1px 0 0 var(--shadow-color)').toMatch(customShadowPattern);
167
+ expect('inset 0 0 0 1px var(--shadow-color)').toMatch(customShadowPattern);
168
+ });
169
+ });
170
+
171
+ describe('STORY-003: Utility token usage (focus-underline, inset-border)', () => {
172
+ it('input flushed variant should use focus-underline token, not inline value', () => {
173
+ // Panda CSS conditional styles (_focus) are not in SystemStyleObject type
174
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
175
+ const flushedVariant = input.variants?.variant?.flushed as any;
176
+ const focusState = flushedVariant?._focus;
177
+
178
+ // TODO: STORY-003 will migrate these inline values to focus-underline utility token
179
+ expect(focusState?.boxShadow).toBe('0 1px 0 0 var(--shadow-color)');
180
+ });
181
+
182
+ it('textarea flushed variant should use focus-underline token, not inline value', () => {
183
+ // Panda CSS conditional styles (_focus) are not in SystemStyleObject type
184
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
+ const flushedVariant = textarea.variants?.variant?.flushed as any;
186
+ const focusState = flushedVariant?._focus;
187
+
188
+ // TODO: STORY-003 will migrate these inline values to focus-underline utility token
189
+ expect(focusState?.boxShadow).toBe('0 1px 0 0 var(--shadow-color)');
190
+ });
191
+
192
+ it('radioGroup control should use inset-border token, not inline value', () => {
193
+ // Radio group has the boxShadow in the variant, not base
194
+ const solidVariant = radioGroup.variants?.variant?.solid;
195
+ const itemControl = solidVariant?.itemControl;
196
+
197
+ // TODO: STORY-003 will migrate these inline values to inset-border utility token
198
+ expect(itemControl?.boxShadow).toBe('inset 0 0 0 1px var(--shadow-color)');
199
+ });
200
+
201
+ it('no recipe should have inline var(--shadow-color) strings', () => {
202
+ const allRecipes = {
203
+ button,
204
+ card,
205
+ dialog,
206
+ drawer,
207
+ popover,
208
+ select,
209
+ slider,
210
+ switchRecipe,
211
+ toast,
212
+ tooltip,
213
+ input,
214
+ textarea,
215
+ radioGroup,
216
+ };
217
+
218
+ // TODO: STORY-003 will migrate these inline values to focus-underline / inset-border utility tokens
219
+ // For now, verify they DO currently contain these inline values (current behavior)
220
+ const recipesWithInlineValues = ['input', 'textarea', 'radioGroup'];
221
+
222
+ Object.entries(allRecipes).forEach(([name, recipe]) => {
223
+ if (!recipe) {
224
+ console.warn(`Recipe ${name} is undefined`);
225
+ return;
226
+ }
227
+
228
+ const recipeString = JSON.stringify(recipe);
229
+
230
+ if (recipesWithInlineValues.includes(name)) {
231
+ // These recipes currently have inline var(--shadow-color) values
232
+ const hasShadowColor = recipeString.includes('var(--shadow-color)');
233
+ expect(hasShadowColor).toBe(true);
234
+ }
235
+ });
236
+ });
237
+ });
238
+ });
@@ -105,14 +105,14 @@ export const button = defineRecipe({
105
105
  elevated: {
106
106
  bg: 'colorPalette.surface.bg',
107
107
  color: 'colorPalette.solid.fg',
108
- boxShadow: 'level2',
108
+ boxShadow: 'sm',
109
109
  border: 'none',
110
110
  _hover: {
111
111
  bg: 'colorPalette.surface.bg',
112
- boxShadow: 'level3',
112
+ boxShadow: 'md',
113
113
  },
114
114
  _active: {
115
- boxShadow: 'level1',
115
+ boxShadow: 'xs',
116
116
  },
117
117
  },
118
118
  },