@sesamehr/react-design-system 3.3.0 → 3.4.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/Button/ButtonGroup/ButtonGroup.d.ts +11 -0
- package/dist/Button/ButtonGroup/index.d.ts +1 -0
- package/dist/Button/Fab/Fab.d.ts +24 -0
- package/dist/Button/Fab/index.d.ts +9 -0
- package/dist/Button/IconButton/IconButton.d.ts +2 -1
- package/dist/Button/IconButton/index.d.ts +5 -4
- package/dist/Button/SplitButton/SplitButton.d.ts +48 -0
- package/dist/Button/SplitButton/index.d.ts +1 -0
- package/dist/Button/index.d.ts +1 -1
- package/dist/Data/Table/hooks/useTableVirtual/useTableVirtual.types.d.ts +1 -1
- package/dist/Display/Badge/Badge.d.ts +3 -1
- package/dist/Display/Badge/index.d.ts +7 -5
- package/dist/Display/Mention/Mention.d.ts +28 -0
- package/dist/Display/Mention/index.d.ts +6 -0
- package/dist/Display/PlayerPill/PlayerPill.d.ts +11 -0
- package/dist/Display/PlayerPill/PlayerPillAction.d.ts +10 -0
- package/dist/Display/PlayerPill/PlayerPillStatus.d.ts +11 -0
- package/dist/Display/PlayerPill/PlayerPillTime.d.ts +18 -0
- package/dist/Display/PlayerPill/index.d.ts +15 -0
- package/dist/Display/PlayerPill/playerPillContext.d.ts +16 -0
- package/dist/Display/PlayerPill/useElapsedSeconds.d.ts +18 -0
- package/dist/Display/Tag/Tag.d.ts +17 -0
- package/dist/Display/Tag/index.d.ts +14 -0
- package/dist/Feedback/Alert/AlertDescription.d.ts +1 -1
- package/dist/Feedback/Alert/alert-context.d.ts +1 -1
- package/dist/Feedback/Bullet/index.d.ts +1 -1
- package/dist/Feedback/Dialog/DialogFullScreenContent.d.ts +33 -0
- package/dist/Feedback/Dialog/index.d.ts +1 -0
- package/dist/Feedback/EmptyState/EmptyState.d.ts +26 -0
- package/dist/Feedback/EmptyState/EmptyStateOption/EmptyStateOption.d.ts +16 -0
- package/dist/Feedback/EmptyState/EmptyStateOption/index.d.ts +1 -0
- package/dist/Feedback/EmptyState/index.d.ts +10 -0
- package/dist/Feedback/Toast/Toast.d.ts +15 -0
- package/dist/Feedback/Toast/ToastAction.d.ts +12 -0
- package/dist/Feedback/Toast/ToastClose.d.ts +12 -0
- package/dist/Feedback/Toast/ToastDescription.d.ts +4 -0
- package/dist/Feedback/Toast/ToastIcon.d.ts +5 -0
- package/dist/Feedback/Toast/ToastTitle.d.ts +4 -0
- package/dist/Feedback/Toast/Toaster.d.ts +24 -0
- package/dist/Feedback/Toast/index.d.ts +26 -0
- package/dist/Feedback/Toast/toast-context.d.ts +6 -0
- package/dist/Feedback/Toast/useToast.d.ts +51 -0
- package/dist/Forms/Field/useFieldGroup.d.ts +1 -1
- package/dist/Forms/Inputs/ColorPicker/ColorPicker.d.ts +15 -0
- package/dist/Forms/Inputs/ColorPicker/ColorPickerContent.d.ts +14 -0
- package/dist/Forms/Inputs/ColorPicker/ColorPickerSwatch.d.ts +11 -0
- package/dist/Forms/Inputs/ColorPicker/ColorPickerTrigger.d.ts +17 -0
- package/dist/Forms/Inputs/ColorPicker/index.d.ts +5 -0
- package/dist/Forms/Inputs/ColorPicker/useColorPicker.d.ts +21 -0
- package/dist/Forms/Inputs/DatePicker/DatePickerTrigger.d.ts +1 -1
- package/dist/Forms/Inputs/IconPicker/IconPicker.d.ts +42 -0
- package/dist/Forms/Inputs/IconPicker/IconPickerContent.d.ts +26 -0
- package/dist/Forms/Inputs/IconPicker/IconPickerItem.d.ts +31 -0
- package/dist/Forms/Inputs/IconPicker/IconPickerTrigger.d.ts +19 -0
- package/dist/Forms/Inputs/IconPicker/index.d.ts +5 -0
- package/dist/Forms/Inputs/IconPicker/useIconPicker.d.ts +44 -0
- package/dist/Forms/Inputs/InputOtp/InputOtpGroup/InputOtpGroup.d.ts +5 -1
- package/dist/Forms/Inputs/InputOtp/types.d.ts +1 -1
- package/dist/Forms/Inputs/Select/SelectTrigger.d.ts +1 -1
- package/dist/Forms/Inputs/Textarea/Textarea.d.ts +48 -3
- package/dist/Forms/Inputs/Textarea/index.d.ts +3 -2
- package/dist/Forms/Inputs/Toggle/Switch/Switch.d.ts +11 -0
- package/dist/Forms/Inputs/index.d.ts +2 -0
- package/dist/Forms/Inputs/inputSize.d.ts +1 -1
- package/dist/Layout/Scrollbar/Scrollbar.d.ts +2 -2
- package/dist/Navigation/Stepper/Stepper.d.ts +23 -0
- package/dist/Navigation/Stepper/StepperItem.d.ts +13 -0
- package/dist/Navigation/Stepper/StepperSubsteps.d.ts +12 -0
- package/dist/Navigation/Stepper/index.d.ts +4 -0
- package/dist/Navigation/Stepper/useStepper.d.ts +25 -0
- package/dist/Navigation/Stepper/useStepperPill.d.ts +28 -0
- package/dist/Navigation/ToggleTabs/useToggleTabsPill.d.ts +1 -1
- package/dist/Transitions/TransitionBase.d.ts +5 -1
- package/dist/hooks/useAutoGrow/useAutoGrow.d.ts +7 -0
- package/dist/main.d.ts +12 -2
- package/dist/react-design-system.css +1 -1
- package/dist/react-design-system.js +21830 -18683
- package/dist/react-design-system.umd.cjs +118 -87
- package/package.json +4 -2
- package/src/assets/styles/theme-v2.css +14 -22
- package/src/assets/styles/theme-v3.css +161 -23
- package/src/assets/styles/theme-v3.test.ts +242 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sesamehr/react-design-system",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Sesame HR React design system — components and design tokens",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"require": "./dist/react-design-system.umd.cjs"
|
|
15
15
|
},
|
|
16
16
|
"./style.css": "./dist/react-design-system.css",
|
|
17
|
+
"./preflight.css": "./dist/preflight.css",
|
|
17
18
|
"./assets/styles/mainTheme.css": "./src/assets/styles/mainTheme.css",
|
|
18
19
|
"./lib/utils": {
|
|
19
20
|
"types": "./dist/lib/utils.d.ts",
|
|
@@ -56,8 +57,9 @@
|
|
|
56
57
|
"@radix-ui/react-slot": "^1.2.3",
|
|
57
58
|
"@radix-ui/react-switch": "^1.2.5",
|
|
58
59
|
"@radix-ui/react-tabs": "^1.1.12",
|
|
60
|
+
"@radix-ui/react-toast": "^1.2.24",
|
|
59
61
|
"@radix-ui/react-tooltip": "^1.2.7",
|
|
60
|
-
"@sesame/orxata-icons": "1.
|
|
62
|
+
"@sesame/orxata-icons": "1.8.0",
|
|
61
63
|
"@tailwindcss/vite": "^4.1.13",
|
|
62
64
|
"@tanstack/react-table": "^8.21.2",
|
|
63
65
|
"@tanstack/react-virtual": "^3.14.13",
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Aligns with Figma: 3. Mode → 2. Theme → 1. TailwindCSS
|
|
7
7
|
*
|
|
8
8
|
* DS EXTENSIONS (pending design approval):
|
|
9
|
-
* - success, neutral-*,
|
|
9
|
+
* - success, neutral-*, border-*, bg-*
|
|
10
10
|
*
|
|
11
11
|
* Dark mode: .dark class on <html>
|
|
12
12
|
*/
|
|
@@ -16,13 +16,19 @@
|
|
|
16
16
|
=========================================== */
|
|
17
17
|
|
|
18
18
|
:root {
|
|
19
|
-
/*
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
/* No `font-*` family, for the same reason v3 has none: every one of them was
|
|
20
|
+
a second name for a semantic declared in this same file. `--font-primary`
|
|
21
|
+
and `--foreground` are both indigo-950 here (white in dark),
|
|
22
|
+
`--font-secondary` and `--muted-foreground` both slate-500 (slate-300),
|
|
23
|
+
and the feedback three are `--error` / `--success` / `--warning`. The
|
|
24
|
+
values below are untouched; only the aliases are gone, so nothing that
|
|
25
|
+
reactivates this file loses a colour — `text-foreground` and
|
|
26
|
+
`text-muted-foreground` resolve here exactly as they do under v3.
|
|
27
|
+
|
|
28
|
+
`--font-tertiary` (slate-400) is the one with no equivalent, in v2 as in
|
|
29
|
+
v3. Its call sites read `--muted-foreground` now, which in this file's
|
|
30
|
+
dark block is slate-300 — a step lighter rather than darker. Academic
|
|
31
|
+
while `mainTheme.css` imports v3 and nothing imports this. */
|
|
26
32
|
|
|
27
33
|
/* Border */
|
|
28
34
|
--border-default: var(--color-slate-200);
|
|
@@ -65,14 +71,6 @@
|
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
.dark {
|
|
68
|
-
/* Font */
|
|
69
|
-
--font-primary: var(--color-white);
|
|
70
|
-
--font-secondary: var(--color-slate-300);
|
|
71
|
-
--font-tertiary: var(--color-slate-400);
|
|
72
|
-
--font-error: var(--color-red-400);
|
|
73
|
-
--font-success: var(--color-green-600);
|
|
74
|
-
--font-warning: var(--color-amber-500);
|
|
75
|
-
|
|
76
74
|
/* Border */
|
|
77
75
|
--border-default: var(--color-slate-700);
|
|
78
76
|
--border-hover: var(--color-indigo-400);
|
|
@@ -192,12 +190,6 @@
|
|
|
192
190
|
/* Expose tokens to Tailwind */
|
|
193
191
|
@theme inline {
|
|
194
192
|
/* DS tokens */
|
|
195
|
-
--color-font-primary: var(--font-primary);
|
|
196
|
-
--color-font-secondary: var(--font-secondary);
|
|
197
|
-
--color-font-tertiary: var(--font-tertiary);
|
|
198
|
-
--color-font-error: var(--font-error);
|
|
199
|
-
--color-font-success: var(--font-success);
|
|
200
|
-
--color-font-warning: var(--font-warning);
|
|
201
193
|
--color-border-default: var(--border-default);
|
|
202
194
|
--color-border-hover: var(--border-hover);
|
|
203
195
|
--color-bg-base: var(--bg-base);
|
|
@@ -20,18 +20,21 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
:root {
|
|
23
|
-
--font-primary: var(--color-zinc-900);
|
|
24
|
-
--font-secondary: var(--color-zinc-500);
|
|
25
|
-
--font-tertiary: var(--color-zinc-400);
|
|
26
|
-
--font-error: var(--color-red-500);
|
|
27
|
-
--font-success: var(--color-green-500);
|
|
28
|
-
--font-warning: var(--color-amber-500);
|
|
29
|
-
--font-info: var(--color-blue-500);
|
|
30
|
-
|
|
31
23
|
--border-default: var(--color-zinc-200);
|
|
32
24
|
--border-hover: var(--color-brand-300);
|
|
33
25
|
|
|
34
|
-
|
|
26
|
+
/* Not the 500 every other semantic colour takes, and `--success-500` sitting
|
|
27
|
+
just below holding green-500 makes that look like a slip. It is not: the
|
|
28
|
+
v3 board binds `success` to green-700 and its hover to green-800, because
|
|
29
|
+
white text on green-500 does not clear contrast on a filled button. The
|
|
30
|
+
`-100/-300/-500` beneath are a tint ramp with the shade in the name, and
|
|
31
|
+
they are a different thing from the semantic colour.
|
|
32
|
+
|
|
33
|
+
Same shape for `--error`, which is red-600 rather than red-500, and for
|
|
34
|
+
`--brand-hover`, which *darkens* to brand-600 — it used to mix towards
|
|
35
|
+
white, which is the opposite of what every hover on that board does. */
|
|
36
|
+
--success: var(--color-green-700);
|
|
37
|
+
--success-hover: var(--color-green-800);
|
|
35
38
|
--success-foreground: var(--color-white);
|
|
36
39
|
--success-100: var(--color-green-100);
|
|
37
40
|
--success-300: var(--color-green-300);
|
|
@@ -55,9 +58,24 @@
|
|
|
55
58
|
--neutral-950: var(--color-zinc-950);
|
|
56
59
|
|
|
57
60
|
--brand: var(--color-brand-500);
|
|
58
|
-
--brand-hover:
|
|
61
|
+
--brand-hover: var(--color-brand-600);
|
|
62
|
+
--brand-active: var(--color-brand-700);
|
|
59
63
|
--brand-foreground: var(--color-white);
|
|
60
64
|
|
|
65
|
+
/* A brand *surface*, not a brand fill: the 100 tint carrying the 700 of the
|
|
66
|
+
same hue, which is what clears 4.5:1 on it. Figma's `brand-subtle`
|
|
67
|
+
description names Button's outline and ghost hover, and the board binds the
|
|
68
|
+
family on Tag, Stepper, Mention and IconPicker too.
|
|
69
|
+
|
|
70
|
+
`-active` is the pressed/hover step on such a surface and `-border` the 1px
|
|
71
|
+
line around one. Both are brand-200 today, and they stay two names because
|
|
72
|
+
the board binds them to two different properties — repainting the line must
|
|
73
|
+
not drag the pressed fill with it. */
|
|
74
|
+
--brand-subtle: var(--color-brand-100);
|
|
75
|
+
--brand-subtle-foreground: var(--color-brand-700);
|
|
76
|
+
--brand-subtle-border: var(--color-brand-200);
|
|
77
|
+
--brand-subtle-active: var(--color-brand-200);
|
|
78
|
+
|
|
61
79
|
--background-custom: #f5f5f7;
|
|
62
80
|
--background-custom-gradient: #e0e7ee;
|
|
63
81
|
|
|
@@ -70,6 +88,29 @@
|
|
|
70
88
|
--background: var(--color-white);
|
|
71
89
|
--foreground: var(--color-zinc-900);
|
|
72
90
|
|
|
91
|
+
/* There is no `font-*` colour family here, and that is the answer rather
|
|
92
|
+
than an omission. Figma v3 carries 434 colour variables across 14
|
|
93
|
+
collections and not one of them is a font colour: text colour comes from
|
|
94
|
+
the same semantic as everything else that means it. `--font-primary` was
|
|
95
|
+
this file's second name for `--foreground`, `--font-secondary` for
|
|
96
|
+
`--muted-foreground`, and `--font-error` / `--font-success` /
|
|
97
|
+
`--font-warning` / `--font-info` forwarded to `--error` / `--success` /
|
|
98
|
+
`--warning` / `--info` — so message text could never drift from the
|
|
99
|
+
control border that shares its meaning, which is the only thing the
|
|
100
|
+
aliases bought.
|
|
101
|
+
|
|
102
|
+
`--font-tertiary` was the one with no counterpart at all. Figma deleted
|
|
103
|
+
the concept, so placeholder text, weekday headers and the scrollbar thumb
|
|
104
|
+
read `--muted-foreground` now: zinc-500 where they used to be zinc-400.
|
|
105
|
+
|
|
106
|
+
Three roles are easy to confuse, so to say which is which:
|
|
107
|
+
`--muted-foreground` is grey *text on the page background*, `--success` is
|
|
108
|
+
a green *fill*, and `--success-foreground` is white text sitting *on* that
|
|
109
|
+
fill. A green message under a field is `text-success`; a filled success
|
|
110
|
+
button is `bg-success text-success-foreground`.
|
|
111
|
+
|
|
112
|
+
`theme-v3.test.ts` fails if any of the seven comes back. */
|
|
113
|
+
|
|
73
114
|
--card: var(--color-white);
|
|
74
115
|
--card-foreground: var(--color-zinc-900);
|
|
75
116
|
|
|
@@ -80,6 +121,7 @@
|
|
|
80
121
|
--primary-foreground: var(--color-white);
|
|
81
122
|
|
|
82
123
|
--secondary: var(--color-zinc-100);
|
|
124
|
+
--secondary-hover: var(--color-zinc-200);
|
|
83
125
|
--secondary-foreground: var(--color-zinc-900);
|
|
84
126
|
|
|
85
127
|
--muted: var(--color-zinc-200);
|
|
@@ -88,10 +130,26 @@
|
|
|
88
130
|
--accent: var(--color-zinc-100);
|
|
89
131
|
--accent-foreground: var(--color-zinc-900);
|
|
90
132
|
|
|
91
|
-
--success: var(--color-green-
|
|
133
|
+
--success: var(--color-green-700);
|
|
134
|
+
--success-hover: var(--color-green-800);
|
|
92
135
|
--success-foreground: var(--color-white);
|
|
136
|
+
/* A semantic colour used as a *surface* instead of as a fill behind white
|
|
137
|
+
text: the 100 tint for the background, the 700 of the same hue for the
|
|
138
|
+
text and the icon sitting on it, which is what clears 4.5:1 there. The v3
|
|
139
|
+
board names the pair `*-subtle` / `*-subtle-foreground` and binds it on
|
|
140
|
+
Alert. `muted` already had this shape under its plain name — `--muted` is
|
|
141
|
+
the tint and `--muted-foreground` the text on it — so there is no
|
|
142
|
+
`--muted-subtle` to add. */
|
|
143
|
+
--success-subtle: var(--color-green-100);
|
|
144
|
+
--success-subtle-foreground: var(--color-green-700);
|
|
93
145
|
--warning: var(--color-amber-500);
|
|
94
|
-
|
|
146
|
+
/* Dark on amber, which looks inverted next to every other `*-foreground`
|
|
147
|
+
here, and is the only pairing that passes: white on amber-500 is 2.1:1,
|
|
148
|
+
zinc-950 on it is 9.3:1. Figma binds `warning-foreground` to zinc/950 in
|
|
149
|
+
both modes. */
|
|
150
|
+
--warning-foreground: var(--color-zinc-950);
|
|
151
|
+
--warning-subtle: var(--color-amber-100);
|
|
152
|
+
--warning-subtle-foreground: var(--color-amber-700);
|
|
95
153
|
/* The one semantic name for this red. Figma's Primitive collection calls it
|
|
96
154
|
`error`, so the code does too. There used to be a `--destructive` beside it
|
|
97
155
|
holding the identical value — two names for one colour, which means two
|
|
@@ -99,16 +157,81 @@
|
|
|
99
157
|
red. The *component* variant stays `destructive` (Button, Badge and
|
|
100
158
|
StatusBadge), which is also what Figma names that style: the action is
|
|
101
159
|
destructive, the colour is error. */
|
|
102
|
-
--error: var(--color-red-
|
|
160
|
+
--error: var(--color-red-600);
|
|
161
|
+
--error-hover: var(--color-red-700);
|
|
103
162
|
--error-foreground: var(--color-white);
|
|
104
|
-
--
|
|
163
|
+
--error-subtle: var(--color-red-100);
|
|
164
|
+
--error-subtle-foreground: var(--color-red-700);
|
|
165
|
+
/* blue-600, not the 500 most semantic colours take — same reason `--success`
|
|
166
|
+
is green-700. `--info-foreground` is white, and white on blue-500 reaches
|
|
167
|
+
only 3.8:1; on blue-600 it is 5.2:1. Figma binds `info` to blue/600. */
|
|
168
|
+
--info: var(--color-blue-600);
|
|
105
169
|
--info-foreground: var(--color-white);
|
|
170
|
+
--info-subtle: var(--color-blue-100);
|
|
171
|
+
--info-subtle-foreground: var(--color-blue-700);
|
|
172
|
+
|
|
173
|
+
/* Content that is *yours*, or that matched what you typed — a mention of you,
|
|
174
|
+
a search hit. Figma binds it to the same amber-100 as `--warning-subtle`,
|
|
175
|
+
and it is deliberately a separate name rather than an alias of it: one says
|
|
176
|
+
"look, this is about you" and the other says "careful". Re-pointing either
|
|
177
|
+
must not drag the other along. */
|
|
178
|
+
--highlight: var(--color-amber-100);
|
|
179
|
+
--highlight-hover: var(--color-amber-200);
|
|
180
|
+
--highlight-foreground: var(--color-amber-900);
|
|
106
181
|
|
|
107
182
|
--border: var(--color-zinc-200);
|
|
183
|
+
--border-strong: var(--color-zinc-300);
|
|
108
184
|
--input: var(--color-zinc-200);
|
|
109
|
-
|
|
185
|
+
/* The focus colour for every control, and the only one: `Semantic-Colors`
|
|
186
|
+
binds `ring` to brand/500 and describes it as the focus-visible ring at
|
|
187
|
+
>= 3:1 contrast in both modes.
|
|
188
|
+
|
|
189
|
+
Two geometries read it, which is why a focused Button and a focused Input
|
|
190
|
+
look different without either being drift. The Accessibility board
|
|
191
|
+
(node 1600:30) sets out the split:
|
|
192
|
+
|
|
193
|
+
- Outside and separated -- the default, and what Button, Icon button,
|
|
194
|
+
Checkbox, RadioButton and Switch draw:
|
|
195
|
+
`0 0 0 2px var(--color-background), 0 0 0 4px var(--color-ring)`,
|
|
196
|
+
which `ring-2 ring-offset-2 ring-offset-background` emits. Drawn with
|
|
197
|
+
shadows, so the control is the same size focused as at rest.
|
|
198
|
+
- Inside -- for a control that already has a border to recolour, or whose
|
|
199
|
+
pieces sit flush: every text field, Select, Combobox, InputOtp. The
|
|
200
|
+
border turns `ring`. An outside ring on a column of form fields would
|
|
201
|
+
climb over its neighbours, which the same board warns about.
|
|
202
|
+
|
|
203
|
+
Destructive and success controls swap the colour for their own and keep the
|
|
204
|
+
geometry; `OxButton`'s destructive variant is the live case. */
|
|
205
|
+
--ring: var(--color-brand-500);
|
|
206
|
+
/* Figma's value (red-200), mirrored so the scale still matches the
|
|
207
|
+
collection -- but nothing in `src` reads it. The variable's own description
|
|
208
|
+
scopes it to Horchata, and the component boards paint an invalid control's
|
|
209
|
+
focus ring with `error` instead: the Textarea board's `Error (Focus)`
|
|
210
|
+
variant (node 1096:14751) binds that ring to `error`, not to this. Do not
|
|
211
|
+
re-point it to make a component work; reach for `--error`. */
|
|
110
212
|
--ring-error: var(--color-red-200);
|
|
111
213
|
|
|
214
|
+
/* The glass behind a modal, a drawer or a sheet. Figma
|
|
215
|
+
`Semantic-Colors/overlay` (Overlay board, node 1845:51257) aliases
|
|
216
|
+
`base/alpha-60`, whose name counts the opacity of what shows *through* it
|
|
217
|
+
and whose value is therefore 0.4 — white at 40%, not 60. Paired with
|
|
218
|
+
`backdrop-blur-lg` at the call site, which is the 16px the component's
|
|
219
|
+
effect is bound to.
|
|
220
|
+
|
|
221
|
+
Figma's dark mode points the same semantic name at `base/black-alpha-40`,
|
|
222
|
+
black at 40%. There is no `.dark` block in v3 yet, so that value is
|
|
223
|
+
recorded here rather than declared, for whoever wires dark mode up. */
|
|
224
|
+
--overlay: var(--color-alpha-60);
|
|
225
|
+
|
|
226
|
+
/* The overlay a control tints itself with while it is hovered, focused or
|
|
227
|
+
pressed, drawn at 8–30% paint opacity over the colour underneath — the
|
|
228
|
+
scale Figma's `Semantic-Colors` prints in the variable's own description.
|
|
229
|
+
White is the only value it takes in either mode, because it is meant for
|
|
230
|
+
coloured surfaces: a brand-filled switch track, a chip, a filled button.
|
|
231
|
+
Over a near-white surface it has nothing to lighten, which is a reason to
|
|
232
|
+
pick a darker surface rather than a reason to tint it a different colour. */
|
|
233
|
+
--state-layer: var(--color-white);
|
|
234
|
+
|
|
112
235
|
--chart-1: var(--color-brand-400);
|
|
113
236
|
--chart-2: var(--color-brand-500);
|
|
114
237
|
--chart-3: var(--color-brand-600);
|
|
@@ -123,7 +246,7 @@
|
|
|
123
246
|
--category-3: var(--color-amber-500); /* amber */
|
|
124
247
|
--category-4: var(--color-teal-500); /* teal */
|
|
125
248
|
--category-5: var(--color-orange-500); /* orange */
|
|
126
|
-
--category-6: var(--color-zinc-
|
|
249
|
+
--category-6: var(--color-zinc-700); /* zinc */
|
|
127
250
|
--category-7: var(--color-red-500); /* red */
|
|
128
251
|
--category-8: var(--color-yellow-500); /* yellow */
|
|
129
252
|
--category-9: var(--color-lime-500); /* lime */
|
|
@@ -154,13 +277,6 @@
|
|
|
154
277
|
emission: without it Tailwind only declares the variables some generated
|
|
155
278
|
utility happens to mention. Kept in step with the Vue package's copy. */
|
|
156
279
|
@theme static inline {
|
|
157
|
-
--color-font-primary: var(--font-primary);
|
|
158
|
-
--color-font-secondary: var(--font-secondary);
|
|
159
|
-
--color-font-tertiary: var(--font-tertiary);
|
|
160
|
-
--color-font-error: var(--font-error);
|
|
161
|
-
--color-font-success: var(--font-success);
|
|
162
|
-
--color-font-warning: var(--font-warning);
|
|
163
|
-
--color-font-info: var(--font-info);
|
|
164
280
|
--color-border-default: var(--border-default);
|
|
165
281
|
--color-border-hover: var(--border-hover);
|
|
166
282
|
--color-bg-base: var(--bg-base);
|
|
@@ -180,8 +296,16 @@
|
|
|
180
296
|
--color-neutral-950: var(--neutral-950);
|
|
181
297
|
--color-brand: var(--brand);
|
|
182
298
|
--color-brand-hover: var(--brand-hover);
|
|
299
|
+
--color-brand-active: var(--brand-active);
|
|
183
300
|
--color-brand-foreground: var(--brand-foreground);
|
|
301
|
+
--color-brand-subtle: var(--brand-subtle);
|
|
302
|
+
--color-brand-subtle-foreground: var(--brand-subtle-foreground);
|
|
303
|
+
--color-brand-subtle-border: var(--brand-subtle-border);
|
|
304
|
+
--color-brand-subtle-active: var(--brand-subtle-active);
|
|
184
305
|
--color-success: var(--success);
|
|
306
|
+
--color-success-hover: var(--success-hover);
|
|
307
|
+
--color-success-subtle: var(--success-subtle);
|
|
308
|
+
--color-success-subtle-foreground: var(--success-subtle-foreground);
|
|
185
309
|
--color-success-foreground: var(--success-foreground);
|
|
186
310
|
--color-success-100: var(--success-100);
|
|
187
311
|
--color-success-300: var(--success-300);
|
|
@@ -202,6 +326,7 @@
|
|
|
202
326
|
--color-primary: var(--primary);
|
|
203
327
|
--color-primary-foreground: var(--primary-foreground);
|
|
204
328
|
--color-secondary: var(--secondary);
|
|
329
|
+
--color-secondary-hover: var(--secondary-hover);
|
|
205
330
|
--color-secondary-foreground: var(--secondary-foreground);
|
|
206
331
|
--color-muted: var(--muted);
|
|
207
332
|
--color-muted-foreground: var(--muted-foreground);
|
|
@@ -209,14 +334,27 @@
|
|
|
209
334
|
--color-accent-foreground: var(--accent-foreground);
|
|
210
335
|
--color-warning: var(--warning);
|
|
211
336
|
--color-warning-foreground: var(--warning-foreground);
|
|
337
|
+
--color-warning-subtle: var(--warning-subtle);
|
|
338
|
+
--color-warning-subtle-foreground: var(--warning-subtle-foreground);
|
|
212
339
|
--color-error: var(--error);
|
|
340
|
+
--color-error-hover: var(--error-hover);
|
|
213
341
|
--color-error-foreground: var(--error-foreground);
|
|
342
|
+
--color-error-subtle: var(--error-subtle);
|
|
343
|
+
--color-error-subtle-foreground: var(--error-subtle-foreground);
|
|
214
344
|
--color-info: var(--info);
|
|
215
345
|
--color-info-foreground: var(--info-foreground);
|
|
346
|
+
--color-info-subtle: var(--info-subtle);
|
|
347
|
+
--color-info-subtle-foreground: var(--info-subtle-foreground);
|
|
348
|
+
--color-highlight: var(--highlight);
|
|
349
|
+
--color-highlight-hover: var(--highlight-hover);
|
|
350
|
+
--color-highlight-foreground: var(--highlight-foreground);
|
|
216
351
|
--color-border: var(--border);
|
|
352
|
+
--color-border-strong: var(--border-strong);
|
|
217
353
|
--color-input: var(--input);
|
|
218
354
|
--color-ring: var(--ring);
|
|
219
355
|
--color-ring-error: var(--ring-error);
|
|
356
|
+
--color-state-layer: var(--state-layer);
|
|
357
|
+
--color-overlay: var(--overlay);
|
|
220
358
|
--color-chart-1: var(--chart-1);
|
|
221
359
|
--color-chart-2: var(--chart-2);
|
|
222
360
|
--color-chart-3: var(--chart-3);
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* jsdom and happy-dom apply no CSS, so a computed colour here is always empty
|
|
8
|
+
* and a token that resolves to nothing looks exactly like one that resolves.
|
|
9
|
+
* The stylesheet text is the only thing that can be pinned, so it is what these
|
|
10
|
+
* assert — the same approach as `internal/Listbox/ListboxItem.test.tsx`.
|
|
11
|
+
*
|
|
12
|
+
* Two halves have to hold together for a token to be usable at all, and each
|
|
13
|
+
* fails silently without the other:
|
|
14
|
+
*
|
|
15
|
+
* - the `:root` declaration, which gives it a value;
|
|
16
|
+
* - the `@theme static inline` entry, which is what makes Tailwind emit a
|
|
17
|
+
* `--color-*` custom property and a `bg-*` / `text-*` / `border-*` utility
|
|
18
|
+
* for it. Declare only the first and `@apply bg-brand-subtle` is an unknown
|
|
19
|
+
* utility; expose only the second and the utility paints with nothing.
|
|
20
|
+
*/
|
|
21
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const theme = readFileSync(join(here, 'theme-v3.css'), 'utf8');
|
|
23
|
+
const src = join(here, '..', '..');
|
|
24
|
+
|
|
25
|
+
/** `--name: value;`, whitespace flattened, comments irrelevant. */
|
|
26
|
+
const declares = (name: string, value: string) =>
|
|
27
|
+
new RegExp(
|
|
28
|
+
`^\\s*${name}:\\s*${value.replace(/[()\-*]/g, '\\$&')};`,
|
|
29
|
+
'm'
|
|
30
|
+
).test(theme);
|
|
31
|
+
|
|
32
|
+
/** The `@theme static inline` block, which is the last one in the file. */
|
|
33
|
+
const themeBlock = theme.slice(theme.indexOf('@theme static inline'));
|
|
34
|
+
|
|
35
|
+
/** `--brand-subtle` is exposed as `--color-brand-subtle`, so drop one dash. */
|
|
36
|
+
const exposes = (name: string) =>
|
|
37
|
+
themeBlock.includes(`--color${name.slice(1)}: var(${name});`);
|
|
38
|
+
|
|
39
|
+
describe('theme-v3 brand-subtle', () => {
|
|
40
|
+
const family = {
|
|
41
|
+
'--brand-subtle': 'var(--color-brand-100)',
|
|
42
|
+
'--brand-subtle-foreground': 'var(--color-brand-700)',
|
|
43
|
+
'--brand-subtle-border': 'var(--color-brand-200)',
|
|
44
|
+
'--brand-subtle-active': 'var(--color-brand-200)',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
it.each(Object.entries(family))(
|
|
48
|
+
'should declare %s as the Figma binding %s',
|
|
49
|
+
(name, value) => {
|
|
50
|
+
expect(declares(name, value)).toBe(true);
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
it.each(Object.keys(family))('should expose %s as a utility', name => {
|
|
55
|
+
expect(exposes(name)).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('theme-v3 highlight', () => {
|
|
60
|
+
const family = {
|
|
61
|
+
'--highlight': 'var(--color-amber-100)',
|
|
62
|
+
'--highlight-hover': 'var(--color-amber-200)',
|
|
63
|
+
'--highlight-foreground': 'var(--color-amber-900)',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
it.each(Object.entries(family))(
|
|
67
|
+
'should declare %s as the Figma binding %s',
|
|
68
|
+
(name, value) => {
|
|
69
|
+
expect(declares(name, value)).toBe(true);
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
it.each(Object.keys(family))('should expose %s as a utility', name => {
|
|
74
|
+
expect(exposes(name)).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* `--highlight` and `--warning-subtle` are both amber-100 in the v3 file, so
|
|
79
|
+
* one looks like a redundant copy of the other. They are not: "this is about
|
|
80
|
+
* you" and "careful" are different questions and Figma names them
|
|
81
|
+
* separately, so each has to keep its own declaration rather than alias the
|
|
82
|
+
* other — otherwise repainting one silently repaints the other.
|
|
83
|
+
*/
|
|
84
|
+
it('should keep --highlight independent of --warning-subtle', () => {
|
|
85
|
+
expect(declares('--warning-subtle', 'var(--color-amber-100)')).toBe(true);
|
|
86
|
+
expect(theme).not.toContain('--highlight: var(--warning-subtle)');
|
|
87
|
+
expect(theme).not.toContain('--warning-subtle: var(--highlight)');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Every one of these reached for a ramp step directly and carried a comment
|
|
93
|
+
* naming the token it was standing in for. The tokens exist now, so the comment
|
|
94
|
+
* is gone and the call site has to be the semantic name — and must not quietly
|
|
95
|
+
* drift back, which is the only thing a reader of this file would not notice.
|
|
96
|
+
*/
|
|
97
|
+
describe('theme-v3 semantic call sites', () => {
|
|
98
|
+
const callSites: [string, string[], string[]][] = [
|
|
99
|
+
[
|
|
100
|
+
'Display/Tag/Tag.module.css',
|
|
101
|
+
['var(--color-brand-subtle)'],
|
|
102
|
+
['var(--color-brand-100)'],
|
|
103
|
+
],
|
|
104
|
+
[
|
|
105
|
+
'Navigation/Stepper/Stepper.module.css',
|
|
106
|
+
['bg-brand-subtle'],
|
|
107
|
+
['bg-brand-100'],
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
'Navigation/Stepper/StepperItem.module.css',
|
|
111
|
+
['bg-brand-subtle', 'text-brand-subtle-foreground'],
|
|
112
|
+
['bg-brand-100', 'text-brand-700'],
|
|
113
|
+
],
|
|
114
|
+
[
|
|
115
|
+
'Navigation/Stepper/StepperSubsteps.module.css',
|
|
116
|
+
['border-brand-subtle-border'],
|
|
117
|
+
['border-brand-200'],
|
|
118
|
+
],
|
|
119
|
+
[
|
|
120
|
+
'Display/Mention/Mention.module.css',
|
|
121
|
+
['bg-brand-subtle', 'bg-highlight', 'text-highlight-foreground'],
|
|
122
|
+
['bg-amber-100', 'text-amber-900', 'var(--color-amber-200)'],
|
|
123
|
+
],
|
|
124
|
+
[
|
|
125
|
+
'Forms/Inputs/IconPicker/IconPickerItem.module.css',
|
|
126
|
+
['bg-brand-subtle'],
|
|
127
|
+
['bg-brand-100'],
|
|
128
|
+
],
|
|
129
|
+
[
|
|
130
|
+
'Button/Button.module.css',
|
|
131
|
+
['var(--color-brand-subtle)'],
|
|
132
|
+
['var(--color-brand-100)'],
|
|
133
|
+
],
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
it.each(callSites)('should use the token in %s', (file, wanted, retired) => {
|
|
137
|
+
const css = readFileSync(join(src, file), 'utf8');
|
|
138
|
+
for (const token of wanted) expect(css).toContain(token);
|
|
139
|
+
for (const stale of retired) expect(css).not.toContain(stale);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The `font-*` colour family is retired. Six of the seven were a second name
|
|
145
|
+
* for a semantic this file already declares — `--font-primary` for
|
|
146
|
+
* `--foreground`, `--font-secondary` for `--muted-foreground`, and the
|
|
147
|
+
* feedback four forwarding to `--error` / `--success` / `--warning` /
|
|
148
|
+
* `--info` — and `--font-tertiary` had no Figma counterpart at all, so its
|
|
149
|
+
* call sites took `--muted-foreground` and went one shade darker.
|
|
150
|
+
*
|
|
151
|
+
* Half of this is self-policing and half is not, which is why the two halves
|
|
152
|
+
* are asserted separately:
|
|
153
|
+
*
|
|
154
|
+
* - Taking a name out of `@theme static inline` makes `@apply text-font-primary`
|
|
155
|
+
* an unknown utility, and the build says so. Putting the entry *back* is the
|
|
156
|
+
* silent move: the utility starts resolving again, under a name Figma does
|
|
157
|
+
* not have, and nothing else would complain.
|
|
158
|
+
* - A bare `:root` declaration with no `@theme` entry is invisible both ways —
|
|
159
|
+
* no utility, no error, just a variable nobody reads.
|
|
160
|
+
*/
|
|
161
|
+
describe('theme-v3 retired font-* family', () => {
|
|
162
|
+
const retired = [
|
|
163
|
+
'--font-primary',
|
|
164
|
+
'--font-secondary',
|
|
165
|
+
'--font-tertiary',
|
|
166
|
+
'--font-error',
|
|
167
|
+
'--font-success',
|
|
168
|
+
'--font-warning',
|
|
169
|
+
'--font-info',
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
it.each(retired)('should not declare %s', name => {
|
|
173
|
+
expect(declares(name, '[^;]+')).toBe(false);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it.each(retired)('should not expose %s as a utility', name => {
|
|
177
|
+
expect(themeBlock).not.toContain(`--color${name.slice(1)}:`);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* `--font-sans`, `--font-mono` and `--font-weight-*` share the prefix and are
|
|
182
|
+
* Tailwind's own font-family and weight tokens. Nothing above should have
|
|
183
|
+
* taken them with it.
|
|
184
|
+
*/
|
|
185
|
+
it('should keep the font-family tokens, which are a different family', () => {
|
|
186
|
+
expect(
|
|
187
|
+
theme.includes('--font-sans') || themeBlock.includes('--font-sans')
|
|
188
|
+
).toBe(true);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* One red, and `--error` is where it lives. Figma's `Semantic-Colors` binds
|
|
194
|
+
* `error` to red/600 and has no second red for the same meaning, so another
|
|
195
|
+
* name holding the same value could only ever be drift — `--destructive` was
|
|
196
|
+
* collapsed into this one for exactly that reason, and `--font-error` held
|
|
197
|
+
* red-500 against it until the whole `font-*` family was retired, which is why
|
|
198
|
+
* `OxFieldGroup` drew a message and a border in two different reds.
|
|
199
|
+
*/
|
|
200
|
+
describe('theme-v3 error family', () => {
|
|
201
|
+
it('should give the error family a single red', () => {
|
|
202
|
+
expect(declares('--error', 'var(--color-red-600)')).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should not reintroduce a second name holding the same red', () => {
|
|
206
|
+
expect(declares('--destructive', '[^;]+')).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Figma's value, mirrored for parity, and deliberately unread: the component
|
|
211
|
+
* boards paint an invalid control's focus ring with `error` instead. Pinned
|
|
212
|
+
* so re-pointing it looks like the change it is.
|
|
213
|
+
*/
|
|
214
|
+
it('should keep ring-error at the value the collection holds', () => {
|
|
215
|
+
expect(declares('--ring-error', 'var(--color-red-200)')).toBe(true);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* `--ring` is the focus colour for every control and the only one, which is
|
|
221
|
+
* what makes it the override a consumer re-points to restyle focus. A focus
|
|
222
|
+
* state spelled `brand` resolves to the same value today and stops tracking the
|
|
223
|
+
* moment that happens.
|
|
224
|
+
*/
|
|
225
|
+
describe('theme-v3 focus ring', () => {
|
|
226
|
+
it('should bind the focus ring to the brand colour', () => {
|
|
227
|
+
expect(declares('--ring', 'var(--color-brand-500)')).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it.each([
|
|
231
|
+
'--error',
|
|
232
|
+
'--error-hover',
|
|
233
|
+
'--error-foreground',
|
|
234
|
+
'--error-subtle',
|
|
235
|
+
'--error-subtle-foreground',
|
|
236
|
+
'--ring',
|
|
237
|
+
'--ring-error',
|
|
238
|
+
])('should expose %s as a utility as well as declaring it', name => {
|
|
239
|
+
expect(declares(name, '[^;]+')).toBe(true);
|
|
240
|
+
expect(exposes(name)).toBe(true);
|
|
241
|
+
});
|
|
242
|
+
});
|