@bsuite/theme 0.13.0 → 0.14.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/package.json +1 -1
- package/src/css/utilities.css +45 -0
- package/src/css/vars.css +89 -11
package/package.json
CHANGED
package/src/css/utilities.css
CHANGED
|
@@ -142,3 +142,48 @@
|
|
|
142
142
|
@media (forced-colors: active) {
|
|
143
143
|
.text-gradient-accent { background-image: none; color: CanvasText; }
|
|
144
144
|
}
|
|
145
|
+
|
|
146
|
+
/* ── NAV / SWITCHER GRADIENT UNDERLINE ───────────────────────────────────
|
|
147
|
+
TH-6. Promoted from crm7/src/index.css, 2026-08-17.
|
|
148
|
+
|
|
149
|
+
The operator asked for the nav gradient underline in every app. It was
|
|
150
|
+
unimplementable anywhere but crm7, because the treatment existed only in
|
|
151
|
+
crm7's own stylesheet — 3 files in one app (the two declarations here plus
|
|
152
|
+
two call sites), and ZERO occurrences in this package or in the other five
|
|
153
|
+
apps. A request that needs a copy-paste to satisfy is a packaging defect,
|
|
154
|
+
not a feature request, and copying it would have produced six declarations
|
|
155
|
+
drifting apart the way `--bg-shell-elevated` did.
|
|
156
|
+
|
|
157
|
+
It paints from --gradient-accent, which this package already owns, so
|
|
158
|
+
nothing app-specific came with it.
|
|
159
|
+
|
|
160
|
+
TWO FORMS, ONE PAINT SOURCE — that pairing is the point. Keeping them
|
|
161
|
+
adjacent is what makes "the switcher and the active nav item match" a fact
|
|
162
|
+
about this file rather than two declarations someone has to remember to
|
|
163
|
+
keep in step:
|
|
164
|
+
|
|
165
|
+
`-span` the caller already renders its own absolutely-positioned
|
|
166
|
+
element for the rule (crm7's tenant switcher does).
|
|
167
|
+
`::after` the caller does not, so the rule is generated for it (the
|
|
168
|
+
navigation items). REQUIRES `position: relative` on the host —
|
|
169
|
+
without it the rule anchors to the nearest positioned ancestor
|
|
170
|
+
and lands somewhere else on the page.
|
|
171
|
+
|
|
172
|
+
The `-span` form carries no geometry on purpose: the caller owns its own
|
|
173
|
+
inset/height/opacity, and crm7's switcher sets `opacity-70` with a
|
|
174
|
+
`group-hover:opacity-100` transition. Only the paint is shared. */
|
|
175
|
+
.bsuite-gradient-underline-span {
|
|
176
|
+
background: var(--gradient-accent);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.bsuite-gradient-underline::after {
|
|
180
|
+
content: '';
|
|
181
|
+
position: absolute;
|
|
182
|
+
inset-inline: 0.5rem;
|
|
183
|
+
bottom: 0.125rem;
|
|
184
|
+
height: 0.125rem;
|
|
185
|
+
border-radius: 9999px;
|
|
186
|
+
background: var(--gradient-accent);
|
|
187
|
+
opacity: 0.7;
|
|
188
|
+
pointer-events: none;
|
|
189
|
+
}
|
package/src/css/vars.css
CHANGED
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
* --light-bg-accent ship as oklch(1 0 0.5) — theme-audit-ok: naming the
|
|
34
34
|
* value that was REMOVED — and render every shadcn card
|
|
35
35
|
* and popover pure white. "White" here means oklch(0.982 0.002 248)
|
|
36
|
-
*
|
|
36
|
+
* theme-audit-ok: naming the equivalent hex for a reader's reference, not
|
|
37
|
+
* consuming it — (#f8f9fa); the lightest surface is bg-panel at L=0.994.
|
|
37
38
|
*
|
|
38
39
|
* OKLCH format is MANDATORY for all new tokens. No hex/rgb/hsl.
|
|
39
40
|
* Syntax: oklch(L C H) — L=lightness 0–1, C=chroma 0–0.4, H=hue 0–360.
|
|
@@ -68,8 +69,9 @@
|
|
|
68
69
|
--neon-electric-deep: oklch(0.380 0.140 270); /* secondary action */
|
|
69
70
|
|
|
70
71
|
/* The two words the estate uses loosely, pinned to values. Operator ruling
|
|
71
|
-
2026-08-02 ("white is primary: #f8f9fa"
|
|
72
|
-
|
|
72
|
+
2026-08-02 ("white is primary: #f8f9fa" — theme-audit-ok: naming the
|
|
73
|
+
ruling's literal, not consuming it; the tokens below are oklch) in terms:
|
|
74
|
+
"define --white and --black once in the token source and reference them everywhere; never
|
|
73
75
|
write either literal inline." This is that definition — it did not exist
|
|
74
76
|
until 2026-08-13, which is why the sanctioned replacement values named in
|
|
75
77
|
packages/eslint-config/rules/no-hardcoded-colours.js had no token to point
|
|
@@ -294,6 +296,43 @@
|
|
|
294
296
|
--role-border: var(--light-border);
|
|
295
297
|
--role-border-strong: oklch(0.880 0.008 250);
|
|
296
298
|
|
|
299
|
+
/* --role-border-interactive — TH-5 / bsuite#1958, the operator's
|
|
300
|
+
"unstyled button".
|
|
301
|
+
------------------------------------------------------------------
|
|
302
|
+
WCAG 1.4.11 (non-text contrast) requires 3:1 for the visual boundary
|
|
303
|
+
that IDENTIFIES a user-interface component — the outline of a text
|
|
304
|
+
input, a select, an `outline`-variant button. It does NOT require 3:1
|
|
305
|
+
of a decorative hairline separating two filled surfaces, which is what
|
|
306
|
+
`--role-border` is for. Those are two different jobs and they had one
|
|
307
|
+
token, so the stricter job inherited the looser value.
|
|
308
|
+
|
|
309
|
+
MEASURED, light mode, OKLCH -> linear-sRGB -> WCAG relative luminance
|
|
310
|
+
(the same pipeline the text scale above is measured with, positive-
|
|
311
|
+
controlled by reproducing this file's own recorded dark figures of
|
|
312
|
+
3.36:1 and 3.61:1 for --role-border-strong to two decimals):
|
|
313
|
+
|
|
314
|
+
--role-border vs --role-bg-panel 1.12:1 <- the filed defect
|
|
315
|
+
--role-border vs --role-bg-surface 1.13:1
|
|
316
|
+
--role-border vs --role-bg-sunken 1.01:1 <- worst live case
|
|
317
|
+
--role-border-strong vs --role-bg-panel 1.35:1
|
|
318
|
+
|
|
319
|
+
So re-pointing at the strongest EXISTING light token does not fix it —
|
|
320
|
+
1.35:1 is still less than half the floor. This role needs a value that
|
|
321
|
+
does not exist in the light scale yet, and here it is.
|
|
322
|
+
|
|
323
|
+
oklch(0.56 0.015 260) is from the D2C source-of-truth document (the
|
|
324
|
+
palette whitelist accepts it) and clears 3:1 on every light surface
|
|
325
|
+
role with margin, so no call site has to know which surface it landed on:
|
|
326
|
+
|
|
327
|
+
vs --role-bg-surface 4.42:1 vs --role-bg-panel 4.39:1
|
|
328
|
+
vs --role-bg-input 4.18:1 vs --role-bg-body 4.15:1
|
|
329
|
+
vs --role-bg-sunken 3.88:1 <- worst case, still +29% over the floor
|
|
330
|
+
|
|
331
|
+
Bound to shadcn's `--input` below, which is what `border-input` on every
|
|
332
|
+
Input/Select/Textarea and the Button `outline` variant resolves through.
|
|
333
|
+
That binding is the whole point: a token nothing invokes is TH-8. */
|
|
334
|
+
--role-border-interactive: oklch(0.56 0.015 260);
|
|
335
|
+
|
|
297
336
|
/* Scrim — the veil behind a modal, and the reason `bg-black/N` survived theme-audit-ok: prose
|
|
298
337
|
everywhere else. It was the single most duplicated pure endpoint in the
|
|
299
338
|
tree (9 sites across nav-core, page-builder and schema-builder) for one
|
|
@@ -357,7 +396,14 @@
|
|
|
357
396
|
--destructive: var(--role-destructive);
|
|
358
397
|
--destructive-foreground: var(--text-on-error);
|
|
359
398
|
--border: var(--role-border);
|
|
360
|
-
|
|
399
|
+
/* `--input` is shadcn's INTERACTIVE boundary — `border-input` is on every
|
|
400
|
+
Input, Select, Textarea and the Button `outline` variant. It pointed at
|
|
401
|
+
--role-border, the decorative hairline, which measures 1.12:1 against the
|
|
402
|
+
panel: WCAG 1.4.11 wants 3:1 and the operator simply called it "an
|
|
403
|
+
unstyled button" (bsuite#1958). Now bound to the role built for the job.
|
|
404
|
+
`--border` deliberately stays on --role-border: separating two filled
|
|
405
|
+
surfaces is not a component boundary and does not carry the 3:1 duty. */
|
|
406
|
+
--input: var(--role-border-interactive);
|
|
361
407
|
--ring: var(--role-primary);
|
|
362
408
|
|
|
363
409
|
/* ============================================================
|
|
@@ -583,13 +629,45 @@
|
|
|
583
629
|
light, 0.320/0.420 dark). Only the D2C theme was half-defined, which is
|
|
584
630
|
why nobody reading the corporate theme would spot it.
|
|
585
631
|
|
|
586
|
-
Value
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
632
|
+
Value WAS oklch(0.52 0.02 248) from the D2C source-of-truth document,
|
|
633
|
+
recorded here as 3.36:1 against `--role-bg-panel` and 3.61:1 against
|
|
634
|
+
`--role-bg-sunken`. Both figures re-measured exactly on 2026-08-17 —
|
|
635
|
+
which is how the instrument for TH-5 was positive-controlled.
|
|
636
|
+
|
|
637
|
+
WHAT THAT PAIR OF FIGURES MISSED (TH-5, 2026-08-17): only two of the five
|
|
638
|
+
dark surface roles were ever checked. Against the third —
|
|
639
|
+
`--role-bg-input` oklch(0.240 0.020 260), the LIGHTEST dark surface and
|
|
640
|
+
the one an interactive boundary actually sits on — 0.52 measures 2.99:1.
|
|
641
|
+
Under the 3:1 floor, by a rounding margin, on precisely the surface the
|
|
642
|
+
rule exists for. Nothing was wrong with the arithmetic; the surface list
|
|
643
|
+
was short, so the worst case was never in it.
|
|
644
|
+
|
|
645
|
+
Raised to oklch(0.55 0.015 250) — also from the source-of-truth document,
|
|
646
|
+
+0.03 L, visually indistinguishable, and decisive:
|
|
647
|
+
|
|
648
|
+
vs --role-bg-body 3.98:1 vs --role-bg-surface 3.65:1
|
|
649
|
+
vs --role-bg-panel 3.81:1 vs --role-bg-sunken 4.09:1
|
|
650
|
+
vs --role-bg-input 3.40:1 <- the case that used to be 2.99:1
|
|
651
|
+
|
|
652
|
+
`--role-border` itself is 2.26:1, which is why "strong" has to exist.
|
|
653
|
+
The contract test now measures all five surfaces in both modes, so a
|
|
654
|
+
short surface list cannot hide a sub-3:1 pair again. */
|
|
655
|
+
--role-border-strong: oklch(0.55 0.015 250);
|
|
656
|
+
|
|
657
|
+
/* Dark half of --role-border-interactive (see the light block for the full
|
|
658
|
+
rationale). oklch(0.66 0.018 250), source-of-truth palette, measured
|
|
659
|
+
against every dark surface role:
|
|
660
|
+
|
|
661
|
+
vs --role-bg-sunken 6.38:1 vs --role-bg-body 6.20:1
|
|
662
|
+
vs --role-bg-panel 5.95:1 vs --role-bg-surface 5.69:1
|
|
663
|
+
vs --role-bg-input 5.30:1 <- worst case
|
|
664
|
+
|
|
665
|
+
Deliberately a wider margin than the light half's 3.88:1: a hairline on
|
|
666
|
+
navy is the case this estate has already got wrong twice (the near-white
|
|
667
|
+
`border-strong` above, and the 0.09-alpha `--border-shell`), and the cost
|
|
668
|
+
of a boundary that is slightly too visible is nothing next to one that
|
|
669
|
+
disappears. */
|
|
670
|
+
--role-border-interactive: oklch(0.66 0.018 250);
|
|
593
671
|
|
|
594
672
|
/* Role text variants rebind for the dark surface — all AAA on navy. */
|
|
595
673
|
--role-primary-text: oklch(0.721 0.215 262.9); /* 7.01:1 ✓ AAA */
|