@discourser/design-system 0.20.1 → 0.21.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.
@@ -1,77 +1,51 @@
1
1
  /**
2
- * M3 Semantic Tokens - layered on top of Park UI
3
- *
4
- * These provide M3-style naming (surface, onSurface, etc.)
5
- * while Park UI components use their own naming (fg, canvas, etc.)
2
+ * Clean semantic color tokens no m3 prefix.
3
+ * These are the authoritative names going forward.
6
4
  */
7
- export declare const m3SemanticTokens: {
8
- surface: {
5
+ export declare const semanticColorTokens: {
6
+ primary: {
9
7
  DEFAULT: {
10
8
  value: {
11
9
  base: string;
12
10
  _dark: string;
13
11
  };
14
12
  };
15
- dim: {
13
+ container: {
16
14
  value: {
17
15
  base: string;
18
16
  _dark: string;
19
17
  };
20
18
  };
21
- bright: {
19
+ };
20
+ onPrimary: {
21
+ DEFAULT: {
22
22
  value: {
23
23
  base: string;
24
24
  _dark: string;
25
25
  };
26
26
  };
27
27
  container: {
28
- DEFAULT: {
29
- value: {
30
- base: string;
31
- _dark: string;
32
- };
33
- };
34
- low: {
35
- value: {
36
- base: string;
37
- _dark: string;
38
- };
39
- };
40
- lowest: {
41
- value: {
42
- base: string;
43
- _dark: string;
44
- };
45
- };
46
- high: {
47
- value: {
48
- base: string;
49
- _dark: string;
50
- };
51
- };
52
- highest: {
53
- value: {
54
- base: string;
55
- _dark: string;
56
- };
28
+ value: {
29
+ base: string;
30
+ _dark: string;
57
31
  };
58
32
  };
59
33
  };
60
- onSurface: {
34
+ secondary: {
61
35
  DEFAULT: {
62
36
  value: {
63
37
  base: string;
64
38
  _dark: string;
65
39
  };
66
40
  };
67
- variant: {
41
+ container: {
68
42
  value: {
69
43
  base: string;
70
44
  _dark: string;
71
45
  };
72
46
  };
73
47
  };
74
- m3Primary: {
48
+ onSecondary: {
75
49
  DEFAULT: {
76
50
  value: {
77
51
  base: string;
@@ -85,7 +59,7 @@ export declare const m3SemanticTokens: {
85
59
  };
86
60
  };
87
61
  };
88
- onM3Primary: {
62
+ tertiary: {
89
63
  DEFAULT: {
90
64
  value: {
91
65
  base: string;
@@ -99,7 +73,7 @@ export declare const m3SemanticTokens: {
99
73
  };
100
74
  };
101
75
  };
102
- m3Secondary: {
76
+ onTertiary: {
103
77
  DEFAULT: {
104
78
  value: {
105
79
  base: string;
@@ -113,7 +87,7 @@ export declare const m3SemanticTokens: {
113
87
  };
114
88
  };
115
89
  };
116
- onM3Secondary: {
90
+ error: {
117
91
  DEFAULT: {
118
92
  value: {
119
93
  base: string;
@@ -127,7 +101,7 @@ export declare const m3SemanticTokens: {
127
101
  };
128
102
  };
129
103
  };
130
- m3Tertiary: {
104
+ onError: {
131
105
  DEFAULT: {
132
106
  value: {
133
107
  base: string;
@@ -141,20 +115,90 @@ export declare const m3SemanticTokens: {
141
115
  };
142
116
  };
143
117
  };
144
- onM3Tertiary: {
118
+ surface: {
145
119
  DEFAULT: {
146
120
  value: {
147
121
  base: string;
148
122
  _dark: string;
149
123
  };
150
124
  };
125
+ dim: {
126
+ value: {
127
+ base: string;
128
+ _dark: string;
129
+ };
130
+ };
131
+ bright: {
132
+ value: {
133
+ base: string;
134
+ _dark: string;
135
+ };
136
+ };
151
137
  container: {
138
+ DEFAULT: {
139
+ value: {
140
+ base: string;
141
+ _dark: string;
142
+ };
143
+ };
144
+ low: {
145
+ value: {
146
+ base: string;
147
+ _dark: string;
148
+ };
149
+ };
150
+ lowest: {
151
+ value: {
152
+ base: string;
153
+ _dark: string;
154
+ };
155
+ };
156
+ high: {
157
+ value: {
158
+ base: string;
159
+ _dark: string;
160
+ };
161
+ };
162
+ highest: {
163
+ value: {
164
+ base: string;
165
+ _dark: string;
166
+ };
167
+ };
168
+ };
169
+ };
170
+ onSurface: {
171
+ DEFAULT: {
172
+ value: {
173
+ base: string;
174
+ _dark: string;
175
+ };
176
+ };
177
+ variant: {
152
178
  value: {
153
179
  base: string;
154
180
  _dark: string;
155
181
  };
156
182
  };
157
183
  };
184
+ surfaceVariant: {
185
+ value: {
186
+ base: string;
187
+ _dark: string;
188
+ };
189
+ };
190
+ background: {
191
+ value: {
192
+ base: string;
193
+ _dark: string;
194
+ };
195
+ };
196
+ onBackground: {
197
+ value: {
198
+ base: string;
199
+ _dark: string;
200
+ };
201
+ };
158
202
  outline: {
159
203
  DEFAULT: {
160
204
  value: {
@@ -205,5 +249,102 @@ export declare const m3SemanticTokens: {
205
249
  _dark: string;
206
250
  };
207
251
  };
252
+ shadow: {
253
+ value: {
254
+ base: string;
255
+ _dark: string;
256
+ };
257
+ };
258
+ };
259
+ /**
260
+ * DEPRECATED: m3-prefixed aliases — will be removed after discourser.ai token update session.
261
+ * Semantic tokens cannot reference other semantic tokens in Panda CSS, so these
262
+ * duplicate the hex values directly from the language contract.
263
+ */
264
+ export declare const m3SemanticTokens: {
265
+ m3Primary: {
266
+ DEFAULT: {
267
+ value: {
268
+ base: string;
269
+ _dark: string;
270
+ };
271
+ };
272
+ container: {
273
+ value: {
274
+ base: string;
275
+ _dark: string;
276
+ };
277
+ };
278
+ };
279
+ onM3Primary: {
280
+ DEFAULT: {
281
+ value: {
282
+ base: string;
283
+ _dark: string;
284
+ };
285
+ };
286
+ container: {
287
+ value: {
288
+ base: string;
289
+ _dark: string;
290
+ };
291
+ };
292
+ };
293
+ m3Secondary: {
294
+ DEFAULT: {
295
+ value: {
296
+ base: string;
297
+ _dark: string;
298
+ };
299
+ };
300
+ container: {
301
+ value: {
302
+ base: string;
303
+ _dark: string;
304
+ };
305
+ };
306
+ };
307
+ onM3Secondary: {
308
+ DEFAULT: {
309
+ value: {
310
+ base: string;
311
+ _dark: string;
312
+ };
313
+ };
314
+ container: {
315
+ value: {
316
+ base: string;
317
+ _dark: string;
318
+ };
319
+ };
320
+ };
321
+ m3Tertiary: {
322
+ DEFAULT: {
323
+ value: {
324
+ base: string;
325
+ _dark: string;
326
+ };
327
+ };
328
+ container: {
329
+ value: {
330
+ base: string;
331
+ _dark: string;
332
+ };
333
+ };
334
+ };
335
+ onM3Tertiary: {
336
+ DEFAULT: {
337
+ value: {
338
+ base: string;
339
+ _dark: string;
340
+ };
341
+ };
342
+ container: {
343
+ value: {
344
+ base: string;
345
+ _dark: string;
346
+ };
347
+ };
348
+ };
208
349
  };
209
350
  //# sourceMappingURL=semantic-tokens.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"semantic-tokens.d.ts","sourceRoot":"","sources":["../../src/preset/semantic-tokens.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkE3B,CAAC"}
1
+ {"version":3,"file":"semantic-tokens.d.ts","sourceRoot":"","sources":["../../src/preset/semantic-tokens.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4H9B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqC3B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@discourser/design-system",
3
- "version": "0.20.1",
3
+ "version": "0.21.0",
4
4
  "description": "Aesthetic-agnostic design system with Panda CSS and Ark UI",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Token Contract Tests
3
+ *
4
+ * Permanent regression guard. Prevents semantic-tokens.ts from drifting out of sync
5
+ * with material3.language.ts. Run in CI on every PR.
6
+ *
7
+ * If any test here fails, a semantic role is missing or broken. Fix the gap in
8
+ * semantic-tokens.ts — do not adjust these tests.
9
+ */
10
+
11
+ import { describe, test, expect } from 'vitest';
12
+ import { material3Language } from '../languages/material3.language';
13
+ import { semanticColorTokens } from '../preset/semantic-tokens';
14
+ import { discourserPandaPreset } from '../preset/index';
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Check 1: Coverage — every language semantic role is represented in the preset
18
+ // ---------------------------------------------------------------------------
19
+
20
+ describe('Semantic token coverage', () => {
21
+ const languageRoles = Object.keys(material3Language.semantic);
22
+ const presetKeys = flattenTokenKeys(semanticColorTokens);
23
+
24
+ test.each(languageRoles)(
25
+ '"%s" from language contract is represented in semanticColorTokens',
26
+ (role) => {
27
+ // Normalize: strip `.DEFAULT` suffix, remove all dots, lowercase.
28
+ // This lets `primaryContainer` match `primary.container`, etc.
29
+ const normalize = (k: string) =>
30
+ k
31
+ .toLowerCase()
32
+ .replace(/\.default$/i, '')
33
+ .replace(/\./g, '');
34
+ const normalizedRole = normalize(role);
35
+ const found = presetKeys.some((k) => normalize(k) === normalizedRole);
36
+ expect(found).toBe(true);
37
+ },
38
+ );
39
+ });
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Check 2: No undefined or empty values in any token definition
43
+ // ---------------------------------------------------------------------------
44
+
45
+ describe('Semantic token values', () => {
46
+ test('no token has an undefined or empty light value', () => {
47
+ walkTokens(semanticColorTokens, (_path, token) => {
48
+ if (
49
+ token.value &&
50
+ typeof token.value === 'object' &&
51
+ 'base' in token.value
52
+ ) {
53
+ expect(token.value.base).toBeDefined();
54
+ expect(token.value.base).not.toBe('');
55
+ expect(token.value.base).toMatch(/^#[0-9A-Fa-f]{6}$/);
56
+ }
57
+ });
58
+ });
59
+
60
+ test('no token has an undefined or empty dark value', () => {
61
+ walkTokens(semanticColorTokens, (_path, token) => {
62
+ if (
63
+ token.value &&
64
+ typeof token.value === 'object' &&
65
+ '_dark' in token.value
66
+ ) {
67
+ expect(token.value._dark).toBeDefined();
68
+ expect(token.value._dark).not.toBe('');
69
+ expect(token.value._dark).toMatch(/^#[0-9A-Fa-f]{6}$/);
70
+ }
71
+ });
72
+ });
73
+ });
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // Check 3: Language contract symmetry — semantic and semanticDark have identical keys
77
+ // ---------------------------------------------------------------------------
78
+
79
+ describe('Language contract symmetry', () => {
80
+ test('semanticDark has the same keys as semantic', () => {
81
+ const lightKeys = Object.keys(material3Language.semantic).sort();
82
+ const darkKeys = Object.keys(material3Language.semanticDark!).sort();
83
+ expect(darkKeys).toEqual(lightKeys);
84
+ });
85
+ });
86
+
87
+ // ---------------------------------------------------------------------------
88
+ // Check 4: Preset assembly — Radix bridge keys AND semantic keys coexist after deep-merge
89
+ // ---------------------------------------------------------------------------
90
+
91
+ describe('Preset color assembly', () => {
92
+ const colors = discourserPandaPreset.theme?.extend?.semanticTokens
93
+ ?.colors as Record<string, Record<string, unknown>>;
94
+
95
+ test.each(['primary', 'secondary', 'tertiary', 'error'])(
96
+ '%s has both Radix bridge keys (1, 12) and semantic keys (DEFAULT, container)',
97
+ (palette) => {
98
+ expect(colors[palette]['1']).toBeDefined();
99
+ expect(colors[palette]['12']).toBeDefined();
100
+ expect(colors[palette]['DEFAULT']).toBeDefined();
101
+ expect(colors[palette]['container']).toBeDefined();
102
+ },
103
+ );
104
+
105
+ test('surface has full nested container structure', () => {
106
+ const surface = colors['surface'] as Record<string, unknown>;
107
+ expect(surface['DEFAULT']).toBeDefined();
108
+ expect(
109
+ (surface['container'] as Record<string, unknown>)?.['DEFAULT'],
110
+ ).toBeDefined();
111
+ expect(
112
+ (surface['container'] as Record<string, unknown>)?.['low'],
113
+ ).toBeDefined();
114
+ expect(
115
+ (surface['container'] as Record<string, unknown>)?.['highest'],
116
+ ).toBeDefined();
117
+ });
118
+
119
+ test('onSurface has variant subkey', () => {
120
+ const onSurface = colors['onSurface'] as Record<string, unknown>;
121
+ expect(onSurface['DEFAULT']).toBeDefined();
122
+ expect(onSurface['variant']).toBeDefined();
123
+ });
124
+
125
+ test('previously missing tokens are now present', () => {
126
+ expect(colors['background']).toBeDefined();
127
+ expect(colors['onBackground']).toBeDefined();
128
+ expect(colors['shadow']).toBeDefined();
129
+ expect(colors['surfaceVariant']).toBeDefined();
130
+ expect(
131
+ (colors['onError'] as Record<string, unknown>)?.['DEFAULT'],
132
+ ).toBeDefined();
133
+ expect(
134
+ (colors['error'] as Record<string, unknown>)?.['container'],
135
+ ).toBeDefined();
136
+ });
137
+ });
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // Helpers
141
+ // ---------------------------------------------------------------------------
142
+
143
+ /** Flatten nested token object to dot-notation key array */
144
+ function flattenTokenKeys(obj: Record<string, unknown>, prefix = ''): string[] {
145
+ return Object.entries(obj).flatMap(([key, val]) => {
146
+ const fullKey = prefix ? `${prefix}.${key}` : key;
147
+ if (val && typeof val === 'object' && !('value' in (val as object))) {
148
+ return flattenTokenKeys(val as Record<string, unknown>, fullKey);
149
+ }
150
+ return [fullKey];
151
+ });
152
+ }
153
+
154
+ /** Walk all leaf token nodes and call callback */
155
+ function walkTokens(
156
+ obj: Record<string, unknown>,
157
+ callback: (path: string, token: { value: unknown }) => void,
158
+ prefix = '',
159
+ ) {
160
+ for (const [key, val] of Object.entries(obj)) {
161
+ const path = prefix ? `${prefix}.${key}` : key;
162
+ if (val && typeof val === 'object' && 'value' in (val as object)) {
163
+ callback(path, val as { value: unknown });
164
+ } else if (val && typeof val === 'object') {
165
+ walkTokens(val as Record<string, unknown>, callback, path);
166
+ }
167
+ }
168
+ }
@@ -0,0 +1,34 @@
1
+ import { ark } from '@ark-ui/react/factory';
2
+ import type { ComponentProps } from 'react';
3
+ import { styled } from 'styled-system/jsx';
4
+
5
+ const StyledSvg = styled(ark.svg);
6
+
7
+ export type TrashIconProps = ComponentProps<typeof StyledSvg>;
8
+
9
+ export const TrashIcon = (props: TrashIconProps) => (
10
+ <StyledSvg
11
+ viewBox="0 0 44 51"
12
+ fill="none"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ width="1em"
15
+ height="1em"
16
+ {...props}
17
+ >
18
+ <path d="M15.24 17.7998H17.8V38.2798H15.24V17.7998Z" fill="currentColor" />
19
+ <path
20
+ d="M20.3601 17.7998H22.9201V38.2798H20.3601V17.7998Z"
21
+ fill="currentColor"
22
+ />
23
+ <path d="M25.48 17.7998H28.04V38.2798H25.48V17.7998Z" fill="currentColor" />
24
+ <path d="M5 10.1201H38.28V12.6801H5V10.1201Z" fill="currentColor" />
25
+ <path
26
+ d="M28.04 10.12H25.48V8.84C25.48 8.072 24.968 7.56 24.2 7.56H19.08C18.312 7.56 17.8 8.072 17.8 8.84V10.12H15.24V8.84C15.24 6.664 16.904 5 19.08 5H24.2C26.376 5 28.04 6.664 28.04 8.84V10.12Z"
27
+ fill="currentColor"
28
+ />
29
+ <path
30
+ d="M29.3201 45.96H13.9601C11.9121 45.96 10.1201 44.296 9.86406 42.248L7.56006 11.528L10.1201 11.272L12.4241 41.992C12.4241 42.76 13.1921 43.4 13.9601 43.4H29.3201C30.0881 43.4 30.7281 42.76 30.8561 41.992L33.1601 11.272L35.7201 11.528L33.4161 42.248C33.1601 44.296 31.3681 45.96 29.3201 45.96Z"
31
+ fill="currentColor"
32
+ />
33
+ </StyledSvg>
34
+ );
@@ -37,3 +37,4 @@ export {
37
37
  export { UserProfileIcon, type UserProfileIconProps } from './UserProfileIcon';
38
38
  export { PlayIcon, type PlayIconProps } from './PlayIcon';
39
39
  export { SpeechIcon, type SpeechIconProps } from './SpeechIcon';
40
+ export { TrashIcon, type TrashIconProps } from './TrashIcon';