@burojs/ui-shadcn 0.2.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/LICENSE +21 -0
- package/README.md +65 -0
- package/dist/chunk-ABUQPOOE.js +11 -0
- package/dist/chunk-ABUQPOOE.js.map +1 -0
- package/dist/chunk-DBGGWBFG.cjs +199 -0
- package/dist/chunk-DBGGWBFG.cjs.map +1 -0
- package/dist/chunk-FMX4GLCQ.js +6 -0
- package/dist/chunk-FMX4GLCQ.js.map +1 -0
- package/dist/chunk-UC57Z243.js +143 -0
- package/dist/chunk-UC57Z243.js.map +1 -0
- package/dist/chunk-UPGPO75B.js +164 -0
- package/dist/chunk-UPGPO75B.js.map +1 -0
- package/dist/chunk-VXJAXJ3U.cjs +8 -0
- package/dist/chunk-VXJAXJ3U.cjs.map +1 -0
- package/dist/chunk-W7AEYE7A.cjs +13 -0
- package/dist/chunk-W7AEYE7A.cjs.map +1 -0
- package/dist/chunk-YF5IW525.cjs +155 -0
- package/dist/chunk-YF5IW525.cjs.map +1 -0
- package/dist/index.cjs +17752 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +6091 -0
- package/dist/index.d.ts +6091 -0
- package/dist/index.js +17369 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/theme-storage-key.cjs +12 -0
- package/dist/layout/theme-storage-key.cjs.map +1 -0
- package/dist/layout/theme-storage-key.d.cts +23 -0
- package/dist/layout/theme-storage-key.d.ts +23 -0
- package/dist/layout/theme-storage-key.js +3 -0
- package/dist/layout/theme-storage-key.js.map +1 -0
- package/dist/markdown/index.cjs +550 -0
- package/dist/markdown/index.cjs.map +1 -0
- package/dist/markdown/index.d.cts +221 -0
- package/dist/markdown/index.d.ts +221 -0
- package/dist/markdown/index.js +492 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/rich-text-6V7WZ3WV.cjs +518 -0
- package/dist/rich-text-6V7WZ3WV.cjs.map +1 -0
- package/dist/rich-text-N2FZEJJV.js +491 -0
- package/dist/rich-text-N2FZEJJV.js.map +1 -0
- package/dist/styles.css +794 -0
- package/dist/theme/theme-css.cjs +105 -0
- package/dist/theme/theme-css.cjs.map +1 -0
- package/dist/theme/theme-css.d.cts +49 -0
- package/dist/theme/theme-css.d.ts +49 -0
- package/dist/theme/theme-css.js +102 -0
- package/dist/theme/theme-css.js.map +1 -0
- package/dist/theme/theme-storage.cjs +51 -0
- package/dist/theme/theme-storage.cjs.map +1 -0
- package/dist/theme/theme-storage.d.cts +75 -0
- package/dist/theme/theme-storage.d.ts +75 -0
- package/dist/theme/theme-storage.js +45 -0
- package/dist/theme/theme-storage.js.map +1 -0
- package/package.json +165 -0
package/dist/styles.css
ADDED
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
/* Buro design tokens + Tailwind v4 entry point.
|
|
2
|
+
*
|
|
3
|
+
* The consumer imports ONLY this file — it pulls in Tailwind itself,
|
|
4
|
+
* declares the scan sources, the dark-mode variant, and the token mapping.
|
|
5
|
+
* `@source` resolves relative to THIS file, so classes from the package's
|
|
6
|
+
* dist reach the consumer's output without a single line in their config;
|
|
7
|
+
* `node_modules` is excluded from Tailwind's auto-detection, so without
|
|
8
|
+
* this line none of the kit's classes make it into the build — silently.
|
|
9
|
+
*/
|
|
10
|
+
@import "tailwindcss";
|
|
11
|
+
|
|
12
|
+
/* Radix overlay animations. This is a CSS library, NOT a plugin: `@plugin
|
|
13
|
+
* "tw-animate-css"` fails with `"." is not exported under the conditions
|
|
14
|
+
* ["node","require"]`. Verified by build. Both @import lines must come
|
|
15
|
+
* first — CSS requires @import to precede all other rules. */
|
|
16
|
+
@import "tw-animate-css";
|
|
17
|
+
|
|
18
|
+
/* The handbook's markdown rendering (`prose`, `prose-sm`, `prose-invert`,
|
|
19
|
+
* `prose-headings`, ...) needs this registered somewhere Tailwind actually
|
|
20
|
+
* scans, or every `prose*` class compiles to nothing and the docs render
|
|
21
|
+
* unstyled. Opposite of the two imports above: this is a real Tailwind
|
|
22
|
+
* plugin, not a CSS library, so it takes `@plugin`, not `@import`. */
|
|
23
|
+
@plugin '@tailwindcss/typography';
|
|
24
|
+
|
|
25
|
+
/* Resolves relative to THIS file. Published consumers scan compiled JS;
|
|
26
|
+
* source-aliased development hosts scan TS/TSX. Both are required because
|
|
27
|
+
* node_modules is excluded from Tailwind's automatic detection. */
|
|
28
|
+
@source './**/*.{js,ts,tsx}';
|
|
29
|
+
|
|
30
|
+
/* The documentation panel's opening.
|
|
31
|
+
*
|
|
32
|
+
* An ANIMATION, not a transition, and that distinction is the whole point: a
|
|
33
|
+
* transition needs a previous value to interpolate from, so it cannot animate
|
|
34
|
+
* an element that has just been inserted. React coalesces the mount and the
|
|
35
|
+
* expand into one commit, `requestAnimationFrame` (even nested) runs before
|
|
36
|
+
* that commit, a `setTimeout` gets batched with it, and `flushSync` fixes it
|
|
37
|
+
* only by doing something React explicitly objects to from inside an effect.
|
|
38
|
+
* A keyframe animation plays from the moment the element exists and needs none
|
|
39
|
+
* of that. Closing keeps the transition — there the element is already on
|
|
40
|
+
* screen, so it has a value to leave from.
|
|
41
|
+
*
|
|
42
|
+
* The target width is read from a custom property so it is declared once, on
|
|
43
|
+
* the element, and the keyframe and the resting width cannot drift apart. */
|
|
44
|
+
@keyframes doc-panel-in {
|
|
45
|
+
from {
|
|
46
|
+
width: 0;
|
|
47
|
+
}
|
|
48
|
+
to {
|
|
49
|
+
width: var(--doc-panel-w);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@custom-variant dark (&:is(.dark *));
|
|
54
|
+
|
|
55
|
+
:root {
|
|
56
|
+
/* Neutral base, values copied verbatim from shadcn v4's default theme.
|
|
57
|
+
* Stored as oklch, not hsl, for the same reason the severity scale is:
|
|
58
|
+
* these exist to reproduce a specific external palette exactly, and only
|
|
59
|
+
* that palette's own notation guarantees it. The previous default was
|
|
60
|
+
* shadcn's older slate base (`hsl(222 47% 11%)` and friends) — a visibly
|
|
61
|
+
* blue grey, which is what this change removes. */
|
|
62
|
+
--background: oklch(1 0 0);
|
|
63
|
+
--foreground: oklch(0.145 0 0);
|
|
64
|
+
--primary: oklch(0.205 0 0);
|
|
65
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
66
|
+
--secondary: oklch(0.97 0 0);
|
|
67
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
68
|
+
--muted: oklch(0.97 0 0);
|
|
69
|
+
/* Re-measured against the new neutral surfaces (final-review-findings.md
|
|
70
|
+
* M3), value UNCHANGED — this is shadcn v4's own default and every other
|
|
71
|
+
* consumer of this palette matches it: 6.62:1 (old blue-grey background)
|
|
72
|
+
* -> 4.73:1 on the new --background, 4.34:1 on a solid --muted surface
|
|
73
|
+
* (primitives/tabs.tsx inactive tab labels, enum/enum-picker.tsx,
|
|
74
|
+
* resource/resource-list.tsx, notification/toaster.tsx). The first still
|
|
75
|
+
* clears WCAG AA's 4.5:1; 4.34 on solid --muted does not — a known tight
|
|
76
|
+
* spot, recorded here rather than left silent, not a fix for this pass. */
|
|
77
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
78
|
+
--accent: oklch(0.97 0 0);
|
|
79
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
80
|
+
/* Measured against the NEW neutral --background in all THREE roles this
|
|
81
|
+
* token is actually used in (see task-2's contrast script, re-run for the
|
|
82
|
+
* final review — final-review-findings.md I2):
|
|
83
|
+
* - red --destructive text on --background: 5.63:1
|
|
84
|
+
* - white --destructive-foreground on a solid --destructive fill: 5.63:1
|
|
85
|
+
* (the SAME computation as the row above, not a second measurement —
|
|
86
|
+
* --background and --destructive-foreground are both pure white here,
|
|
87
|
+
* so "red on background" and "white on red fill" reduce to one
|
|
88
|
+
* number. A prior pass of this comment paired 4.76 with 4.76 for
|
|
89
|
+
* exactly this reason and read as two data points; it wasn't.)
|
|
90
|
+
* - red text on a 10%-red tint over --background
|
|
91
|
+
* (`bg-destructive/10 text-destructive` — badges, banners, inline
|
|
92
|
+
* errors; see forms/declarative-form.tsx, resource/resource-form.tsx,
|
|
93
|
+
* auth/login-form.tsx, resource/list-error-state.tsx,
|
|
94
|
+
* resource/tab-body.tsx, document-show-banner.tsx and others):
|
|
95
|
+
* 4.74:1
|
|
96
|
+
* (`bg-destructive/5` variant: 5.17:1.) THIS is the case that
|
|
97
|
+
* actually constrains the value — it is the tightest of the three.
|
|
98
|
+
* shadcn v4's own candidate, `oklch(0.577 0.245 27.325)`, passes the
|
|
99
|
+
* first two roles at 4.91:1 but only reaches ≈4.0:1 in this one,
|
|
100
|
+
* below WCAG AA's 4.5:1 — so it does NOT replace the hand-tuned value
|
|
101
|
+
* below, despite passing the two roles that look, at a glance, like
|
|
102
|
+
* the only ones that matter.
|
|
103
|
+
* All four numbers above clear 4.5:1. Restores the pre-migration
|
|
104
|
+
* hand-tuned `hsl(0 84% 44%)`, tuned against the OLD blue-grey background
|
|
105
|
+
* this change removes and re-verified here against the new one. shadcn
|
|
106
|
+
* itself drops --destructive-foreground and hardcodes white; this file
|
|
107
|
+
* keeps the token, valued the same as every other solid-fill `-foreground`
|
|
108
|
+
* below (pure white, kept in hsl() for readability — see the
|
|
109
|
+
* severity-scale comment for why pure black/white stay hsl()). Kept in
|
|
110
|
+
* hsl(), matching the file's default convention: oklch is reserved for
|
|
111
|
+
* values reproducing an external palette exactly, and a hand-tuned value
|
|
112
|
+
* is not that. */
|
|
113
|
+
--destructive: hsl(0 84% 44%);
|
|
114
|
+
--destructive-foreground: hsl(0 0% 100%);
|
|
115
|
+
|
|
116
|
+
/* Severity scale. Task 3 of the token-contract plan: the tree hardcoded 11
|
|
117
|
+
* palette colours across status badges, the history timeline, the log
|
|
118
|
+
* viewer, and a couple of one-off notices (25 amber / 19 emerald / 11 zinc
|
|
119
|
+
* / 11 violet / 9 red / 9 blue / 7 orange / 6 sky / 2 yellow / 2 purple /
|
|
120
|
+
* 2 indigo uses — see the task report for the full meaning → colour →
|
|
121
|
+
* site table). Deriving MEANING from those sites (not guessing from the
|
|
122
|
+
* plan's hypothesis) produced 7 distinct meanings, not the plan's 6:
|
|
123
|
+
* success, warning, info, neutral, highlight, active, and caution. `error`
|
|
124
|
+
* needed no new token — every error/failure site already reduces to the
|
|
125
|
+
* existing `--destructive` above. `active` (blue: a workflow actually
|
|
126
|
+
* RUNNING right now) and `caution` (orange: TimedOut, one notch more
|
|
127
|
+
* severe than warning's Paused/Canceled) are real, separately-rendered
|
|
128
|
+
* meanings the plan didn't anticipate — folding either into `info`/
|
|
129
|
+
* `warning` would visibly change what's on screen once tasks 4-5 wire
|
|
130
|
+
* these up, which is exactly the kind of silent drift this contract
|
|
131
|
+
* exists to prevent. The one deliberate MERGE going the other way:
|
|
132
|
+
* `resource-form.tsx`'s yellow-500 draft-banner collapses into `warning`
|
|
133
|
+
* (amber) — Tailwind's own OKLCH hue distance from amber-500 is 16°
|
|
134
|
+
* (amber↔yellow) vs 22.5° for both amber↔orange and sky↔blue, and L/C are
|
|
135
|
+
* nearly identical (76.9%/0.188 vs 79.5%/0.184) — this reads as accidental
|
|
136
|
+
* drift (whoever wrote that banner grabbed "yellow" for the same "heads
|
|
137
|
+
* up" register everyone else spells "amber"), not a second meaning.
|
|
138
|
+
*
|
|
139
|
+
* FIX ROUND 1 (task 4): `-foreground` briefly carried two incompatible
|
|
140
|
+
* meanings in this file. Every OTHER `-foreground` pair here
|
|
141
|
+
* (`--primary-foreground`, `--destructive-foreground`, ...) is shadcn's
|
|
142
|
+
* own convention: near-white text ON A SOLID FILL of that colour. Task 3's
|
|
143
|
+
* severity `-foreground`s were something else — a severity's text colour
|
|
144
|
+
* on a TINTED background (`badge.tsx`'s 15% tint) — because a tinted
|
|
145
|
+
* badge was the only context that existed when task 3 measured them. Task
|
|
146
|
+
* 4 initially reused the name `-foreground` for that tinted-text value
|
|
147
|
+
* too, and it broke at the one real filled-surface site that exists
|
|
148
|
+
* today: `resource-wizard.tsx`'s completed-step circle
|
|
149
|
+
* (`bg-success text-success-foreground`) rendered dark-green-on-green at
|
|
150
|
+
* 2.19:1 — *worse* than the `text-white` it replaced. Split into two
|
|
151
|
+
* names so the shape (`bg-<x> text-<x>-foreground`) means the same thing
|
|
152
|
+
* for every token pair in this file, severity or not:
|
|
153
|
+
* `--<severity>-foreground` — shadcn's meaning, restored: text on a
|
|
154
|
+
* SOLID `bg-<severity>` fill. Near-white, reproducing what the
|
|
155
|
+
* wizard's literal `text-white` actually rendered (`rgb(255, 255,
|
|
156
|
+
* 255)`) — the only filled-surface severity site that exists today.
|
|
157
|
+
* Not repeated in `.dark`: the fill itself (`--success` etc.) doesn't
|
|
158
|
+
* vary by mode either (see below), so neither does its contrasting
|
|
159
|
+
* text. Only `--success-foreground` has a live consumer right now;
|
|
160
|
+
* the other six carry the same value for symmetry within the family,
|
|
161
|
+
* unverified against a real filled site of their own — see task 4's
|
|
162
|
+
* fix-round report.
|
|
163
|
+
* `--<severity>-emphasis` — task 3's original measurement, renamed,
|
|
164
|
+
* VALUES the same COLOURS (see FIX ROUND 2 below for why the stored
|
|
165
|
+
* FORM changed): a severity's text colour on a TINTED background
|
|
166
|
+
* (`badge.tsx`'s `bg-<severity>/15`), genuinely different between
|
|
167
|
+
* :root and .dark because the source (`text-emerald-700
|
|
168
|
+
* dark:text-emerald-400` etc.) really does swap by `dark:` — see
|
|
169
|
+
* .dark below.
|
|
170
|
+
*
|
|
171
|
+
* FIX ROUND 2 (task 4): every value below — the 7 base tints AND all 14
|
|
172
|
+
* `-emphasis` values (7 light + 7 dark) — is stored as Tailwind's OWN
|
|
173
|
+
* `oklch(...)` string, copied verbatim from the installed
|
|
174
|
+
* `tailwindcss@4.3.3`'s `theme.css`, NOT converted to hsl(). This
|
|
175
|
+
* contradicts task 1's project-wide convention of writing colours as
|
|
176
|
+
* `hsl(...)` (chosen there to keep the computed-style gate's diffs
|
|
177
|
+
* readable — see that task's report) — the severity tokens are the one
|
|
178
|
+
* deliberate exception, for a reason specific to them and nowhere else in
|
|
179
|
+
* this file: they exist to reproduce specific Tailwind palette entries
|
|
180
|
+
* exactly, and only Tailwind's own notation can guarantee that.
|
|
181
|
+
*
|
|
182
|
+
* Fix round 1 stored these as `hsl(...)`, converted from Tailwind's oklch
|
|
183
|
+
* source (oklch → linear sRGB → sRGB → HSL). That conversion is WRONG,
|
|
184
|
+
* not just imprecise, for any of these values that fall outside the sRGB
|
|
185
|
+
* gamut — and checking all 21 shades actually in play (7 severities × 3
|
|
186
|
+
* shades: 500 base, 700 and 400 emphasis) against sRGB, ALL BUT FIVE do:
|
|
187
|
+
* every emerald/amber/sky/orange shade, plus violet-500/400 and
|
|
188
|
+
* blue-500/400. Only zinc (all 3 shades) and violet-700/blue-700 land
|
|
189
|
+
* inside sRGB. `emerald-400` is the clearest example: Tailwind's
|
|
190
|
+
* `oklch(76.5% 0.177 163.223)` is a soft mint (`#34d399`,
|
|
191
|
+
* `rgb(52, 211, 153)`) — converting it via the naive per-channel-clip an
|
|
192
|
+
* hsl() round-trip forces (linear red channel computes to -0.031, which
|
|
193
|
+
* has no representation in hsl() at all) collapses it to
|
|
194
|
+
* `rgb(0, 214, 146)`, a visibly more saturated, wrongly-vivid green — the
|
|
195
|
+
* red channel is off by 52, not a rounding-scale error. This is not a
|
|
196
|
+
* bug in the conversion arithmetic (verified independently twice, by two
|
|
197
|
+
* different people, and both derivations agreed) — the mistake was
|
|
198
|
+
* converting through hsl() AT ALL for a colour hsl() cannot represent.
|
|
199
|
+
* The fix is to stop converting: the browser computes a literal
|
|
200
|
+
* `oklch(...)` custom property as `oklch(...)`, so storing Tailwind's own
|
|
201
|
+
* oklch string makes the computed value byte-identical to Tailwind's
|
|
202
|
+
* native utility classes, and the gate's diff disappears rather than
|
|
203
|
+
* approximating it.
|
|
204
|
+
*
|
|
205
|
+
* `--overlay` and the two `-foreground` whites below are NOT Tailwind
|
|
206
|
+
* palette entries (pure black / pure white, invented for this contract,
|
|
207
|
+
* not looked up), and pure black/white have zero chroma — there is no
|
|
208
|
+
* gamut to fall outside of, in any notation, so they stay in `hsl()` for
|
|
209
|
+
* readability; the notation-mismatch concern this fix round exists for
|
|
210
|
+
* does not apply to them. Confirmed by the pairing analysis in task 4's
|
|
211
|
+
* fix-round-2 report: the overlay's `hsl(0 0% 0%)` pairing was
|
|
212
|
+
* byte-identical against the live gate, no conversion issue.
|
|
213
|
+
*
|
|
214
|
+
* The base tint itself does NOT vary by mode in the source today (no
|
|
215
|
+
* `dark:bg-*` on any of these sites), so it is declared once here and NOT
|
|
216
|
+
* repeated in .dark. */
|
|
217
|
+
--success: oklch(69.6% 0.17 162.48); /* emerald-500 */
|
|
218
|
+
--success-foreground: hsl(0 0% 100%); /* text on a SOLID bg-success fill (not a palette colour) */
|
|
219
|
+
--success-emphasis: oklch(50.8% 0.118 165.612); /* emerald-700, text on a TINTED bg-success/15 */
|
|
220
|
+
--warning: oklch(76.9% 0.188 70.08); /* amber-500 */
|
|
221
|
+
--warning-foreground: hsl(0 0% 100%); /* text on a SOLID bg-warning fill (not a palette colour) */
|
|
222
|
+
--warning-emphasis: oklch(55.5% 0.163 48.998); /* amber-700, text on a TINTED bg-warning/15 */
|
|
223
|
+
--info: oklch(68.5% 0.169 237.323); /* sky-500 */
|
|
224
|
+
--info-foreground: hsl(0 0% 100%); /* text on a SOLID bg-info fill (not a palette colour) */
|
|
225
|
+
--info-emphasis: oklch(50% 0.134 242.749); /* sky-700, text on a TINTED bg-info/15 */
|
|
226
|
+
--neutral: oklch(55.2% 0.016 285.938); /* zinc-500 */
|
|
227
|
+
--neutral-foreground: hsl(0 0% 100%); /* text on a SOLID bg-neutral fill (not a palette colour) */
|
|
228
|
+
--neutral-emphasis: oklch(37% 0.013 285.805); /* zinc-700, text on a TINTED bg-neutral/15 */
|
|
229
|
+
--highlight: oklch(60.6% 0.25 292.717); /* violet-500 */
|
|
230
|
+
--highlight-foreground: hsl(0 0% 100%); /* text on a SOLID bg-highlight fill (not a palette colour) */
|
|
231
|
+
--highlight-emphasis: oklch(49.1% 0.27 292.581); /* violet-700, text on a TINTED bg-highlight/15 */
|
|
232
|
+
--active: oklch(62.3% 0.214 259.815); /* blue-500 */
|
|
233
|
+
--active-foreground: hsl(0 0% 100%); /* text on a SOLID bg-active fill (not a palette colour) */
|
|
234
|
+
--active-emphasis: oklch(48.8% 0.243 264.376); /* blue-700, text on a TINTED bg-active/15 */
|
|
235
|
+
--caution: oklch(70.5% 0.213 47.604); /* orange-500 */
|
|
236
|
+
--caution-foreground: hsl(0 0% 100%); /* text on a SOLID bg-caution fill (not a palette colour) */
|
|
237
|
+
--caution-emphasis: oklch(55.3% 0.195 38.402); /* orange-700, text on a TINTED bg-caution/15 */
|
|
238
|
+
|
|
239
|
+
/* Modal/sheet/alert-dialog backdrop veil. Was a literal `bg-black/80` in
|
|
240
|
+
* three primitives (alert-dialog.tsx, dialog.tsx, sheet.tsx) — black at a
|
|
241
|
+
* fixed lightness needs no light/dark variant, so this is declared once. */
|
|
242
|
+
--overlay: hsl(0 0% 0%);
|
|
243
|
+
|
|
244
|
+
--border: oklch(0.922 0 0);
|
|
245
|
+
--input: oklch(0.922 0 0);
|
|
246
|
+
/* Disabled-field surface. `disabled:opacity-50` (input/textarea/select
|
|
247
|
+
* trigger/command input) is shadcn's canonical disabled signal, and it
|
|
248
|
+
* works when the control's own foreground/background pair has enough
|
|
249
|
+
* luminance separation for a 50%-alpha fade to read as "faded". It stops
|
|
250
|
+
* working the moment a theme narrows that gap — a disabled field can then
|
|
251
|
+
* look pixel-identical to an enabled one (reported against a live theme).
|
|
252
|
+
* A dedicated background makes the signal independent of luminance
|
|
253
|
+
* contrast: the field visibly changes SURFACE, not just opacity, so the
|
|
254
|
+
* two cues reinforce each other instead of one silently doing nothing.
|
|
255
|
+
* Value equals --muted's shipped value on purpose (a muted surface, not a
|
|
256
|
+
* tint of --background) but is its own token, not `var(--muted)`, so a
|
|
257
|
+
* theme can tune "this field is inert" independently of `--muted`'s other
|
|
258
|
+
* jobs (zebra stripes, badges, etc. don't reason about disabled state). */
|
|
259
|
+
--input-disabled: oklch(0.97 0 0);
|
|
260
|
+
/* DEPARTURE from shadcn: v4's own default here is oklch(0.708 0 0), and it
|
|
261
|
+
* fails WCAG 1.4.11's 3:1 focus-indicator minimum in this kit — measured
|
|
262
|
+
* at 2.59:1 against --background and 2.38:1 against --muted/--accent/
|
|
263
|
+
* --secondary (the worse case, and the one call sites actually hit with
|
|
264
|
+
* no ring offset: primitives/button.tsx, input.tsx, select.tsx,
|
|
265
|
+
* textarea.tsx, badge.tsx and ~15 more). shadcn can afford 0.708 because
|
|
266
|
+
* its own components draw focus with `border-ring` PLUS `ring-ring/50
|
|
267
|
+
* ring-[3px]` — two signals; this kit kept the older single-signal
|
|
268
|
+
* treatment, an opaque `ring-2 ring-ring` with `outline-hidden` killing
|
|
269
|
+
* the native fallback, so the ring alone has to clear 3:1. Raised to
|
|
270
|
+
* 0.556 — the same lightness this file already uses for light
|
|
271
|
+
* --muted-foreground and dark --ring, not a new arbitrary stop — which
|
|
272
|
+
* clears 3:1 with margin in both places this ring lands:
|
|
273
|
+
* - vs --background (oklch(1 0 0)): 4.73:1
|
|
274
|
+
* - vs --muted/--accent/--secondary (oklch(0.97 0 0)): 4.34:1
|
|
275
|
+
* Dark --ring is untouched: it already passes (4.18:1 on --background,
|
|
276
|
+
* 3.79:1 on --card/--popover/--sidebar). */
|
|
277
|
+
--ring: oklch(0.556 0 0);
|
|
278
|
+
--popover: oklch(1 0 0);
|
|
279
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
280
|
+
--card: oklch(1 0 0);
|
|
281
|
+
--card-foreground: oklch(0.145 0 0);
|
|
282
|
+
--radius: 0.5rem;
|
|
283
|
+
--sidebar: oklch(0.985 0 0);
|
|
284
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
285
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
286
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
287
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
288
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
289
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
290
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
291
|
+
|
|
292
|
+
/* Tailwind v4's built-in default `--font-sans` is not the same stack v3
|
|
293
|
+
* shipped (verified: `tailwindcss@3.4.19`'s
|
|
294
|
+
* `stubs/config.full.js` → `theme.fontFamily.sans` vs v4's
|
|
295
|
+
* `tailwindcss/theme.css` → `--font-sans`). Neither the old preset nor
|
|
296
|
+
* this file ever pinned `font-sans` before, so the v4 default was free to
|
|
297
|
+
* leak through onto nearly every element on every page — a site-wide,
|
|
298
|
+
* unaccepted visual change caught by the computed-style gate (task 7).
|
|
299
|
+
* Pinning the exact v3 value here keeps the migration invisible, per plan
|
|
300
|
+
* B's contract: nothing visible changes except what's on the accepted
|
|
301
|
+
* list. This is the BASE value only — declared in this unlayered `:root`
|
|
302
|
+
* (not inside `@theme inline`) and re-exposed to `@theme inline` below via
|
|
303
|
+
* `var(--font-sans)`, the same indirection the colour tokens above use.
|
|
304
|
+
* That is load-bearing, not decorative: a value written as a literal
|
|
305
|
+
* inside `@theme inline` gets compiled into the generated utility at
|
|
306
|
+
* build time and emits no custom property at all, so a later theme's
|
|
307
|
+
* runtime `:root { --font-sans: … }` would compile fine and do nothing.
|
|
308
|
+
* Declaring it here first means Tailwind's own `@layer theme` declaration
|
|
309
|
+
* of `--font-sans` loses to this unlayered one regardless of source
|
|
310
|
+
* order, and a theme injected later at runtime (also unlayered) wins over
|
|
311
|
+
* this one in turn — so a later plan CAN override `--font-sans` per-theme.
|
|
312
|
+
* Confirmed by compiling a fixture against the built stylesheet, then
|
|
313
|
+
* again with an extra unlayered `:root { --font-sans: "Comic Sans MS" }`
|
|
314
|
+
* appended: `.font-sans` picked up the override (see the plan's fix
|
|
315
|
+
* report for both compiled outputs).
|
|
316
|
+
* `--font-mono` needed no such pin: v3's and v4's default mono stacks are
|
|
317
|
+
* byte-identical (same two files), confirmed by the gate showing zero
|
|
318
|
+
* font-family drift on any `font-mono` element. */
|
|
319
|
+
--font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
|
320
|
+
"Segoe UI Symbol", "Noto Color Emoji";
|
|
321
|
+
|
|
322
|
+
/* v4 redefines every named font-size's paired line-height as a UNITLESS
|
|
323
|
+
* ratio (e.g. `--text-xs--line-height: calc(1 / 0.75)`); v3 used a FIXED
|
|
324
|
+
* length (`1rem`) instead. CSS inherits a number by recomputing it at each
|
|
325
|
+
* descendant from THAT descendant's own font-size; it inherits a length
|
|
326
|
+
* unchanged. So the two are only equivalent when an element's own
|
|
327
|
+
* font-size matches the ancestor supplying its line-height — anywhere a
|
|
328
|
+
* smaller/larger override (`text-[10px]`, a nested heading, ...) sits
|
|
329
|
+
* inside a `text-*` ancestor, v4 silently recomputes a different
|
|
330
|
+
* line-height than v3 did. Task 7's computed-style gate caught exactly
|
|
331
|
+
* one instance of this (the command palette's `text-[10px]` kbd chips,
|
|
332
|
+
* nested in `text-xs`, went 16px -> 13.3333px) but the mechanism is
|
|
333
|
+
* general, not particular to kbd — `resource-form.tsx`,
|
|
334
|
+
* `resource-show.tsx`, `file.tsx` and others carry `text-[10px]`/
|
|
335
|
+
* `text-[11px]` on routes this gate doesn't cover, and would have drifted
|
|
336
|
+
* silently. Pinning every named size actually used in this kit
|
|
337
|
+
* (`grep -rE 'text-(xs|sm|base|lg|xl|2xl|3xl)\b' packages/ui-shadcn/src
|
|
338
|
+
* apps/playground/src`) to v3's fixed value closes the whole class of bug
|
|
339
|
+
* at the token level, not just the four kbd elements that happened to be
|
|
340
|
+
* gated — those per-element `leading-[1rem]` pins have been removed as
|
|
341
|
+
* redundant. Values read verbatim from the installed
|
|
342
|
+
* `tailwindcss@3.4.19`'s `stubs/config.full.js` `fontSize` block.
|
|
343
|
+
* `text-4xl` and up are unused except `text-8xl`, which v3 and v4 both
|
|
344
|
+
* define as the unitless ratio `1` — identical in both, no pin needed.
|
|
345
|
+
* Declared here (unlayered `:root`), not as a literal inside `@theme
|
|
346
|
+
* inline`, for the same runtime-override reason as `--font-sans` above. */
|
|
347
|
+
--text-xs--line-height: 1rem;
|
|
348
|
+
--text-sm--line-height: 1.25rem;
|
|
349
|
+
--text-base--line-height: 1.5rem;
|
|
350
|
+
--text-lg--line-height: 1.75rem;
|
|
351
|
+
--text-xl--line-height: 1.75rem;
|
|
352
|
+
--text-2xl--line-height: 2rem;
|
|
353
|
+
--text-3xl--line-height: 2.25rem;
|
|
354
|
+
|
|
355
|
+
/* Chart palette. A theme with no opinion on data-viz colour still needs
|
|
356
|
+
* five slots to land in — these are shadcn v4's own default chart palette
|
|
357
|
+
* (its current oklch chart preset, not the older HSL chart-docs example
|
|
358
|
+
* this file previously carried: https://v3.shadcn.com/docs/components/chart),
|
|
359
|
+
* stored as oklch, not hsl, for the same reason the severity scale is: a
|
|
360
|
+
* token whose job is to reproduce a specific external palette entry is
|
|
361
|
+
* stored in that palette's own notation. Genuinely different between
|
|
362
|
+
* light and dark, not a lazy copy: the light set is tuned to read against
|
|
363
|
+
* a white canvas, the dark set trades some of that saturation for
|
|
364
|
+
* mid-lightness so the same five hues stay legible on a dark surface.
|
|
365
|
+
* Nothing in this kit renders a chart yet, so there's no existing look
|
|
366
|
+
* these numbers could contradict — first-run defaults for whenever one
|
|
367
|
+
* shows up, and immediately overridable by any theme that brings its own
|
|
368
|
+
* five. */
|
|
369
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
370
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
371
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
372
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
373
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
374
|
+
|
|
375
|
+
/* Tailwind v4's own default --font-serif, verbatim from the installed
|
|
376
|
+
* tailwindcss@4.3.3's theme.css. Unlike --font-sans above, nothing in this
|
|
377
|
+
* kit sets `font-serif` anywhere today, so there is no prior look to
|
|
378
|
+
* preserve here — this is just the first-run default a pasted theme can
|
|
379
|
+
* override. Declared here (unlayered `:root`), re-exposed via `@theme
|
|
380
|
+
* inline` below through `var(--font-serif)`, for the identical
|
|
381
|
+
* runtime-override reason spelled out above --font-sans. */
|
|
382
|
+
--font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
|
|
383
|
+
|
|
384
|
+
/* Tailwind v4's own defaults for these two (theme.css: `--spacing:
|
|
385
|
+
* 0.25rem`, `--tracking-normal: 0em`), pinned here explicitly rather than
|
|
386
|
+
* left to Tailwind's built-in `@layer theme` default. `--spacing` was
|
|
387
|
+
* already runtime-overridable even before this line — Tailwind emits its
|
|
388
|
+
* own default inside a cascade layer, which any unlayered `:root`
|
|
389
|
+
* override (ours or a theme's) already beats regardless of source order —
|
|
390
|
+
* but declaring it here makes that contract visible in this file instead
|
|
391
|
+
* of implicit in Tailwind's internals, matches how every other themeable
|
|
392
|
+
* token in this file is declared, and pins the value against any future
|
|
393
|
+
* upstream default change. `--tracking-normal` had no existing Tailwind
|
|
394
|
+
* default to inherit an override from in this way (see the tracking-*
|
|
395
|
+
* family below), so for it this declaration is load-bearing, not just
|
|
396
|
+
* documentation. */
|
|
397
|
+
--spacing: 0.25rem;
|
|
398
|
+
--tracking-normal: 0em;
|
|
399
|
+
|
|
400
|
+
/* Six raw shadow components, in the shape tweakcn's own default preset
|
|
401
|
+
* ships them (color/opacity/blur/spread/offset-x/offset-y) — so that a
|
|
402
|
+
* theme built around varying these six has somewhere for them to land.
|
|
403
|
+
* Nothing in THIS file reads them: Tailwind's stock shadow scale is not
|
|
404
|
+
* one shadow linearly scaled by a color/opacity/blur/spread/offset tuple,
|
|
405
|
+
* it is hand-tuned per step, and several steps (md, lg, xl) stack TWO
|
|
406
|
+
* box-shadow layers with independently-chosen blur/spread/offset pairs
|
|
407
|
+
* that no single set of six numbers reproduces via calc(). The eight
|
|
408
|
+
* computed shadows below are pinned to Tailwind's actual literal values
|
|
409
|
+
* instead of being assembled from these six — see the comment there and
|
|
410
|
+
* the task report's before/after compile diff for the proof. */
|
|
411
|
+
--shadow-color: hsl(0 0% 0%);
|
|
412
|
+
--shadow-opacity: 0.1;
|
|
413
|
+
--shadow-blur: 3px;
|
|
414
|
+
--shadow-spread: 0px;
|
|
415
|
+
--shadow-offset-x: 0px;
|
|
416
|
+
--shadow-offset-y: 1px;
|
|
417
|
+
|
|
418
|
+
/* Eight computed shadows. Values are copied verbatim from the installed
|
|
419
|
+
* tailwindcss@4.3.3's theme.css/index.css default shadow scale (`0 1px
|
|
420
|
+
* rgb(0 0 0 / 0.05)` through `0 25px 50px -12px rgb(0 0 0 / 0.25)`) —
|
|
421
|
+
* checked by reading that file, not from memory — so every `shadow-*`
|
|
422
|
+
* utility keeps rendering exactly what it renders today; see the task
|
|
423
|
+
* report for the compiled-fixture diff proving it. Deliberately NOT
|
|
424
|
+
* calc()'d from the six raw components above (see that comment). Declared
|
|
425
|
+
* here in the unlayered `:root`, not as literals inside `@theme inline`,
|
|
426
|
+
* for the same runtime-override reason as --font-sans: the indirection
|
|
427
|
+
* through `var(--shadow-x)` is what lets a theme's own unlayered `:root`
|
|
428
|
+
* override win later. */
|
|
429
|
+
--shadow-2xs: 0 1px rgb(0 0 0 / 0.05);
|
|
430
|
+
--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
431
|
+
--shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
|
432
|
+
--shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
|
433
|
+
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
434
|
+
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
435
|
+
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
|
436
|
+
--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
|
|
437
|
+
|
|
438
|
+
/* Overlay timing. Declared here, in the unlayered `:root`, for the same
|
|
439
|
+
* reason the shadows and fonts are: a theme pasted at runtime (tweakcn
|
|
440
|
+
* emits `:root` and `.dark` blocks, and `stripThemeCss` keeps exactly
|
|
441
|
+
* those) can then retime the whole UI along with recolouring it.
|
|
442
|
+
*
|
|
443
|
+
* These reach the animations without any new machinery:
|
|
444
|
+
* `duration-[var(--duration-overlay)]` sets Tailwind's `--tw-duration`
|
|
445
|
+
* and `ease-[var(--ease-overlay)]` sets `--tw-ease`, and
|
|
446
|
+
* `tw-animate-css` declares
|
|
447
|
+
* --animate-in: enter var(--tw-animation-duration, var(--tw-duration,.15s)) var(--tw-ease, ease) …
|
|
448
|
+
* — i.e. it already reads both. Verified against the package source.
|
|
449
|
+
*
|
|
450
|
+
* `--duration-panel` is separate because a sheet travels the width of
|
|
451
|
+
* its edge, not 5% of its own size like a zooming dialog; the same
|
|
452
|
+
* number would read as a snap on one and a crawl on the other. */
|
|
453
|
+
--duration-overlay: 200ms;
|
|
454
|
+
--duration-panel: 300ms;
|
|
455
|
+
--ease-overlay: cubic-bezier(0.16, 1, 0.3, 1);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.dark {
|
|
459
|
+
--background: oklch(0.145 0 0);
|
|
460
|
+
--foreground: oklch(0.985 0 0);
|
|
461
|
+
--primary: oklch(0.922 0 0);
|
|
462
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
463
|
+
--secondary: oklch(0.269 0 0);
|
|
464
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
465
|
+
--muted: oklch(0.269 0 0);
|
|
466
|
+
/* Re-measured against the new neutral dark surfaces (final-review-findings.md
|
|
467
|
+
* M3), value unchanged: 7.63:1 on --background, 6.91:1 on
|
|
468
|
+
* --card/--popover/--sidebar — both comfortably clear WCAG AA's 4.5:1. */
|
|
469
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
470
|
+
--accent: oklch(0.269 0 0);
|
|
471
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
472
|
+
/* ⚠️ Dark needs BOTH values changed together: one token serves red TEXT on
|
|
473
|
+
a dark surface AND a solid red FILL under a label, which pull in opposite
|
|
474
|
+
directions. At 0 62% 50% the fill passed (4.87:1) while red menu text
|
|
475
|
+
measured 3.51:1. A lighter red with a DARK label satisfies both —
|
|
476
|
+
5.29:1 either way — and is the conventional dark-mode treatment.
|
|
477
|
+
shadcn v4's own candidate (`oklch(0.704 0.191 22.216)`) was measured
|
|
478
|
+
against the new dark --background too: red text passes at 6.84:1, but
|
|
479
|
+
its white --destructive-foreground on that same fill only reaches
|
|
480
|
+
2.89:1 — below WCAG AA's 4.5:1 — so this half keeps the hand-tuned
|
|
481
|
+
values above instead of adopting shadcn's.
|
|
482
|
+
M1 (final-review-findings.md): --destructive-foreground was still the
|
|
483
|
+
slate hsl(222 47% 11%) named at the :root comment's :56 as exactly what
|
|
484
|
+
this whole change exists to remove — the last surviving occurrence.
|
|
485
|
+
Neutralised to oklch(0.145 0 0) (this file's own near-black stop —
|
|
486
|
+
light --foreground / dark --background). Re-measured against the dark
|
|
487
|
+
--destructive fill above: 5.31:1 -> 5.87:1, an improvement, not just a
|
|
488
|
+
wash. */
|
|
489
|
+
--destructive: hsl(0 72% 64%);
|
|
490
|
+
--destructive-foreground: oklch(0.145 0 0);
|
|
491
|
+
|
|
492
|
+
/* Severity-scale `-emphasis` overrides, dark-mode half — see the long
|
|
493
|
+
* comment on the :root block above (fix rounds 1 and 2) for where these
|
|
494
|
+
* come from and why they're `oklch(...)`, not `hsl(...)` (fix round 2:
|
|
495
|
+
* Tailwind's own oklch values verbatim, not converted — several of these
|
|
496
|
+
* fall outside sRGB gamut and an hsl() conversion silently mangles them).
|
|
497
|
+
* Only `-emphasis` (tinted-badge text) varies by `.dark` — the source
|
|
498
|
+
* genuinely swaps `text-emerald-700 dark:text-emerald-400` etc.
|
|
499
|
+
* `-foreground` (solid-fill text) is NOT repeated here, for the same
|
|
500
|
+
* reason the base tints aren't: neither side of that pair varies by mode
|
|
501
|
+
* in the source today. */
|
|
502
|
+
--success-emphasis: oklch(76.5% 0.177 163.223); /* emerald-400 */
|
|
503
|
+
--warning-emphasis: oklch(82.8% 0.189 84.429); /* amber-400 */
|
|
504
|
+
--info-emphasis: oklch(74.6% 0.16 232.661); /* sky-400 */
|
|
505
|
+
--neutral-emphasis: oklch(70.5% 0.015 286.067); /* zinc-400 */
|
|
506
|
+
--highlight-emphasis: oklch(70.2% 0.183 293.541); /* violet-400 */
|
|
507
|
+
--active-emphasis: oklch(70.7% 0.165 254.624); /* blue-400 */
|
|
508
|
+
--caution-emphasis: oklch(75% 0.183 55.934); /* orange-400 */
|
|
509
|
+
|
|
510
|
+
/* Re-measured against the new neutral dark surfaces (final-review-findings.md
|
|
511
|
+
* M3): --border/10 reads 1.25:1 against the page --background and 1.32:1
|
|
512
|
+
* against a --card; the old opaque dark border (hsl(217 33% 22%) on the
|
|
513
|
+
* old slate hsl(222 47% 11%) background) was 1.42:1 — fainter, but in the
|
|
514
|
+
* same family. A border is a structural divider, not text, so WCAG's
|
|
515
|
+
* text-contrast minimums don't gate it; recorded for the next person who
|
|
516
|
+
* wants to know how much margin is left, not because it's failing. */
|
|
517
|
+
--border: oklch(1 0 0 / 10%);
|
|
518
|
+
/* --input/15 reads 1.74:1 against a --card and 1.47:1 over the page
|
|
519
|
+
* --background — see the --border comment above for why these are
|
|
520
|
+
* recorded despite not being text. */
|
|
521
|
+
--input: oklch(1 0 0 / 15%);
|
|
522
|
+
/* Dark half of --input-disabled above — same relationship to --muted's
|
|
523
|
+
* dark value that the light half has. */
|
|
524
|
+
--input-disabled: oklch(0.269 0 0);
|
|
525
|
+
--ring: oklch(0.556 0 0);
|
|
526
|
+
--popover: oklch(0.205 0 0);
|
|
527
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
528
|
+
--card: oklch(0.205 0 0);
|
|
529
|
+
--card-foreground: oklch(0.985 0 0);
|
|
530
|
+
--sidebar: oklch(0.205 0 0);
|
|
531
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
532
|
+
/* DEPARTURE from shadcn: it ships oklch(0.488 0.243 264.376) here — the
|
|
533
|
+
* same blue as --chart-1 — and that colour lands under the active nav
|
|
534
|
+
* item. Keeping the neutral --primary value instead; a blue active row is
|
|
535
|
+
* exactly the tint this palette change set out to remove. */
|
|
536
|
+
--sidebar-primary: oklch(0.922 0 0);
|
|
537
|
+
--sidebar-primary-foreground: oklch(0.205 0 0);
|
|
538
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
539
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
540
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
541
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
542
|
+
|
|
543
|
+
/* Dark half of the chart palette declared in :root above — see that
|
|
544
|
+
* comment for the source and why the two sets of five are genuinely
|
|
545
|
+
* different values, not a copy. */
|
|
546
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
547
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
548
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
549
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
550
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/* Maps tokens to utilities. `inline` is required: without it, values get
|
|
554
|
+
* copied in at build time and swapping the variable at runtime stops
|
|
555
|
+
* working — i.e. plan D's runtime themes won't work. */
|
|
556
|
+
@theme inline {
|
|
557
|
+
--color-background: var(--background);
|
|
558
|
+
--color-foreground: var(--foreground);
|
|
559
|
+
--color-primary: var(--primary);
|
|
560
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
561
|
+
--color-secondary: var(--secondary);
|
|
562
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
563
|
+
--color-muted: var(--muted);
|
|
564
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
565
|
+
--color-accent: var(--accent);
|
|
566
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
567
|
+
--color-destructive: var(--destructive);
|
|
568
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
569
|
+
|
|
570
|
+
/* Severity scale — see the :root block above for the meaning → colour
|
|
571
|
+
* derivation and fix round 1's `-foreground`/`-emphasis` split. `inline`
|
|
572
|
+
* here is exactly as load-bearing as it is for every other token above:
|
|
573
|
+
* without it, `bg-success/15` etc. cannot resolve to a runtime
|
|
574
|
+
* `color-mix()` against a swappable custom property. */
|
|
575
|
+
--color-success: var(--success);
|
|
576
|
+
--color-success-foreground: var(--success-foreground);
|
|
577
|
+
--color-success-emphasis: var(--success-emphasis);
|
|
578
|
+
--color-warning: var(--warning);
|
|
579
|
+
--color-warning-foreground: var(--warning-foreground);
|
|
580
|
+
--color-warning-emphasis: var(--warning-emphasis);
|
|
581
|
+
--color-info: var(--info);
|
|
582
|
+
--color-info-foreground: var(--info-foreground);
|
|
583
|
+
--color-info-emphasis: var(--info-emphasis);
|
|
584
|
+
--color-neutral: var(--neutral);
|
|
585
|
+
--color-neutral-foreground: var(--neutral-foreground);
|
|
586
|
+
--color-neutral-emphasis: var(--neutral-emphasis);
|
|
587
|
+
--color-highlight: var(--highlight);
|
|
588
|
+
--color-highlight-foreground: var(--highlight-foreground);
|
|
589
|
+
--color-highlight-emphasis: var(--highlight-emphasis);
|
|
590
|
+
--color-active: var(--active);
|
|
591
|
+
--color-active-foreground: var(--active-foreground);
|
|
592
|
+
--color-active-emphasis: var(--active-emphasis);
|
|
593
|
+
--color-caution: var(--caution);
|
|
594
|
+
--color-caution-foreground: var(--caution-foreground);
|
|
595
|
+
--color-caution-emphasis: var(--caution-emphasis);
|
|
596
|
+
--color-overlay: var(--overlay);
|
|
597
|
+
|
|
598
|
+
--color-popover: var(--popover);
|
|
599
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
600
|
+
--color-card: var(--card);
|
|
601
|
+
--color-card-foreground: var(--card-foreground);
|
|
602
|
+
--color-border: var(--border);
|
|
603
|
+
--color-input: var(--input);
|
|
604
|
+
--color-input-disabled: var(--input-disabled);
|
|
605
|
+
--color-ring: var(--ring);
|
|
606
|
+
--color-sidebar: var(--sidebar);
|
|
607
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
608
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
609
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
610
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
611
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
612
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
613
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
614
|
+
|
|
615
|
+
--color-chart-1: var(--chart-1);
|
|
616
|
+
--color-chart-2: var(--chart-2);
|
|
617
|
+
--color-chart-3: var(--chart-3);
|
|
618
|
+
--color-chart-4: var(--chart-4);
|
|
619
|
+
--color-chart-5: var(--chart-5);
|
|
620
|
+
|
|
621
|
+
--radius-lg: var(--radius);
|
|
622
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
623
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
624
|
+
/* shadcn's canonical v4 block also derives -xl from the base; without this
|
|
625
|
+
* line -xl stayed pinned at Tailwind's literal 0.75rem, so a themed
|
|
626
|
+
* `--radius` rescaled every corner except `rounded-xl` sites (e.g. the
|
|
627
|
+
* sidebar's inset-variant shell in primitives/sidebar.tsx). */
|
|
628
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
629
|
+
|
|
630
|
+
/* All declared in the unlayered `:root` above, not as literals here —
|
|
631
|
+
* see the comment there for why the indirection is load-bearing
|
|
632
|
+
* (runtime overridability), not decorative. */
|
|
633
|
+
--font-sans: var(--font-sans);
|
|
634
|
+
--font-serif: var(--font-serif);
|
|
635
|
+
--text-xs--line-height: var(--text-xs--line-height);
|
|
636
|
+
--text-sm--line-height: var(--text-sm--line-height);
|
|
637
|
+
--text-base--line-height: var(--text-base--line-height);
|
|
638
|
+
--text-lg--line-height: var(--text-lg--line-height);
|
|
639
|
+
--text-xl--line-height: var(--text-xl--line-height);
|
|
640
|
+
--text-2xl--line-height: var(--text-2xl--line-height);
|
|
641
|
+
--text-3xl--line-height: var(--text-3xl--line-height);
|
|
642
|
+
|
|
643
|
+
--spacing: var(--spacing);
|
|
644
|
+
|
|
645
|
+
/* Only --tracking-normal is a real declared token in :root (the base a
|
|
646
|
+
* theme sets); the other five are computed from it here so overriding the
|
|
647
|
+
* base ripples through the whole scale — the same shape tweakcn's own
|
|
648
|
+
* generated output uses. At the default --tracking-normal: 0em these
|
|
649
|
+
* resolve to exactly Tailwind's own built-in absolute values
|
|
650
|
+
* (-0.05em/-0.025em/0.025em/0.05em/0.1em), so nothing currently rendered
|
|
651
|
+
* moves. */
|
|
652
|
+
--tracking-tighter: calc(var(--tracking-normal) - 0.05em);
|
|
653
|
+
--tracking-tight: calc(var(--tracking-normal) - 0.025em);
|
|
654
|
+
--tracking-normal: var(--tracking-normal);
|
|
655
|
+
--tracking-wide: calc(var(--tracking-normal) + 0.025em);
|
|
656
|
+
--tracking-wider: calc(var(--tracking-normal) + 0.05em);
|
|
657
|
+
--tracking-widest: calc(var(--tracking-normal) + 0.1em);
|
|
658
|
+
|
|
659
|
+
--shadow-2xs: var(--shadow-2xs);
|
|
660
|
+
--shadow-xs: var(--shadow-xs);
|
|
661
|
+
--shadow-sm: var(--shadow-sm);
|
|
662
|
+
--shadow: var(--shadow);
|
|
663
|
+
--shadow-md: var(--shadow-md);
|
|
664
|
+
--shadow-lg: var(--shadow-lg);
|
|
665
|
+
--shadow-xl: var(--shadow-xl);
|
|
666
|
+
--shadow-2xl: var(--shadow-2xl);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/* This can now ship from the package — in v3 it couldn't, because plain CSS
|
|
670
|
+
* doesn't pass through @apply, and every adopter had to copy this rule
|
|
671
|
+
* themselves. Its absence is invisible in light mode and gives 27 glowing
|
|
672
|
+
* borders out of 99 in dark. */
|
|
673
|
+
@layer base {
|
|
674
|
+
* {
|
|
675
|
+
@apply border-border;
|
|
676
|
+
}
|
|
677
|
+
body {
|
|
678
|
+
@apply bg-background text-foreground;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/* v4 preflight makes buttons `cursor: default`; v3 gave them `pointer`.
|
|
682
|
+
* Restoring it here keeps the migration invisible to users. */
|
|
683
|
+
button:not(:disabled),
|
|
684
|
+
[role="button"]:not(:disabled) {
|
|
685
|
+
cursor: pointer;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/* The other half of v3's preflight pair. v3 shipped BOTH
|
|
689
|
+
* `button, [role="button"] { cursor: pointer }` AND
|
|
690
|
+
* `:disabled { cursor: default }`; v4 dropped both, and the rule above
|
|
691
|
+
* only restores the first half via a `:not(:disabled)` guard, which stops
|
|
692
|
+
* a disabled control from claiming `pointer` but does not force it back
|
|
693
|
+
* to `default`. `cursor` inherits, so without this a disabled control
|
|
694
|
+
* inherits whatever cursor its ancestor set instead of being pinned —
|
|
695
|
+
* real nestings exist (clickable rows in `resource-list.tsx`,
|
|
696
|
+
* `relation-picker-dialog.tsx`, `<summary className="cursor-pointer">`
|
|
697
|
+
* in `resource-form.tsx` and `list-error-state.tsx`), and any disabled
|
|
698
|
+
* input without an explicit `disabled:cursor-not-allowed` loses v3's
|
|
699
|
+
* behaviour too. */
|
|
700
|
+
:disabled {
|
|
701
|
+
cursor: default;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/* v4 preflight's `*, ::before, ::after, ::backdrop, ::file-selector-button
|
|
705
|
+
* { padding: 0 }` is a genuinely universal reset. v3's never was: it only
|
|
706
|
+
* zeroed padding on `button/input/optgroup/select/textarea`
|
|
707
|
+
* (`tailwindcss@3.4.19` .../lib/css/preflight.css) and never touched
|
|
708
|
+
* `th`/`td`, so the browser's own UA default (`th, td { padding: 1px }`)
|
|
709
|
+
* was left standing under v3. `TableHead`/`TableCell`
|
|
710
|
+
* (`packages/ui-shadcn/src/primitives/table.tsx`) never set their own
|
|
711
|
+
* vertical padding — only `px-2` horizontal, with `h-10` + `align-middle`
|
|
712
|
+
* for row height — so v4's broader reset silently shaved 1px off every
|
|
713
|
+
* table row's top/bottom padding (caught by task 7's computed-style gate
|
|
714
|
+
* on the `list` route). Restoring the pre-v4 default here, same as the
|
|
715
|
+
* cursor rule above, keeps row heights identical to v3. `px-2` still wins
|
|
716
|
+
* on left/right: Tailwind's `@layer utilities` always beats `@layer base`
|
|
717
|
+
* regardless of specificity, and this shorthand only reaches top/bottom in
|
|
718
|
+
* practice. */
|
|
719
|
+
th,
|
|
720
|
+
td {
|
|
721
|
+
padding: 1px;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/* Standard root. `text-base` therefore computes to 16px on mobile; editable
|
|
726
|
+
* controls that need compact desktop typography opt into `md:text-sm` locally
|
|
727
|
+
* instead of globally rescaling rem-based dimensions.
|
|
728
|
+
*/
|
|
729
|
+
html {
|
|
730
|
+
font-size: 16px;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/* Scrollbars follow the palette.
|
|
734
|
+
*
|
|
735
|
+
* Unstyled, they keep the operating system's own colours, so under any theme
|
|
736
|
+
* they are the one chrome that never repaints — which is exactly how they
|
|
737
|
+
* looked wrong once themes arrived. There are 42 scroll containers across the
|
|
738
|
+
* kit and the app, so this belongs here as one rule rather than at each site.
|
|
739
|
+
*
|
|
740
|
+
* `--border` for the thumb and a transparent track: the thumb reads as a
|
|
741
|
+
* boundary rather than as content, it inherits every theme automatically, and
|
|
742
|
+
* no new token enters the contract. On hover it steps up to
|
|
743
|
+
* `--muted-foreground` so the target is findable without being loud at rest.
|
|
744
|
+
*
|
|
745
|
+
* ⚠️ What CSS does NOT decide here is whether the bar overlays the content or
|
|
746
|
+
* takes layout width. There is no standard property for it — CSS Scrollbars
|
|
747
|
+
* Level 1 defines only `scrollbar-width` and `scrollbar-color`, `overflow:
|
|
748
|
+
* overlay` was removed, and `scrollbar-gutter` reserves space rather than
|
|
749
|
+
* lifting the bar out of flow. Overlay versus classic is the operating
|
|
750
|
+
* system's and browser's call: macOS set to show bars automatically overlays
|
|
751
|
+
* them, Windows and Linux with classic settings do not. If content visibly
|
|
752
|
+
* jumps as a container crosses the fits/does-not-fit boundary, the fix is
|
|
753
|
+
* `scrollbar-gutter: stable` on that container — it always reserves, so the
|
|
754
|
+
* width stops changing. It is deliberately NOT applied globally, because on a
|
|
755
|
+
* platform that already overlays it would carve out a gutter nobody asked for.
|
|
756
|
+
*
|
|
757
|
+
* The two strips that hide their scrollbar on purpose — the tab rail in
|
|
758
|
+
* `tabs-layout.tsx` and the mobile rail in `secondary-sidebar-layout.tsx` —
|
|
759
|
+
* set `scrollbar-width: none` themselves and are unaffected: `none` wins over
|
|
760
|
+
* the `thin` set here by ordinary cascade.
|
|
761
|
+
*/
|
|
762
|
+
@layer base {
|
|
763
|
+
* {
|
|
764
|
+
scrollbar-width: thin;
|
|
765
|
+
scrollbar-color: var(--border) transparent;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/* Safari does not implement the standard properties above; these reproduce
|
|
769
|
+
* the same look through the WebKit pseudo-elements. Chromium honours both
|
|
770
|
+
* and the standard ones win, so this costs nothing there. */
|
|
771
|
+
*::-webkit-scrollbar {
|
|
772
|
+
width: 10px;
|
|
773
|
+
height: 10px;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
*::-webkit-scrollbar-track {
|
|
777
|
+
background: transparent;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
*::-webkit-scrollbar-thumb {
|
|
781
|
+
background-color: var(--border);
|
|
782
|
+
border: 3px solid transparent;
|
|
783
|
+
border-radius: 9999px;
|
|
784
|
+
background-clip: content-box;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
*::-webkit-scrollbar-thumb:hover {
|
|
788
|
+
background-color: var(--muted-foreground);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
*::-webkit-scrollbar-corner {
|
|
792
|
+
background: transparent;
|
|
793
|
+
}
|
|
794
|
+
}
|