@graundtech/fluent2-react-kit 0.5.1
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/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/components/ui/accordion.d.ts +133 -0
- package/dist/components/ui/accordion.js +80 -0
- package/dist/components/ui/accordion.js.map +1 -0
- package/dist/components/ui/alert.d.ts +106 -0
- package/dist/components/ui/alert.js +73 -0
- package/dist/components/ui/alert.js.map +1 -0
- package/dist/components/ui/avatar.d.ts +42 -0
- package/dist/components/ui/avatar.js +55 -0
- package/dist/components/ui/avatar.js.map +1 -0
- package/dist/components/ui/badge.d.ts +36 -0
- package/dist/components/ui/badge.js +81 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/breadcrumb.d.ts +53 -0
- package/dist/components/ui/breadcrumb.js +149 -0
- package/dist/components/ui/breadcrumb.js.map +1 -0
- package/dist/components/ui/button.d.ts +24 -0
- package/dist/components/ui/button.js +86 -0
- package/dist/components/ui/button.js.map +1 -0
- package/dist/components/ui/card.d.ts +51 -0
- package/dist/components/ui/card.js +97 -0
- package/dist/components/ui/card.js.map +1 -0
- package/dist/components/ui/checkbox.d.ts +101 -0
- package/dist/components/ui/checkbox.js +57 -0
- package/dist/components/ui/checkbox.js.map +1 -0
- package/dist/components/ui/combobox.d.ts +212 -0
- package/dist/components/ui/combobox.js +236 -0
- package/dist/components/ui/combobox.js.map +1 -0
- package/dist/components/ui/command.d.ts +187 -0
- package/dist/components/ui/command.js +225 -0
- package/dist/components/ui/command.js.map +1 -0
- package/dist/components/ui/dialog.d.ts +117 -0
- package/dist/components/ui/dialog.js +165 -0
- package/dist/components/ui/dialog.js.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +155 -0
- package/dist/components/ui/dropdown-menu.js +295 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -0
- package/dist/components/ui/input.d.ts +44 -0
- package/dist/components/ui/input.js +43 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/label.d.ts +39 -0
- package/dist/components/ui/label.js +30 -0
- package/dist/components/ui/label.js.map +1 -0
- package/dist/components/ui/link.d.ts +62 -0
- package/dist/components/ui/link.js +61 -0
- package/dist/components/ui/link.js.map +1 -0
- package/dist/components/ui/multi-select.d.ts +115 -0
- package/dist/components/ui/multi-select.js +133 -0
- package/dist/components/ui/multi-select.js.map +1 -0
- package/dist/components/ui/pagination.d.ts +46 -0
- package/dist/components/ui/pagination.js +164 -0
- package/dist/components/ui/pagination.js.map +1 -0
- package/dist/components/ui/popover.d.ts +117 -0
- package/dist/components/ui/popover.js +62 -0
- package/dist/components/ui/popover.js.map +1 -0
- package/dist/components/ui/progress.d.ts +98 -0
- package/dist/components/ui/progress.js +67 -0
- package/dist/components/ui/progress.js.map +1 -0
- package/dist/components/ui/radio-group.d.ts +94 -0
- package/dist/components/ui/radio-group.js +65 -0
- package/dist/components/ui/radio-group.js.map +1 -0
- package/dist/components/ui/select.d.ts +145 -0
- package/dist/components/ui/select.js +240 -0
- package/dist/components/ui/select.js.map +1 -0
- package/dist/components/ui/separator.d.ts +43 -0
- package/dist/components/ui/separator.js +27 -0
- package/dist/components/ui/separator.js.map +1 -0
- package/dist/components/ui/skeleton.d.ts +81 -0
- package/dist/components/ui/skeleton.js +24 -0
- package/dist/components/ui/skeleton.js.map +1 -0
- package/dist/components/ui/spinner.d.ts +56 -0
- package/dist/components/ui/spinner.js +83 -0
- package/dist/components/ui/spinner.js.map +1 -0
- package/dist/components/ui/switch.d.ts +60 -0
- package/dist/components/ui/switch.js +63 -0
- package/dist/components/ui/switch.js.map +1 -0
- package/dist/components/ui/tabs.d.ts +107 -0
- package/dist/components/ui/tabs.js +102 -0
- package/dist/components/ui/tabs.js.map +1 -0
- package/dist/components/ui/textarea.d.ts +28 -0
- package/dist/components/ui/textarea.js +36 -0
- package/dist/components/ui/textarea.js.map +1 -0
- package/dist/components/ui/toast.d.ts +185 -0
- package/dist/components/ui/toast.js +181 -0
- package/dist/components/ui/toast.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +115 -0
- package/dist/components/ui/tooltip.js +72 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +284 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.d.ts +9 -0
- package/dist/lib/utils.js +9 -0
- package/dist/lib/utils.js.map +1 -0
- package/package.json +77 -0
- package/src/styles/tokens.css +596 -0
- package/src/styles/tokens.test.ts +216 -0
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
/* -------------------------------------------------------------------------- */
|
|
2
|
+
/* @graundtech/fluent2-react-kit — design tokens */
|
|
3
|
+
/* */
|
|
4
|
+
/* Fluent 2-inspired token layer expressed in the shadcn/ui variable */
|
|
5
|
+
/* contract (CSS custom properties + Tailwind v4 utilities). Values are */
|
|
6
|
+
/* derived from publicly documented Fluent 2 design constants; hex is the */
|
|
7
|
+
/* source of truth. See docs/design/tokens-research.md for provenance. */
|
|
8
|
+
/* */
|
|
9
|
+
/* Consumers own `@import "tailwindcss";` — this file must NOT import it. */
|
|
10
|
+
/* Import order in the consumer app: */
|
|
11
|
+
/* @import "tailwindcss"; */
|
|
12
|
+
/* @import "@graundtech/fluent2-react-kit/tokens.css"; */
|
|
13
|
+
/* -------------------------------------------------------------------------- */
|
|
14
|
+
|
|
15
|
+
/* shadcn v4 dark-mode convention: `.dark` on any ancestor enables dark.
|
|
16
|
+
The `:not(.light *)` guard stops `dark:` utilities from firing inside a
|
|
17
|
+
`.light` scope that sits under a `.dark` ancestor — the `.light` re-scope
|
|
18
|
+
class (see the light theme block below) forces light mode locally, so its
|
|
19
|
+
subtree must not keep emitting dark-mode overrides. */
|
|
20
|
+
@custom-variant dark (&:is(.dark *):not(.light *));
|
|
21
|
+
|
|
22
|
+
/* ========================================================================== */
|
|
23
|
+
/* LIGHT theme — the default. All values authored in HEX. */
|
|
24
|
+
/* ========================================================================== */
|
|
25
|
+
/* `:root, .light` — `.light` re-scopes light mode onto any subtree, letting a
|
|
26
|
+
consumer/demo force light inside a `.dark` ancestor. It carries the same
|
|
27
|
+
declarations as `:root` (zero duplication), and because an element's own
|
|
28
|
+
variable declarations beat inherited ones, a `.light` block overrides the
|
|
29
|
+
dark values it inherits. The dark `@custom-variant` above is guarded so
|
|
30
|
+
`dark:` utilities don't fire inside this scope. */
|
|
31
|
+
:root,
|
|
32
|
+
.light {
|
|
33
|
+
/* Native UA chrome (form controls, scrollbars, spellcheck underlines) reads
|
|
34
|
+
`color-scheme` to pick its light/dark rendering — set it at the token
|
|
35
|
+
layer so it tracks the theme without a separate consumer opt-in. */
|
|
36
|
+
color-scheme: light;
|
|
37
|
+
|
|
38
|
+
/* --- radius base (Fluent "large" = 6px; drives the rounded-* scale) ----- */
|
|
39
|
+
--radius: 6px;
|
|
40
|
+
|
|
41
|
+
/* --- shadcn core set (light) ------------------------------------------- */
|
|
42
|
+
--background: #ffffff;
|
|
43
|
+
--foreground: #242424;
|
|
44
|
+
--card: #ffffff;
|
|
45
|
+
--card-foreground: #242424;
|
|
46
|
+
--popover: #ffffff;
|
|
47
|
+
--popover-foreground: #242424;
|
|
48
|
+
--primary: #0f6cbd; /* brand[80] */
|
|
49
|
+
--primary-foreground: #ffffff;
|
|
50
|
+
--secondary: #f5f5f5; /* grey[96] */
|
|
51
|
+
--secondary-foreground: #242424;
|
|
52
|
+
--muted: #f5f5f5; /* grey[96] */
|
|
53
|
+
--muted-foreground: #616161; /* grey[38] */
|
|
54
|
+
--accent: #f0f0f0; /* grey[94] — neutral (brand tint lives on sidebar-accent) */
|
|
55
|
+
--accent-foreground: #242424;
|
|
56
|
+
--destructive: #d13438; /* red primary */
|
|
57
|
+
--destructive-foreground: #ffffff;
|
|
58
|
+
--border: #d1d1d1; /* grey[82] */
|
|
59
|
+
--input: #d1d1d1; /* grey[82] */
|
|
60
|
+
--ring: #0f6cbd; /* brand[80] */
|
|
61
|
+
|
|
62
|
+
/* --- chart palette ------------------------------------------------------ */
|
|
63
|
+
--chart-1: #0f6cbd; /* brand blue */
|
|
64
|
+
--chart-2: #038387; /* teal */
|
|
65
|
+
--chart-3: #107c10; /* green */
|
|
66
|
+
--chart-4: #da3b01; /* darkOrange */
|
|
67
|
+
--chart-5: #5c2e91; /* purple */
|
|
68
|
+
|
|
69
|
+
/* --- sidebar ------------------------------------------------------------ */
|
|
70
|
+
--sidebar: #fafafa; /* grey[98] */
|
|
71
|
+
--sidebar-foreground: #242424;
|
|
72
|
+
--sidebar-primary: #0f6cbd;
|
|
73
|
+
--sidebar-primary-foreground: #ffffff;
|
|
74
|
+
--sidebar-accent: #ebf3fc; /* brand[160] — Fluent "selected nav" tint */
|
|
75
|
+
--sidebar-accent-foreground: #115ea3; /* brand[70] */
|
|
76
|
+
--sidebar-border: #e0e0e0; /* grey[88] */
|
|
77
|
+
--sidebar-ring: #0f6cbd;
|
|
78
|
+
|
|
79
|
+
/* --- brand ramp (full 16 stops; buttons need hover/pressed/selected) ---- */
|
|
80
|
+
--brand-10: #061724;
|
|
81
|
+
--brand-20: #082338;
|
|
82
|
+
--brand-30: #0a2e4a;
|
|
83
|
+
--brand-40: #0c3b5e;
|
|
84
|
+
--brand-50: #0e4775;
|
|
85
|
+
--brand-60: #0f548c;
|
|
86
|
+
--brand-70: #115ea3;
|
|
87
|
+
--brand-80: #0f6cbd;
|
|
88
|
+
--brand-90: #2886de;
|
|
89
|
+
--brand-100: #479ef5;
|
|
90
|
+
--brand-110: #62abf5;
|
|
91
|
+
--brand-120: #77b7f7;
|
|
92
|
+
--brand-130: #96c6fa;
|
|
93
|
+
--brand-140: #b4d6fa;
|
|
94
|
+
--brand-150: #cfe4fa;
|
|
95
|
+
--brand-160: #ebf3fc;
|
|
96
|
+
|
|
97
|
+
/* --- status extensions (shadcn ships only --destructive) ---------------- */
|
|
98
|
+
--success: #107c10;
|
|
99
|
+
--success-foreground: #ffffff;
|
|
100
|
+
--success-subtle: #f1faf1;
|
|
101
|
+
--success-border: #9fd89f;
|
|
102
|
+
--warning: #da3b01; /* Fluent darkOrange (not yellow) */
|
|
103
|
+
--warning-foreground: #ffffff;
|
|
104
|
+
--warning-text: #bc4b09; /* AA-safe warning text on light surfaces (4.74:1 on --warning-subtle) — exact Fluent Figma Message-bar value (validation pass 1 precision fix) */
|
|
105
|
+
--warning-subtle: #fdf6f3;
|
|
106
|
+
--warning-border: #f4bfab;
|
|
107
|
+
--destructive-subtle: #fdf6f6;
|
|
108
|
+
--destructive-border: #f1bbbc;
|
|
109
|
+
--destructive-text: #b10e1c; /* AA-safe destructive text on light surfaces (Fluent red foreground) */
|
|
110
|
+
|
|
111
|
+
/* --- second-tier neutral foreground (Fluent NeutralForeground2) ---------
|
|
112
|
+
One grey step lighter than --foreground (#242424), one darker than
|
|
113
|
+
--muted-foreground (#616161). Fluent uses it for secondary-but-still-
|
|
114
|
+
primary-ish text: menu section headers, breadcrumb trail items, list
|
|
115
|
+
item rest text. Added in the v0.3.0/v0.4.0 Figma validation pass, where
|
|
116
|
+
three independent components resolved to this exact value. */
|
|
117
|
+
--foreground-2: #424242; /* NeutralForeground2.Rest (grey[26]) */
|
|
118
|
+
|
|
119
|
+
/* --- accessible interactive stroke (Fluent NeutralStrokeAccessible ramp) -
|
|
120
|
+
The signature outline for interactive controls: checkbox/radio/switch
|
|
121
|
+
rest borders and the input/select resting bottom edge. Kept as a SEPARATE
|
|
122
|
+
alias from --muted-foreground for stroke semantics, even though the light
|
|
123
|
+
rest value is the same hex. Rest -> Hover -> Pressed darkens (more
|
|
124
|
+
contrast on interaction) to mirror Fluent's Rest/Hover/Pressed states. */
|
|
125
|
+
--stroke-accessible: #616161; /* NeutralStrokeAccessible.Rest (= --muted-foreground) */
|
|
126
|
+
--stroke-accessible-hover: #575757; /* NeutralStrokeAccessible.Hover */
|
|
127
|
+
--stroke-accessible-pressed: #4d4d4d; /* NeutralStrokeAccessible.Pressed */
|
|
128
|
+
|
|
129
|
+
/* --- badge warning (Fluent bright-orange chip) --------------------------
|
|
130
|
+
Fluent's Badge Warning fill is a brighter orange than --warning
|
|
131
|
+
(darkOrange) and pairs with dark static text — the opposite foreground
|
|
132
|
+
polarity from --warning's white text. Both are Fluent Static tokens
|
|
133
|
+
(WarningBackground3 / NeutralForeground1Static): #242424 on #f7630c is
|
|
134
|
+
~4.98:1, clearing AA. A dedicated pair (not --warning) so Alert's
|
|
135
|
+
darkOrange fill stays put. See docs/design/tokens-research.md §12.9. */
|
|
136
|
+
--warning-badge: #f7630c;
|
|
137
|
+
--warning-badge-foreground: #242424;
|
|
138
|
+
|
|
139
|
+
/* --- elevation / shadows ------------------------------------------------ */
|
|
140
|
+
--shadow-ambient: rgba(0, 0, 0, 0.12);
|
|
141
|
+
--shadow-key: rgba(0, 0, 0, 0.14);
|
|
142
|
+
--shadow-brand-ambient: rgba(15, 108, 189, 0.30);
|
|
143
|
+
--shadow-brand-key: rgba(15, 108, 189, 0.25);
|
|
144
|
+
--shadow-2: 0 0 2px var(--shadow-ambient), 0 1px 2px var(--shadow-key);
|
|
145
|
+
--shadow-4: 0 0 2px var(--shadow-ambient), 0 2px 4px var(--shadow-key);
|
|
146
|
+
--shadow-8: 0 0 2px var(--shadow-ambient), 0 4px 8px var(--shadow-key);
|
|
147
|
+
--shadow-16: 0 0 2px var(--shadow-ambient), 0 8px 16px var(--shadow-key);
|
|
148
|
+
--shadow-28: 0 0 8px var(--shadow-ambient), 0 14px 28px var(--shadow-key);
|
|
149
|
+
--shadow-64: 0 0 8px var(--shadow-ambient), 0 32px 64px var(--shadow-key);
|
|
150
|
+
|
|
151
|
+
/* --- typography: font stacks -------------------------------------------- */
|
|
152
|
+
--font-sans: "Segoe UI", -apple-system, BlinkMacSystemFont, "Roboto",
|
|
153
|
+
"Helvetica Neue", sans-serif;
|
|
154
|
+
--font-mono: "Cascadia Code", Consolas, "Courier New", Courier, monospace;
|
|
155
|
+
--font-numeric: Bahnschrift, "Segoe UI", -apple-system, sans-serif;
|
|
156
|
+
|
|
157
|
+
/* --- typography: weights ------------------------------------------------ */
|
|
158
|
+
--font-weight-regular: 400;
|
|
159
|
+
--font-weight-medium: 500;
|
|
160
|
+
--font-weight-semibold: 600;
|
|
161
|
+
--font-weight-bold: 700;
|
|
162
|
+
|
|
163
|
+
/* --- stroke widths ------------------------------------------------------ */
|
|
164
|
+
--stroke-thin: 1px;
|
|
165
|
+
--stroke-thick: 2px;
|
|
166
|
+
--stroke-thicker: 3px;
|
|
167
|
+
--stroke-thickest: 4px;
|
|
168
|
+
|
|
169
|
+
/* --- focus (Fluent-authentic double-stroke variant) --------------------- */
|
|
170
|
+
--stroke-focus-outer: #000000;
|
|
171
|
+
--stroke-focus-inner: #ffffff;
|
|
172
|
+
|
|
173
|
+
/* --- motion: durations -------------------------------------------------- */
|
|
174
|
+
--duration-ultra-fast: 50ms;
|
|
175
|
+
--duration-faster: 100ms;
|
|
176
|
+
--duration-fast: 150ms;
|
|
177
|
+
--duration-normal: 200ms;
|
|
178
|
+
--duration-gentle: 250ms;
|
|
179
|
+
--duration-slow: 300ms;
|
|
180
|
+
--duration-slower: 400ms;
|
|
181
|
+
--duration-ultra-slow: 500ms;
|
|
182
|
+
|
|
183
|
+
/* --- motion: easing curves ---------------------------------------------- */
|
|
184
|
+
--ease-accelerate-max: cubic-bezier(0.9, 0.1, 1, 0.2);
|
|
185
|
+
--ease-accelerate-mid: cubic-bezier(1, 0, 1, 1);
|
|
186
|
+
--ease-accelerate-min: cubic-bezier(0.8, 0, 0.78, 1);
|
|
187
|
+
--ease-decelerate-max: cubic-bezier(0.1, 0.9, 0.2, 1);
|
|
188
|
+
--ease-decelerate-mid: cubic-bezier(0, 0, 0, 1);
|
|
189
|
+
--ease-decelerate-min: cubic-bezier(0.33, 0, 0.1, 1);
|
|
190
|
+
--ease-max: cubic-bezier(0.8, 0, 0.2, 1);
|
|
191
|
+
--ease-ease: cubic-bezier(0.33, 0, 0.67, 1);
|
|
192
|
+
--ease-linear: cubic-bezier(0, 0, 1, 1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* ========================================================================== */
|
|
196
|
+
/* DARK theme — re-points aliases at Fluent's published dark slots. */
|
|
197
|
+
/* (Not a hex inversion.) Brand ramp is global and unchanged. */
|
|
198
|
+
/* ========================================================================== */
|
|
199
|
+
.dark {
|
|
200
|
+
/* Match native UA chrome to the dark surface (see :root note). */
|
|
201
|
+
color-scheme: dark;
|
|
202
|
+
|
|
203
|
+
/* --- shadcn core set (dark) — tonal elevation delta --------------------- */
|
|
204
|
+
--background: #242424; /* grey[14] canvas */
|
|
205
|
+
--foreground: #ffffff;
|
|
206
|
+
--card: #292929; /* grey[16] — raised above canvas */
|
|
207
|
+
--card-foreground: #ffffff;
|
|
208
|
+
--popover: #292929;
|
|
209
|
+
--popover-foreground: #ffffff;
|
|
210
|
+
--primary: #115ea3; /* brand[70] — Fluent dark filled-brand */
|
|
211
|
+
--primary-foreground: #ffffff;
|
|
212
|
+
--secondary: #333333; /* grey[20] */
|
|
213
|
+
--secondary-foreground: #ffffff;
|
|
214
|
+
--muted: #1f1f1f; /* grey[12] */
|
|
215
|
+
--muted-foreground: #adadad; /* grey[68] */
|
|
216
|
+
--accent: #333333; /* grey[20] */
|
|
217
|
+
--accent-foreground: #ffffff;
|
|
218
|
+
--destructive: #d13438; /* red primary (kept) */
|
|
219
|
+
--destructive-foreground: #ffffff;
|
|
220
|
+
--border: #525252; /* grey[32] */
|
|
221
|
+
--input: #525252; /* grey[32] */
|
|
222
|
+
--ring: #479ef5; /* brand[100] */
|
|
223
|
+
|
|
224
|
+
/* --- chart palette (lightened for dark surfaces) ------------------------ */
|
|
225
|
+
--chart-1: #479ef5;
|
|
226
|
+
--chart-2: #4bb6b9;
|
|
227
|
+
--chart-3: #54b054;
|
|
228
|
+
--chart-4: #ff8f6b;
|
|
229
|
+
--chart-5: #a97fd6;
|
|
230
|
+
|
|
231
|
+
/* --- sidebar ------------------------------------------------------------ */
|
|
232
|
+
--sidebar: #1f1f1f; /* grey[12] */
|
|
233
|
+
--sidebar-foreground: #ffffff;
|
|
234
|
+
--sidebar-primary: #479ef5;
|
|
235
|
+
--sidebar-primary-foreground: #ffffff;
|
|
236
|
+
--sidebar-accent: #333333; /* grey[20] */
|
|
237
|
+
--sidebar-accent-foreground: #ffffff;
|
|
238
|
+
--sidebar-border: #333333; /* grey[20] */
|
|
239
|
+
--sidebar-ring: #479ef5;
|
|
240
|
+
|
|
241
|
+
/* --- status extensions (dark) ------------------------------------------- */
|
|
242
|
+
--success: #54b054;
|
|
243
|
+
--success-foreground: #0a0a0a;
|
|
244
|
+
--success-subtle: #0c1c0c;
|
|
245
|
+
--success-border: #2f7a2f;
|
|
246
|
+
--warning: #ff8f6b;
|
|
247
|
+
--warning-foreground: #1a0a02;
|
|
248
|
+
--warning-text: #ffb59a;
|
|
249
|
+
--warning-subtle: #22120a;
|
|
250
|
+
--warning-border: #a62d01;
|
|
251
|
+
--destructive-subtle: #3b1212;
|
|
252
|
+
--destructive-border: #9f282b;
|
|
253
|
+
--destructive-text: #ff9a90; /* AA-safe destructive text on dark surfaces (lightened red) */
|
|
254
|
+
|
|
255
|
+
/* --- second-tier neutral foreground (dark) ------------------------------ */
|
|
256
|
+
--foreground-2: #d6d6d6; /* NeutralForeground2 dark (grey[84]) */
|
|
257
|
+
|
|
258
|
+
/* --- accessible interactive stroke (dark) — no dark Figma frame exists for
|
|
259
|
+
these, so hover/pressed are derived: dark neutrals brighten on
|
|
260
|
+
interaction, so each step is lighter than rest (rest = --muted-foreground
|
|
261
|
+
dark). */
|
|
262
|
+
--stroke-accessible: #adadad; /* grey[68] (= --muted-foreground) */
|
|
263
|
+
--stroke-accessible-hover: #b3b3b3; /* [D] derived — one step brighter */
|
|
264
|
+
--stroke-accessible-pressed: #bdbdbd; /* [D] derived — brightest (grey[75]) */
|
|
265
|
+
|
|
266
|
+
/* --- badge warning (dark) — both tokens are Fluent Static, kept identical
|
|
267
|
+
across themes; the chip is self-contained (#242424 on #f7630c ~4.98:1
|
|
268
|
+
clears AA in both themes). */
|
|
269
|
+
--warning-badge: #f7630c;
|
|
270
|
+
--warning-badge-foreground: #242424;
|
|
271
|
+
|
|
272
|
+
/* --- elevation primitives (composite strings inherit via var()) --------- */
|
|
273
|
+
--shadow-ambient: rgba(0, 0, 0, 0.24);
|
|
274
|
+
--shadow-key: rgba(0, 0, 0, 0.28);
|
|
275
|
+
|
|
276
|
+
/* --- focus double-stroke (inverted for dark) ---------------------------- */
|
|
277
|
+
--stroke-focus-outer: #ffffff;
|
|
278
|
+
--stroke-focus-inner: #000000;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* ========================================================================== */
|
|
282
|
+
/* High contrast (a): Windows / forced-colors — hand control to the system. */
|
|
283
|
+
/* ========================================================================== */
|
|
284
|
+
@media (forced-colors: active) {
|
|
285
|
+
:root {
|
|
286
|
+
--ring: Highlight;
|
|
287
|
+
--border: CanvasText;
|
|
288
|
+
--input: CanvasText;
|
|
289
|
+
}
|
|
290
|
+
/* No explicit brand-ramp / status-extension remap is needed here (unlike the
|
|
291
|
+
`.high-contrast` class below, which must remap them by hand). Under
|
|
292
|
+
forced-colors the UA applies `forced-color-adjust: auto` to every element,
|
|
293
|
+
so hex-valued utilities like `bg-brand-160`, `bg-destructive-subtle`, and
|
|
294
|
+
`text-warning-text` are already forced to the system palette automatically.
|
|
295
|
+
The three tokens above are remapped only to steer specific roles to a
|
|
296
|
+
*chosen* system color (Highlight ring, CanvasText border/input) rather than
|
|
297
|
+
the UA default — the brand/status surfaces have no such preference, so the
|
|
298
|
+
automatic override is exactly right and re-declaring them would be noise. */
|
|
299
|
+
/* Focus must stay visible and survive forced-color-adjust. */
|
|
300
|
+
:where(button, a, [role="button"], input, select, textarea):focus-visible {
|
|
301
|
+
outline: 2px solid Highlight;
|
|
302
|
+
outline-offset: 2px;
|
|
303
|
+
}
|
|
304
|
+
/* Preserve brand fill only where meaning depends on it. */
|
|
305
|
+
.btn-primary {
|
|
306
|
+
forced-color-adjust: none;
|
|
307
|
+
background: Highlight;
|
|
308
|
+
color: HighlightText;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* ========================================================================== */
|
|
313
|
+
/* High contrast (b): explicit `.high-contrast` theme class (cross-platform). */
|
|
314
|
+
/* Mirrors Fluent's highContrast theme via CSS system-color keywords. */
|
|
315
|
+
/* ========================================================================== */
|
|
316
|
+
.high-contrast {
|
|
317
|
+
/* This theme paints entirely in CSS system colors (Canvas / CanvasText /
|
|
318
|
+
Highlight …), which resolve against whatever forced-colors theme the OS is
|
|
319
|
+
showing — it is neither inherently light nor dark. Advertise both so native
|
|
320
|
+
chrome resolves to the OS's current scheme. */
|
|
321
|
+
color-scheme: light dark;
|
|
322
|
+
|
|
323
|
+
--background: Canvas;
|
|
324
|
+
--foreground: CanvasText;
|
|
325
|
+
--card: Canvas;
|
|
326
|
+
--card-foreground: CanvasText;
|
|
327
|
+
--popover: Canvas;
|
|
328
|
+
--popover-foreground: CanvasText;
|
|
329
|
+
--primary: Highlight;
|
|
330
|
+
--primary-foreground: HighlightText;
|
|
331
|
+
--secondary: ButtonFace;
|
|
332
|
+
--secondary-foreground: ButtonText;
|
|
333
|
+
--muted: Canvas;
|
|
334
|
+
--muted-foreground: GrayText;
|
|
335
|
+
--foreground-2: CanvasText;
|
|
336
|
+
--accent: Highlight;
|
|
337
|
+
--accent-foreground: HighlightText;
|
|
338
|
+
--destructive: LinkText;
|
|
339
|
+
--destructive-foreground: Canvas;
|
|
340
|
+
--border: CanvasText;
|
|
341
|
+
--input: CanvasText;
|
|
342
|
+
--ring: Highlight;
|
|
343
|
+
--success: CanvasText;
|
|
344
|
+
--warning: CanvasText;
|
|
345
|
+
|
|
346
|
+
/* Status-extension surfaces/borders/text that Alert consumes. The core set
|
|
347
|
+
above covers shadcn's tokens but NOT these, so a destructive/success/
|
|
348
|
+
warning Alert would otherwise keep its raw hex tint in an HC theme.
|
|
349
|
+
Subtle fills collapse to Canvas; borders and the AA-safe *-text tokens go
|
|
350
|
+
to CanvasText — so a status Alert renders as a Canvas panel with a
|
|
351
|
+
CanvasText border and CanvasText icon + title (status still conveyed by
|
|
352
|
+
icon + title text, never color alone — checklist §5). `--success` is
|
|
353
|
+
already CanvasText above, which is what the success icon/title read from,
|
|
354
|
+
so only `--warning-text`/`--destructive-text` need explicit entries. */
|
|
355
|
+
--destructive-subtle: Canvas;
|
|
356
|
+
--success-subtle: Canvas;
|
|
357
|
+
--warning-subtle: Canvas;
|
|
358
|
+
--destructive-border: CanvasText;
|
|
359
|
+
--success-border: CanvasText;
|
|
360
|
+
--warning-border: CanvasText;
|
|
361
|
+
--destructive-text: CanvasText;
|
|
362
|
+
--warning-text: CanvasText;
|
|
363
|
+
|
|
364
|
+
/* Interactive control strokes -> CanvasText so every outline stays visible;
|
|
365
|
+
the badge-warning chip collapses to a Canvas surface with CanvasText ink
|
|
366
|
+
(status conveyed by text, never color alone — checklist §5). */
|
|
367
|
+
--stroke-accessible: CanvasText;
|
|
368
|
+
--stroke-accessible-hover: CanvasText;
|
|
369
|
+
--stroke-accessible-pressed: CanvasText;
|
|
370
|
+
--warning-badge: Canvas;
|
|
371
|
+
--warning-badge-foreground: CanvasText;
|
|
372
|
+
|
|
373
|
+
/* Alert's `info` variant and Avatar's initials fallback tint with the raw
|
|
374
|
+
brand ramp (no dedicated status token), so the specific stops they use as
|
|
375
|
+
surfaces vs. text/border must be remapped here too — otherwise real brand
|
|
376
|
+
blue leaks into an HC theme. Only the consumed stops are redefined:
|
|
377
|
+
surfaces -> Canvas: --brand-160 (Alert-info + Avatar light bg),
|
|
378
|
+
--brand-30 (Alert-info dark bg),
|
|
379
|
+
--brand-40 (Avatar dark bg).
|
|
380
|
+
text + border -> CanvasText: --brand-80 / --brand-100 (Alert-info
|
|
381
|
+
icon+title, light/dark), plus the two DUAL-ROLE
|
|
382
|
+
stops --brand-70 and --brand-140. Each of those is
|
|
383
|
+
both an Avatar initials-text color (light text=70,
|
|
384
|
+
dark text=140) AND an Alert-info border (light
|
|
385
|
+
border=140, dark border=70). Mapping them to
|
|
386
|
+
CanvasText — not Canvas as a naive "border stop"
|
|
387
|
+
reading would suggest — is deliberate: Canvas would
|
|
388
|
+
paint Avatar's initials the same color as their
|
|
389
|
+
fallback background and make them vanish. CanvasText
|
|
390
|
+
keeps the initials legible in both themes and still
|
|
391
|
+
renders the info border as a visible stroke.
|
|
392
|
+
--brand-110 is unused today but remapped defensively
|
|
393
|
+
to keep the consumed-text band internally consistent. */
|
|
394
|
+
--brand-160: Canvas;
|
|
395
|
+
--brand-30: Canvas;
|
|
396
|
+
--brand-40: Canvas;
|
|
397
|
+
--brand-70: CanvasText;
|
|
398
|
+
--brand-80: CanvasText;
|
|
399
|
+
--brand-100: CanvasText;
|
|
400
|
+
--brand-110: CanvasText;
|
|
401
|
+
--brand-140: CanvasText;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* ========================================================================== */
|
|
405
|
+
/* Reduced motion: collapse durations so utilities/vars settle instantly. */
|
|
406
|
+
/* ========================================================================== */
|
|
407
|
+
@media (prefers-reduced-motion: reduce) {
|
|
408
|
+
:root {
|
|
409
|
+
--duration-ultra-fast: 0.01ms;
|
|
410
|
+
--duration-faster: 0.01ms;
|
|
411
|
+
--duration-fast: 0.01ms;
|
|
412
|
+
--duration-normal: 0.01ms;
|
|
413
|
+
--duration-gentle: 0.01ms;
|
|
414
|
+
--duration-slow: 0.01ms;
|
|
415
|
+
--duration-slower: 0.01ms;
|
|
416
|
+
--duration-ultra-slow: 0.01ms;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* ========================================================================== */
|
|
421
|
+
/* Tailwind v4 bridge: re-export semantic vars as utilities. */
|
|
422
|
+
/* `inline` resolves var() at use-site, so `.dark` re-pointing works without */
|
|
423
|
+
/* regenerating utilities. Inline theme vars are NOT re-emitted to :root, so */
|
|
424
|
+
/* the `--x: var(--x)` self-references below are safe (no circular refs). */
|
|
425
|
+
/* ========================================================================== */
|
|
426
|
+
@theme inline {
|
|
427
|
+
/* colors → bg-*, text-*, border-*, ring-* */
|
|
428
|
+
--color-background: var(--background);
|
|
429
|
+
--color-foreground: var(--foreground);
|
|
430
|
+
--color-card: var(--card);
|
|
431
|
+
--color-card-foreground: var(--card-foreground);
|
|
432
|
+
--color-popover: var(--popover);
|
|
433
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
434
|
+
--color-primary: var(--primary);
|
|
435
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
436
|
+
--color-secondary: var(--secondary);
|
|
437
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
438
|
+
--color-muted: var(--muted);
|
|
439
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
440
|
+
--color-foreground-2: var(--foreground-2);
|
|
441
|
+
--color-accent: var(--accent);
|
|
442
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
443
|
+
--color-destructive: var(--destructive);
|
|
444
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
445
|
+
--color-border: var(--border);
|
|
446
|
+
--color-input: var(--input);
|
|
447
|
+
--color-ring: var(--ring);
|
|
448
|
+
|
|
449
|
+
/* chart */
|
|
450
|
+
--color-chart-1: var(--chart-1);
|
|
451
|
+
--color-chart-2: var(--chart-2);
|
|
452
|
+
--color-chart-3: var(--chart-3);
|
|
453
|
+
--color-chart-4: var(--chart-4);
|
|
454
|
+
--color-chart-5: var(--chart-5);
|
|
455
|
+
|
|
456
|
+
/* sidebar */
|
|
457
|
+
--color-sidebar: var(--sidebar);
|
|
458
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
459
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
460
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
461
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
462
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
463
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
464
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
465
|
+
|
|
466
|
+
/* status extensions */
|
|
467
|
+
--color-success: var(--success);
|
|
468
|
+
--color-success-foreground: var(--success-foreground);
|
|
469
|
+
--color-success-subtle: var(--success-subtle);
|
|
470
|
+
--color-success-border: var(--success-border);
|
|
471
|
+
--color-warning: var(--warning);
|
|
472
|
+
--color-warning-foreground: var(--warning-foreground);
|
|
473
|
+
--color-warning-text: var(--warning-text);
|
|
474
|
+
--color-warning-subtle: var(--warning-subtle);
|
|
475
|
+
--color-warning-border: var(--warning-border);
|
|
476
|
+
--color-destructive-subtle: var(--destructive-subtle);
|
|
477
|
+
--color-destructive-border: var(--destructive-border);
|
|
478
|
+
--color-destructive-text: var(--destructive-text);
|
|
479
|
+
--color-stroke-accessible: var(--stroke-accessible);
|
|
480
|
+
--color-stroke-accessible-hover: var(--stroke-accessible-hover);
|
|
481
|
+
--color-stroke-accessible-pressed: var(--stroke-accessible-pressed);
|
|
482
|
+
--color-warning-badge: var(--warning-badge);
|
|
483
|
+
--color-warning-badge-foreground: var(--warning-badge-foreground);
|
|
484
|
+
|
|
485
|
+
/* brand ramp → bg-brand-80, text-brand-160, etc. */
|
|
486
|
+
--color-brand-10: var(--brand-10);
|
|
487
|
+
--color-brand-20: var(--brand-20);
|
|
488
|
+
--color-brand-30: var(--brand-30);
|
|
489
|
+
--color-brand-40: var(--brand-40);
|
|
490
|
+
--color-brand-50: var(--brand-50);
|
|
491
|
+
--color-brand-60: var(--brand-60);
|
|
492
|
+
--color-brand-70: var(--brand-70);
|
|
493
|
+
--color-brand-80: var(--brand-80);
|
|
494
|
+
--color-brand-90: var(--brand-90);
|
|
495
|
+
--color-brand-100: var(--brand-100);
|
|
496
|
+
--color-brand-110: var(--brand-110);
|
|
497
|
+
--color-brand-120: var(--brand-120);
|
|
498
|
+
--color-brand-130: var(--brand-130);
|
|
499
|
+
--color-brand-140: var(--brand-140);
|
|
500
|
+
--color-brand-150: var(--brand-150);
|
|
501
|
+
--color-brand-160: var(--brand-160);
|
|
502
|
+
|
|
503
|
+
/* radius → rounded-* (derived from a single --radius base) */
|
|
504
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
505
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
506
|
+
--radius-lg: var(--radius);
|
|
507
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
508
|
+
--radius-full: 9999px;
|
|
509
|
+
|
|
510
|
+
/* shadows → shadow-* (composite strings live in :root, adapt via primitives) */
|
|
511
|
+
--shadow-2: var(--shadow-2);
|
|
512
|
+
--shadow-4: var(--shadow-4);
|
|
513
|
+
--shadow-8: var(--shadow-8);
|
|
514
|
+
--shadow-16: var(--shadow-16);
|
|
515
|
+
--shadow-28: var(--shadow-28);
|
|
516
|
+
--shadow-64: var(--shadow-64);
|
|
517
|
+
|
|
518
|
+
/* fonts → font-sans, font-mono, font-numeric */
|
|
519
|
+
--font-sans: var(--font-sans);
|
|
520
|
+
--font-mono: var(--font-mono);
|
|
521
|
+
--font-numeric: var(--font-numeric);
|
|
522
|
+
|
|
523
|
+
/* custom type sizes Tailwind's default scale misses (paired line-heights) */
|
|
524
|
+
--text-caption2: 10px;
|
|
525
|
+
--text-caption2--line-height: 14px;
|
|
526
|
+
--text-title2: 28px;
|
|
527
|
+
--text-title2--line-height: 36px;
|
|
528
|
+
--text-title1: 32px;
|
|
529
|
+
--text-title1--line-height: 40px;
|
|
530
|
+
--text-large-title: 40px;
|
|
531
|
+
--text-large-title--line-height: 52px;
|
|
532
|
+
--text-display: 68px;
|
|
533
|
+
--text-display--line-height: 92px;
|
|
534
|
+
|
|
535
|
+
/* motion durations → duration-* utilities (Tailwind reads --transition-duration-*) */
|
|
536
|
+
--transition-duration-ultra-fast: var(--duration-ultra-fast);
|
|
537
|
+
--transition-duration-faster: var(--duration-faster);
|
|
538
|
+
--transition-duration-fast: var(--duration-fast);
|
|
539
|
+
--transition-duration-normal: var(--duration-normal);
|
|
540
|
+
--transition-duration-gentle: var(--duration-gentle);
|
|
541
|
+
--transition-duration-slow: var(--duration-slow);
|
|
542
|
+
--transition-duration-slower: var(--duration-slower);
|
|
543
|
+
--transition-duration-ultra-slow: var(--duration-ultra-slow);
|
|
544
|
+
|
|
545
|
+
/* motion easings → ease-* utilities */
|
|
546
|
+
--ease-accelerate-max: var(--ease-accelerate-max);
|
|
547
|
+
--ease-accelerate-mid: var(--ease-accelerate-mid);
|
|
548
|
+
--ease-accelerate-min: var(--ease-accelerate-min);
|
|
549
|
+
--ease-decelerate-max: var(--ease-decelerate-max);
|
|
550
|
+
--ease-decelerate-mid: var(--ease-decelerate-mid);
|
|
551
|
+
--ease-decelerate-min: var(--ease-decelerate-min);
|
|
552
|
+
--ease-max: var(--ease-max);
|
|
553
|
+
--ease-ease: var(--ease-ease);
|
|
554
|
+
--ease-linear: var(--ease-linear);
|
|
555
|
+
|
|
556
|
+
/* motion keyframes → animate-* utilities.
|
|
557
|
+
shimmer: Fluent Skeleton's moving-gradient wave — the consuming element
|
|
558
|
+
paints a 200%-wide gradient and the keyframes sweep background-position
|
|
559
|
+
(see skeleton.tsx). progress-indeterminate: Fluent ProgressBar's sliding
|
|
560
|
+
segment — a w-1/3 indicator travels from fully off-track left (-100% of
|
|
561
|
+
its own width) to fully off-track right (300%) inside the root's
|
|
562
|
+
overflow-hidden (see progress.tsx). Components pair these with
|
|
563
|
+
motion-reduce:animate-none themselves (the automatic reduced-motion
|
|
564
|
+
duration collapse only covers the --duration-* transition tokens). */
|
|
565
|
+
--animate-shimmer: shimmer 3s linear infinite;
|
|
566
|
+
--animate-progress-indeterminate: progress-indeterminate 1.8s var(--ease-ease) infinite;
|
|
567
|
+
|
|
568
|
+
@keyframes shimmer {
|
|
569
|
+
from {
|
|
570
|
+
background-position: 200% 0;
|
|
571
|
+
}
|
|
572
|
+
to {
|
|
573
|
+
background-position: -200% 0;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
@keyframes progress-indeterminate {
|
|
577
|
+
from {
|
|
578
|
+
translate: -100% 0;
|
|
579
|
+
}
|
|
580
|
+
to {
|
|
581
|
+
translate: 300% 0;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/* ========================================================================== */
|
|
587
|
+
/* Base layer: shadcn v4 defaults (border/outline color + body surface). */
|
|
588
|
+
/* ========================================================================== */
|
|
589
|
+
@layer base {
|
|
590
|
+
* {
|
|
591
|
+
@apply border-border outline-ring/50;
|
|
592
|
+
}
|
|
593
|
+
body {
|
|
594
|
+
@apply bg-background text-foreground;
|
|
595
|
+
}
|
|
596
|
+
}
|