@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.
- package/dist/{chunk-BQAXPMOR.js → chunk-F7LHARS4.js} +2 -2
- package/dist/{chunk-BQAXPMOR.js.map → chunk-F7LHARS4.js.map} +1 -1
- package/dist/{chunk-ZNAYN5UV.js → chunk-M7J7WKJY.js} +178 -242
- package/dist/chunk-M7J7WKJY.js.map +1 -0
- package/dist/{chunk-XSX6TKJZ.cjs → chunk-QC44JPCA.cjs} +178 -242
- package/dist/chunk-QC44JPCA.cjs.map +1 -0
- package/dist/{chunk-MIBEMJNS.cjs → chunk-QP4EJI3G.cjs} +2 -2
- package/dist/{chunk-MIBEMJNS.cjs.map → chunk-QP4EJI3G.cjs.map} +1 -1
- package/dist/components/Badge.d.ts +1 -1
- package/dist/components/Badge.d.ts.map +1 -1
- package/dist/components/Checkbox.d.ts +2 -2
- package/dist/components/index.cjs +33 -33
- package/dist/components/index.js +1 -1
- package/dist/index.cjs +37 -37
- package/dist/index.js +2 -2
- package/dist/preset/colors/create-palette-bridge.d.ts +45 -0
- package/dist/preset/colors/create-palette-bridge.d.ts.map +1 -0
- package/dist/preset/colors/index.d.ts +13 -926
- package/dist/preset/colors/index.d.ts.map +1 -1
- package/dist/preset/colors/m3-error.d.ts +4 -306
- package/dist/preset/colors/m3-error.d.ts.map +1 -1
- package/dist/preset/colors/m3-primary.d.ts +1 -306
- package/dist/preset/colors/m3-primary.d.ts.map +1 -1
- package/dist/preset/colors/m3-secondary.d.ts +7 -0
- package/dist/preset/colors/m3-secondary.d.ts.map +1 -0
- package/dist/preset/colors/m3-tertiary.d.ts +7 -0
- package/dist/preset/colors/m3-tertiary.d.ts.map +1 -0
- package/dist/preset/index.cjs +2 -2
- package/dist/preset/index.d.ts.map +1 -1
- package/dist/preset/index.js +1 -1
- package/dist/preset/recipes/radio-group.d.ts.map +1 -1
- package/dist/preset/semantic-tokens.d.ts +4 -4
- package/dist/preset/shadows.d.ts +12 -26
- package/dist/preset/shadows.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/components/Badge.tsx +1 -1
- package/src/languages/__tests__/transform.test.ts +100 -0
- package/src/preset/__tests__/shadows.test.ts +83 -0
- package/src/preset/__tests__/token-resolution.test.ts +247 -0
- package/src/preset/colors/create-palette-bridge.ts +156 -0
- package/src/preset/colors/index.ts +4 -0
- package/src/preset/colors/m3-error.ts +12 -77
- package/src/preset/colors/m3-primary.ts +5 -90
- package/src/preset/colors/m3-secondary.ts +13 -0
- package/src/preset/colors/m3-tertiary.ts +13 -0
- package/src/preset/index.ts +1 -6
- package/src/preset/recipes/__tests__/recipe-shadows.test.ts +238 -0
- package/src/preset/recipes/button.ts +3 -3
- package/src/preset/recipes/radio-group.ts +12 -11
- package/src/preset/semantic-tokens.ts +6 -6
- package/src/preset/shadows.ts +12 -26
- package/src/stories/foundations/Colors.mdx +1 -1
- package/src/stories/foundations/Elevation.mdx +37 -8
- package/src/stories/foundations/Spacing.mdx +1 -1
- package/src/stories/foundations/Typography.mdx +1 -1
- package/dist/chunk-XSX6TKJZ.cjs.map +0 -1
- 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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
+
});
|
package/src/preset/index.ts
CHANGED
|
@@ -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
|
-
|
|
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: '
|
|
108
|
+
boxShadow: 'sm',
|
|
109
109
|
border: 'none',
|
|
110
110
|
_hover: {
|
|
111
111
|
bg: 'colorPalette.surface.bg',
|
|
112
|
-
boxShadow: '
|
|
112
|
+
boxShadow: 'md',
|
|
113
113
|
},
|
|
114
114
|
_active: {
|
|
115
|
-
boxShadow: '
|
|
115
|
+
boxShadow: 'xs',
|
|
116
116
|
},
|
|
117
117
|
},
|
|
118
118
|
},
|