@duro-app/tokens 4.5.0 → 5.1.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/src/keys.ts CHANGED
@@ -31,12 +31,24 @@ export const SPACING_PX = {
31
31
  xxxl: 64,
32
32
  } as const
33
33
 
34
- export const RADIUS_KEYS = ['xs', 'sm', 'md', 'lg', 'full'] as const
34
+ // Mirrors tokens/spacing.css.ts `microSpacing` (px). Not a SpacingToken.
35
+ export const MICRO_SPACING_KEYS = ['px1', 'px2', 'px3', 'px5', 'px6'] as const
36
+ export type MicroSpacingToken = (typeof MICRO_SPACING_KEYS)[number]
37
+ export const MICRO_SPACING_PX = {
38
+ px1: 1,
39
+ px2: 2,
40
+ px3: 3,
41
+ px5: 5,
42
+ px6: 6,
43
+ } as const
44
+
45
+ export const RADIUS_KEYS = ['xs', 'px6', 'sm', 'md', 'lg', 'full'] as const
35
46
  export type RadiusToken = (typeof RADIUS_KEYS)[number]
36
47
 
37
48
  // Mirrors tokens/spacing.css.ts `radii` (px).
38
49
  export const RADII_PX = {
39
50
  xs: 4,
51
+ px6: 6,
40
52
  sm: 8,
41
53
  md: 12,
42
54
  lg: 16,
@@ -49,20 +61,148 @@ export type ShadowToken = (typeof SHADOW_KEYS)[number]
49
61
  // Mirrors tokens/motion.css.ts `duration`, as numbers for setTimeout use.
50
62
  export const DURATION_MS = {
51
63
  instant: 0,
64
+ minimal: 1,
65
+ quick: 120,
52
66
  fast: 150,
67
+ brisk: 160,
53
68
  base: 200,
54
69
  slow: 280,
55
70
  } as const
56
71
  export type DurationToken = keyof typeof DURATION_MS
57
72
 
58
- // Icon rendering sizes (SVG width/height, px). Not a css.defineVars scale —
59
- // icons size via attributes, not CSS custom properties.
73
+ export const SIZE_KEYS = [
74
+ 'touchTarget',
75
+ 'controlSm',
76
+ 'controlMd',
77
+ 'controlLg',
78
+ 'indicator',
79
+ 'indicatorDot',
80
+ 'checkMarkW',
81
+ 'checkMarkH',
82
+ 'switchTrackW',
83
+ 'switchTrackH',
84
+ 'switchThumb',
85
+ 'iconButton',
86
+ 'iconButtonSm',
87
+ 'spinnerSm',
88
+ 'spinnerMd',
89
+ 'spinnerLg',
90
+ 'glyphXs',
91
+ 'glyphSm',
92
+ 'glyphMd',
93
+ 'iconSm',
94
+ 'iconMd',
95
+ 'iconLg',
96
+ 'iconXl',
97
+ 'iconXxl',
98
+ 'navMarkerW',
99
+ 'navMarkerH',
100
+ 'divider',
101
+ 'tabIndicator',
102
+ 'edgeFade',
103
+ 'scrollbar',
104
+ 'swatchW',
105
+ 'swatchH',
106
+ 'labelMinW',
107
+ 'popupMinW',
108
+ 'listMaxH',
109
+ 'listMaxHSm',
110
+ 'dialogSm',
111
+ 'dialogMd',
112
+ 'dialogLg',
113
+ 'panelSm',
114
+ 'panelMd',
115
+ 'panelLg',
116
+ 'toastMaxW',
117
+ 'gridColSm',
118
+ 'gridColMd',
119
+ 'pageSm',
120
+ 'pageMd',
121
+ 'pageLg',
122
+ ] as const
123
+ export type SizeToken = (typeof SIZE_KEYS)[number]
124
+
125
+ // Mirrors tokens/sizes.css.ts `sizes` (px).
126
+ export const SIZES_PX = {
127
+ touchTarget: 44,
128
+ controlSm: 28,
129
+ controlMd: 39,
130
+ controlLg: 40,
131
+ indicator: 18,
132
+ indicatorDot: 8,
133
+ checkMarkW: 5,
134
+ checkMarkH: 9,
135
+ switchTrackW: 36,
136
+ switchTrackH: 20,
137
+ switchThumb: 16,
138
+ iconButton: 32,
139
+ iconButtonSm: 28,
140
+ spinnerSm: 16,
141
+ spinnerMd: 24,
142
+ spinnerLg: 40,
143
+ glyphXs: 10,
144
+ glyphSm: 12,
145
+ glyphMd: 16,
146
+ iconSm: 16,
147
+ iconMd: 18,
148
+ iconLg: 24,
149
+ iconXl: 36,
150
+ iconXxl: 48,
151
+ navMarkerW: 3,
152
+ navMarkerH: 18,
153
+ divider: 1,
154
+ tabIndicator: 2,
155
+ edgeFade: 32,
156
+ scrollbar: 8,
157
+ swatchW: 44,
158
+ swatchH: 34,
159
+ labelMinW: 120,
160
+ popupMinW: 160,
161
+ listMaxH: 280,
162
+ listMaxHSm: 200,
163
+ dialogSm: 400,
164
+ dialogMd: 520,
165
+ dialogLg: 680,
166
+ panelSm: 360,
167
+ panelMd: 480,
168
+ panelLg: 640,
169
+ toastMaxW: 440,
170
+ gridColSm: 240,
171
+ gridColMd: 280,
172
+ pageSm: 600,
173
+ pageMd: 800,
174
+ pageLg: 1200,
175
+ } as const
176
+
177
+ export const BORDER_KEYS = [
178
+ 'hairline',
179
+ 'strong',
180
+ 'accent',
181
+ 'focusRing',
182
+ 'focusOffset',
183
+ 'focusOffsetSm',
184
+ ] as const
185
+ export type BorderToken = (typeof BORDER_KEYS)[number]
186
+
187
+ // Mirrors tokens/borders.css.ts `borders` (px).
188
+ export const BORDERS_PX = {
189
+ hairline: 1,
190
+ strong: 2,
191
+ accent: 3,
192
+ focusRing: 2,
193
+ focusOffset: 2,
194
+ focusOffsetSm: 1,
195
+ } as const
196
+
197
+ // Icon rendering sizes (SVG width/height, px). Not its own css.defineVars
198
+ // scale — icons size via attributes — so it is derived from the `sizes` group
199
+ // (iconSm…iconXxl), keeping one source.
60
200
  export const ICON_SIZES = {
61
- sm: 16,
62
- md: 18,
63
- lg: 24,
64
- xl: 36,
65
- xxl: 48,
201
+ sm: SIZES_PX.iconSm,
202
+ md: SIZES_PX.iconMd,
203
+ lg: SIZES_PX.iconLg,
204
+ xl: SIZES_PX.iconXl,
205
+ xxl: SIZES_PX.iconXxl,
66
206
  } as const
67
207
  export type IconSize = keyof typeof ICON_SIZES
68
208
 
@@ -119,6 +259,7 @@ export const SHADOWS = {
119
259
  // Mirrors tokens/motion.css.ts `easing` verbatim.
120
260
  export const EASINGS = {
121
261
  standard: 'ease',
262
+ linear: 'linear',
122
263
  easeOut: 'cubic-bezier(0.32, 0.72, 0, 1)',
123
264
  easeIn: 'cubic-bezier(0.72, 0, 0.68, 0.28)',
124
265
  } as const
package/src/raw.ts CHANGED
@@ -37,8 +37,110 @@ export type RawColors = {
37
37
  infoBg: string
38
38
  infoBorder: string
39
39
  infoText: string
40
+ scrim: string
41
+ inverseFill: string
42
+ inverseFillHover: string
43
+ inverseBorder: string
44
+ inverseBorderHover: string
45
+ fixedLight: string
40
46
  }
41
47
 
48
+ // Numeric maps for consumers that load only the compiled `/raw` entry (e.g.
49
+ // ui-email, which cannot read CSS variables or the TypeScript `/keys` source).
50
+ // Copies of the keys.ts maps; check-token-drift.mjs fails if they diverge.
51
+ export const SPACING_PX = {
52
+ xs: 4,
53
+ sm: 8,
54
+ ms: 12,
55
+ md: 16,
56
+ lg: 24,
57
+ xl: 32,
58
+ xxl: 48,
59
+ xxxl: 64,
60
+ } as const
61
+
62
+ export const RADII_PX = {
63
+ xs: 4,
64
+ px6: 6,
65
+ sm: 8,
66
+ md: 12,
67
+ lg: 16,
68
+ full: 9999,
69
+ } as const
70
+
71
+ export const FONT_SIZE_REM = {
72
+ fontSizeXs: 0.75,
73
+ fontSizeSm: 0.875,
74
+ fontSizeMd: 1,
75
+ fontSizeLg: 1.125,
76
+ fontSizeXl: 1.25,
77
+ fontSizeHeading: 1.5,
78
+ } as const
79
+
80
+ export const BORDERS_PX = {
81
+ hairline: 1,
82
+ strong: 2,
83
+ accent: 3,
84
+ focusRing: 2,
85
+ focusOffset: 2,
86
+ focusOffsetSm: 1,
87
+ } as const
88
+
89
+ export const SIZES_PX = {
90
+ touchTarget: 44,
91
+ controlSm: 28,
92
+ controlMd: 39,
93
+ controlLg: 40,
94
+ indicator: 18,
95
+ indicatorDot: 8,
96
+ checkMarkW: 5,
97
+ checkMarkH: 9,
98
+ switchTrackW: 36,
99
+ switchTrackH: 20,
100
+ switchThumb: 16,
101
+ iconButton: 32,
102
+ iconButtonSm: 28,
103
+ spinnerSm: 16,
104
+ spinnerMd: 24,
105
+ spinnerLg: 40,
106
+ glyphXs: 10,
107
+ glyphSm: 12,
108
+ glyphMd: 16,
109
+ iconSm: 16,
110
+ iconMd: 18,
111
+ iconLg: 24,
112
+ iconXl: 36,
113
+ iconXxl: 48,
114
+ navMarkerW: 3,
115
+ navMarkerH: 18,
116
+ divider: 1,
117
+ tabIndicator: 2,
118
+ edgeFade: 32,
119
+ scrollbar: 8,
120
+ swatchW: 44,
121
+ swatchH: 34,
122
+ labelMinW: 120,
123
+ popupMinW: 160,
124
+ listMaxH: 280,
125
+ listMaxHSm: 200,
126
+ dialogSm: 400,
127
+ dialogMd: 520,
128
+ dialogLg: 680,
129
+ panelSm: 360,
130
+ panelMd: 480,
131
+ panelLg: 640,
132
+ toastMaxW: 440,
133
+ gridColSm: 240,
134
+ gridColMd: 280,
135
+ pageSm: 600,
136
+ pageMd: 800,
137
+ pageLg: 1200,
138
+ } as const
139
+
140
+ // Email-only measures: no CSS variable (mail clients cannot read them) and not
141
+ // a lint candidate, so it is deliberately absent from keys.ts.
142
+ export const EMAIL_PX = {emailCardW: 520, trackingPixel: 1} as const
143
+
42
144
  // Dark theme — matches the defaults in `tokens/colors.css.ts`.
43
145
  export const darkColors: RawColors = {
44
146
  bg: '#0f0f0f',
@@ -68,6 +170,12 @@ export const darkColors: RawColors = {
68
170
  infoBg: 'rgba(106, 175, 252, 0.1)',
69
171
  infoBorder: 'rgba(106, 175, 252, 0.3)',
70
172
  infoText: '#93c5fd',
173
+ scrim: 'rgba(0, 0, 0, 0.4)',
174
+ inverseFill: 'rgba(0, 0, 0, 0.10)',
175
+ inverseFillHover: 'rgba(0, 0, 0, 0.18)',
176
+ inverseBorder: 'rgba(0, 0, 0, 0.55)',
177
+ inverseBorderHover: 'rgba(0, 0, 0, 0.70)',
178
+ fixedLight: '#ffffff',
71
179
  }
72
180
 
73
181
  // Light theme — matches the overrides in `themes/light.css.ts`.
@@ -99,6 +207,12 @@ export const lightColors: RawColors = {
99
207
  infoBg: 'rgba(30, 64, 175, 0.08)',
100
208
  infoBorder: 'rgba(30, 64, 175, 0.3)',
101
209
  infoText: '#1e40af',
210
+ scrim: 'rgba(0, 0, 0, 0.4)',
211
+ inverseFill: 'rgba(0, 0, 0, 0.10)',
212
+ inverseFillHover: 'rgba(0, 0, 0, 0.18)',
213
+ inverseBorder: 'rgba(0, 0, 0, 0.55)',
214
+ inverseBorderHover: 'rgba(0, 0, 0, 0.70)',
215
+ fixedLight: '#ffffff',
102
216
  }
103
217
 
104
218
  // High-contrast theme — matches the overrides in `themes/high-contrast.css.ts`.
@@ -130,4 +244,10 @@ export const highContrastColors: RawColors = {
130
244
  infoBg: 'rgba(96, 165, 250, 0.15)',
131
245
  infoBorder: 'rgba(96, 165, 250, 0.5)',
132
246
  infoText: '#bfdbfe',
247
+ scrim: 'rgba(0, 0, 0, 0.4)',
248
+ inverseFill: 'rgba(0, 0, 0, 0.10)',
249
+ inverseFillHover: 'rgba(0, 0, 0, 0.18)',
250
+ inverseBorder: 'rgba(0, 0, 0, 0.55)',
251
+ inverseBorderHover: 'rgba(0, 0, 0, 0.70)',
252
+ fixedLight: '#ffffff',
133
253
  }
@@ -30,6 +30,15 @@ export const highContrastTheme = css.createTheme(colors, {
30
30
  infoBg: 'rgba(96, 165, 250, 0.15)',
31
31
  infoBorder: 'rgba(96, 165, 250, 0.5)',
32
32
  infoText: '#bfdbfe',
33
+ // Fixed overlays — the same in every theme (a scrim darkens whatever is
34
+ // under it; the inverse overlays sit on an accent surface).
35
+ scrim: 'rgba(0, 0, 0, 0.4)',
36
+ inverseFill: 'rgba(0, 0, 0, 0.10)',
37
+ inverseFillHover: 'rgba(0, 0, 0, 0.18)',
38
+ inverseBorder: 'rgba(0, 0, 0, 0.55)',
39
+ inverseBorderHover: 'rgba(0, 0, 0, 0.70)',
40
+ // A light that does not follow the theme (the Switch knob).
41
+ fixedLight: '#ffffff',
33
42
  })
34
43
 
35
44
  export const highContrastShadows = css.createTheme(shadows, {
@@ -30,6 +30,15 @@ export const lightTheme = css.createTheme(colors, {
30
30
  infoBg: 'rgba(30, 64, 175, 0.08)',
31
31
  infoBorder: 'rgba(30, 64, 175, 0.3)',
32
32
  infoText: '#1e40af',
33
+ // Fixed overlays — the same in every theme (a scrim darkens whatever is
34
+ // under it; the inverse overlays sit on an accent surface).
35
+ scrim: 'rgba(0, 0, 0, 0.4)',
36
+ inverseFill: 'rgba(0, 0, 0, 0.10)',
37
+ inverseFillHover: 'rgba(0, 0, 0, 0.18)',
38
+ inverseBorder: 'rgba(0, 0, 0, 0.55)',
39
+ inverseBorderHover: 'rgba(0, 0, 0, 0.70)',
40
+ // A light that does not follow the theme (the Switch knob).
41
+ fixedLight: '#ffffff',
33
42
  })
34
43
 
35
44
  export const lightShadows = css.createTheme(shadows, {
@@ -0,0 +1,10 @@
1
+ import {css} from 'react-strict-dom'
2
+
3
+ export const borders = css.defineVars({
4
+ hairline: '1px',
5
+ strong: '2px',
6
+ accent: '3px',
7
+ focusRing: '2px',
8
+ focusOffset: '2px',
9
+ focusOffsetSm: '1px',
10
+ })
@@ -43,4 +43,14 @@ export const colors = css.defineVars({
43
43
  infoBg: 'rgba(106, 175, 252, 0.1)',
44
44
  infoBorder: 'rgba(106, 175, 252, 0.3)',
45
45
  infoText: '#93c5fd',
46
+
47
+ // Fixed overlays — the same in every theme (a scrim darkens whatever is
48
+ // under it; the inverse overlays sit on an accent surface).
49
+ scrim: 'rgba(0, 0, 0, 0.4)',
50
+ inverseFill: 'rgba(0, 0, 0, 0.10)',
51
+ inverseFillHover: 'rgba(0, 0, 0, 0.18)',
52
+ inverseBorder: 'rgba(0, 0, 0, 0.55)',
53
+ inverseBorderHover: 'rgba(0, 0, 0, 0.70)',
54
+ // A light that does not follow the theme (the Switch knob).
55
+ fixedLight: '#ffffff',
46
56
  })
@@ -7,8 +7,12 @@ import {css} from 'react-strict-dom'
7
7
  * - durations: 150ms is the de-facto transition (Button, Card, and ~30 other
8
8
  * spots), 200ms the common overlay enter (Drawer), 280ms the larger panel
9
9
  * enter (DetailPanel). `instant` is for explicit no-motion.
10
+ * `quick` (List hover, sort indicator) and `brisk` (Toast enter) are the
11
+ * two other values components used; `minimal` is the 1ms reduced-motion
12
+ * duration that still fires animationend.
10
13
  * - easings: `standard` is the default `ease` used throughout; `easeOut` /
11
14
  * `easeIn` are the "Apple ease" curves already defined for DetailPanel.
15
+ * `linear` is the Spinner's constant rotation.
12
16
  *
13
17
  * Reduced motion is handled globally (the `@media (prefers-reduced-motion)`
14
18
  * rule in the reset neutralizes timing) plus per-component transform guards, so
@@ -16,13 +20,18 @@ import {css} from 'react-strict-dom'
16
20
  */
17
21
  export const duration = css.defineVars({
18
22
  instant: '0ms',
23
+ // Non-zero so animationend still fires under reduced motion.
24
+ minimal: '1ms',
25
+ quick: '120ms',
19
26
  fast: '150ms',
27
+ brisk: '160ms',
20
28
  base: '200ms',
21
29
  slow: '280ms',
22
30
  })
23
31
 
24
32
  export const easing = css.defineVars({
25
33
  standard: 'ease',
34
+ linear: 'linear',
26
35
  easeOut: 'cubic-bezier(0.32, 0.72, 0, 1)',
27
36
  easeIn: 'cubic-bezier(0.72, 0, 0.68, 0.28)',
28
37
  })
@@ -0,0 +1,52 @@
1
+ import {css} from 'react-strict-dom'
2
+
3
+ export const sizes = css.defineVars({
4
+ touchTarget: '44px',
5
+ controlSm: '28px',
6
+ controlMd: '39px',
7
+ controlLg: '40px',
8
+ indicator: '18px',
9
+ indicatorDot: '8px',
10
+ checkMarkW: '5px',
11
+ checkMarkH: '9px',
12
+ switchTrackW: '36px',
13
+ switchTrackH: '20px',
14
+ switchThumb: '16px',
15
+ iconButton: '32px',
16
+ iconButtonSm: '28px',
17
+ spinnerSm: '16px',
18
+ spinnerMd: '24px',
19
+ spinnerLg: '40px',
20
+ glyphXs: '10px',
21
+ glyphSm: '12px',
22
+ glyphMd: '16px',
23
+ iconSm: '16px',
24
+ iconMd: '18px',
25
+ iconLg: '24px',
26
+ iconXl: '36px',
27
+ iconXxl: '48px',
28
+ navMarkerW: '3px',
29
+ navMarkerH: '18px',
30
+ divider: '1px',
31
+ tabIndicator: '2px',
32
+ edgeFade: '32px',
33
+ scrollbar: '8px',
34
+ swatchW: '44px',
35
+ swatchH: '34px',
36
+ labelMinW: '120px',
37
+ popupMinW: '160px',
38
+ listMaxH: '280px',
39
+ listMaxHSm: '200px',
40
+ dialogSm: '400px',
41
+ dialogMd: '520px',
42
+ dialogLg: '680px',
43
+ panelSm: '360px',
44
+ panelMd: '480px',
45
+ panelLg: '640px',
46
+ toastMaxW: '440px',
47
+ gridColSm: '240px',
48
+ gridColMd: '280px',
49
+ pageSm: '600px',
50
+ pageMd: '800px',
51
+ pageLg: '1200px',
52
+ })
@@ -11,8 +11,20 @@ export const spacing = css.defineVars({
11
11
  xxxl: '64px',
12
12
  })
13
13
 
14
+ // Optical micro-steps below and between the spacing scale: a 1-6px nudge that
15
+ // aligns a glyph or tightens a dense control. Not a layout choice, so not part
16
+ // of SpacingToken (no `gap="px2"`); prefer the scale.
17
+ export const microSpacing = css.defineVars({
18
+ px1: '1px',
19
+ px2: '2px',
20
+ px3: '3px',
21
+ px5: '5px',
22
+ px6: '6px',
23
+ })
24
+
14
25
  export const radii = css.defineVars({
15
26
  xs: '4px',
27
+ px6: '6px',
16
28
  sm: '8px',
17
29
  md: '12px',
18
30
  lg: '16px',