@bsuite/theme 0.9.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/components/StatusBadge.js +1 -1
- package/package.json +2 -2
- package/src/css/braden.css +104 -0
- package/src/css/vars.css +62 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
/*
|
|
3
3
|
* Role-bound, not palette-bound. Until 0.7.0 these were raw Tailwind palette
|
|
4
|
-
* classes (`bg-emerald-50`, `bg-red-50`, …), which meant the design-system
|
|
4
|
+
* theme-audit-ok: prose. classes (`bg-emerald-50`, `bg-red-50`, …), which meant the design-system
|
|
5
5
|
* component every app imports was invisible to tenant white-labelling —
|
|
6
6
|
* BrandingProvider mutates the role layer, and these bound below it.
|
|
7
7
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsuite/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"publishConfig": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"url": "https://github.com/GaryOcean428/bsuite.git",
|
|
12
12
|
"directory": "packages/theme"
|
|
13
13
|
},
|
|
14
|
-
"description": "BSuite Universal theme
|
|
14
|
+
"description": "BSuite Universal theme \u2014 D2C Neon Electric + Braden Corporate baselines. Tailwind v4 preset, CSS vars (5-layer), role aliases, colourblind-safe tokens, eye-strain-safe text scale, BrandingProvider (runtime white-label). Used by BSU, CRM7, conduit, R80.3, throughput (D2C) and braden.com.au (Corporate).",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
17
|
"src/css",
|
package/src/css/braden.css
CHANGED
|
@@ -193,11 +193,23 @@
|
|
|
193
193
|
--text-on-warning: var(--text-on-dark-fill);
|
|
194
194
|
--text-on-success: var(--text-on-dark-fill);
|
|
195
195
|
--text-on-info: var(--text-on-light-fill);
|
|
196
|
+
/* Was MISSING and preset-v4.css references it, so --color-overlay-foreground
|
|
197
|
+
resolved to nothing and any text on a scrim fell back to whatever it
|
|
198
|
+
inherited. Measured undefined in the browser on 2026-08-03. The scrim is
|
|
199
|
+
dark in both modes, so this is light in both. */
|
|
200
|
+
--text-on-overlay: var(--text-on-light-fill);
|
|
196
201
|
|
|
197
202
|
/* ============================================================
|
|
198
203
|
DECORATION — shadow, glow, dot and grid
|
|
199
204
|
============================================================ */
|
|
200
205
|
--gradient-accent: linear-gradient(135deg, var(--braden-red), var(--braden-gold));
|
|
206
|
+
/* Same gradient, re-stopped for a DARK backdrop. --gradient-accent is built
|
|
207
|
+
for fills on a light surface: against the hero scrim (L~0.36) its red stop
|
|
208
|
+
measures ~1.7:1 and the text half-disappears. This keeps the red hue and
|
|
209
|
+
chroma exactly (17.6 / 0.17) and lifts only lightness to a value the
|
|
210
|
+
contract already ships, so both stops clear the scrim and G2 still passes. */
|
|
211
|
+
--braden-red-on-dark: oklch(0.704 0.170 17.6);
|
|
212
|
+
--gradient-accent-on-dark: linear-gradient(135deg, var(--braden-red-on-dark), var(--braden-gold));
|
|
201
213
|
--shadow-xs: 0 1px 2px oklch(0.30 0.03 250 / 0.06);
|
|
202
214
|
--shadow-sm: 0 1px 3px oklch(0.30 0.03 250 / 0.08), 0 1px 2px oklch(0.30 0.03 250 / 0.04);
|
|
203
215
|
--shadow-md: 0 4px 10px -3px oklch(0.30 0.03 250 / 0.10), 0 2px 4px -2px oklch(0.30 0.03 250 / 0.06);
|
|
@@ -281,3 +293,95 @@
|
|
|
281
293
|
--dot-color: oklch(0.72 0.060 90 / 0.16);
|
|
282
294
|
--grid-color: oklch(0.82 0.020 250 / 0.055);
|
|
283
295
|
}
|
|
296
|
+
|
|
297
|
+
/* ============================================================
|
|
298
|
+
BASE — Corporate sets the heading FACE only.
|
|
299
|
+
============================================================
|
|
300
|
+
Not colour, not size, not weight, not transform. Reverted to this after
|
|
301
|
+
operator review ("well that looks worse") and a measurement that showed
|
|
302
|
+
why: on the live site the hero H1 picked up the ramp colour while the 25
|
|
303
|
+
headings below it kept their explicit `text-braden-navy`. One heading
|
|
304
|
+
stopped matching the rest. That is not a hierarchy.
|
|
305
|
+
|
|
306
|
+
A base rule only reaches elements that do not style themselves. In a
|
|
307
|
+
dense app UI that is most headings, which is the case the ramp was
|
|
308
|
+
measured against. On a marketing site it is almost none — 59 headings
|
|
309
|
+
here carry an explicit brand colour and their own size utility — so a
|
|
310
|
+
base ramp cannot create hierarchy there, only a mismatch on whichever
|
|
311
|
+
few it happens to reach.
|
|
312
|
+
|
|
313
|
+
font-family is the exception: nothing on the page overrides it, so it
|
|
314
|
+
lands uniformly and is exactly what a base rule is for.
|
|
315
|
+
|
|
316
|
+
D2C keeps the full ramp (see vars.css) — those apps ARE the dense-UI
|
|
317
|
+
case, and the ramp is verified there in a real browser by G5. */
|
|
318
|
+
@layer base {
|
|
319
|
+
h1 { font-family: var(--font-display); }
|
|
320
|
+
h2, h3, h4, h5, h6 { font-family: var(--font-heading); }
|
|
321
|
+
|
|
322
|
+
/* The root element's colour was the UA default — measured pure black (L=0)
|
|
323
|
+
in BOTH modes on 2026-08-03, i.e. the one banned value, sitting at the top
|
|
324
|
+
of the inheritance chain. Nothing renders text at <html> so it was
|
|
325
|
+
invisible to review, but every element that does not set a colour and is
|
|
326
|
+
not under a styled ancestor lands on it. */
|
|
327
|
+
html { color: var(--role-text-body); background-color: var(--role-bg-base); }
|
|
328
|
+
|
|
329
|
+
/* The contract names a family and the package shipped the token, but NOTHING
|
|
330
|
+
bound it to an element — so every app fell back to whatever it happened
|
|
331
|
+
to set itself, which is how crm7 ran Inter against a Geist contract and
|
|
332
|
+
the estate ran four families at once. Same defect as the heading ramp:
|
|
333
|
+
a token nobody applies is not a font, it is a variable. */
|
|
334
|
+
body { font-family: var(--font-body); }
|
|
335
|
+
code, kbd, samp, pre { font-family: var(--font-mono); }
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* ============================================================
|
|
339
|
+
DARK SURFACES INSIDE LIGHT MODE
|
|
340
|
+
============================================================
|
|
341
|
+
A navy or red panel on a light page is a dark surface, and the mode-sensitive
|
|
342
|
+
role tokens are still resolving their LIGHT values inside it. That is not a
|
|
343
|
+
theoretical problem — measured on the contact section 2026-08-03:
|
|
344
|
+
|
|
345
|
+
--role-accent-text (light) on bg-braden-navy ....... 2.20:1 FAIL
|
|
346
|
+
--braden-gold on bg-braden-navy ..... 5.07:1 AA
|
|
347
|
+
|
|
348
|
+
and the mirror holds on a light background, where the brand gold is 2.05:1
|
|
349
|
+
and the light accent is 4.71:1. No single value passes on both, so this
|
|
350
|
+
cannot be fixed by picking a better gold — the token has to follow the
|
|
351
|
+
SURFACE, not just the mode. Mark a dark panel and its accent text flips. */
|
|
352
|
+
[data-surface='dark'] {
|
|
353
|
+
--role-accent-text: var(--braden-gold);
|
|
354
|
+
--role-text-body: var(--text-on-light-fill);
|
|
355
|
+
--role-text-secondary: var(--text-on-light-fill);
|
|
356
|
+
/* D2C's heading ramp reads this; Corporate has no colour ramp, but a shared
|
|
357
|
+
surface contract should behave the same in both brands. */
|
|
358
|
+
--heading-color: currentColor;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* ============================================================
|
|
362
|
+
GRADIENT TEXT — the one place the accent gradient paints glyphs.
|
|
363
|
+
============================================================
|
|
364
|
+
background-clip:text needs `color: transparent` to show through, which means
|
|
365
|
+
a failed gradient produces INVISIBLE TEXT rather than an ugly one. So both
|
|
366
|
+
stops resolve from tokens defined above in this same file, never from a
|
|
367
|
+
token an app might or might not have. */
|
|
368
|
+
@layer components {
|
|
369
|
+
.text-gradient-accent {
|
|
370
|
+
background-image: var(--gradient-accent);
|
|
371
|
+
-webkit-background-clip: text;
|
|
372
|
+
background-clip: text;
|
|
373
|
+
color: transparent;
|
|
374
|
+
}
|
|
375
|
+
.text-gradient-accent-on-dark {
|
|
376
|
+
background-image: var(--gradient-accent-on-dark);
|
|
377
|
+
-webkit-background-clip: text;
|
|
378
|
+
background-clip: text;
|
|
379
|
+
color: transparent;
|
|
380
|
+
}
|
|
381
|
+
/* Forced-colours mode drops background-image entirely, which would leave
|
|
382
|
+
transparent glyphs on screen. Hand the text back to the system palette. */
|
|
383
|
+
@media (forced-colors: active) {
|
|
384
|
+
.text-gradient-accent,
|
|
385
|
+
.text-gradient-accent-on-dark { background-image: none; color: CanvasText; }
|
|
386
|
+
}
|
|
387
|
+
}
|
package/src/css/vars.css
CHANGED
|
@@ -482,3 +482,65 @@
|
|
|
482
482
|
|
|
483
483
|
/* Shadcn bridge picks up rebindings automatically via var() chain */
|
|
484
484
|
}
|
|
485
|
+
|
|
486
|
+
/* ============================================================
|
|
487
|
+
BASE — the heading ramp, APPLIED.
|
|
488
|
+
============================================================
|
|
489
|
+
Added 2026-08-03 after operator review: "borders and heading levels
|
|
490
|
+
unstyled" (conduit) and "Document Management looks all black" (BSU).
|
|
491
|
+
|
|
492
|
+
Both were the same omission. --role-h1..h6 existed as tokens and NOTHING
|
|
493
|
+
bound them to an actual h1..h6, so every heading fell through to
|
|
494
|
+
--role-text-body — one flat near-black for all six levels. A ramp nobody
|
|
495
|
+
applies is not a ramp, it is six unused variables, and that is exactly what
|
|
496
|
+
an operator sees as "unstyled".
|
|
497
|
+
|
|
498
|
+
Values are the contract's, verbatim (d2c-theme-source-of-truth.html and the
|
|
499
|
+
Corporate equivalent). Each level shifts HUE as well as lightness, so the
|
|
500
|
+
hierarchy survives greyscale and both dichromacies rather than relying on
|
|
501
|
+
size alone.
|
|
502
|
+
|
|
503
|
+
Scoped to @layer base so any component class ON THE HEADING ITSELF wins.
|
|
504
|
+
That is a narrower guarantee than it looks, and the earlier version of this
|
|
505
|
+
comment ("it does not fight Tailwind utilities") was simply wrong.
|
|
506
|
+
|
|
507
|
+
WHAT IT DOES OVERRIDE: an INHERITED colour. Inheritance is the last resort in
|
|
508
|
+
the cascade, so a rule targeting `h1` beats `text-primary-foreground` sitting
|
|
509
|
+
on an ancestor — specificity never enters into it. Measured on braden
|
|
510
|
+
2026-08-03: a hero `<h1>` inheriting a near-white foreground from its wrapper
|
|
511
|
+
rendered dark navy against a dark scrim, i.e. all but invisible, and it was
|
|
512
|
+
the ONLY heading on the page that moved. That is the signature of this bug —
|
|
513
|
+
one heading changing is not a small problem, it is the coloured-container
|
|
514
|
+
case, and coloured containers are where text goes invisible.
|
|
515
|
+
|
|
516
|
+
--heading-color is the escape hatch. Any container that colours its own text
|
|
517
|
+
sets it once and the whole heading subtree defers:
|
|
518
|
+
<div class="text-primary-foreground [--heading-color:currentColor]">
|
|
519
|
+
currentColor there resolves to the container's colour, so headings inherit
|
|
520
|
+
again instead of being repainted. */
|
|
521
|
+
@layer base {
|
|
522
|
+
/* See braden.css: the root element inherits the UA default pure black when
|
|
523
|
+
nothing sets it, which is the one banned value at the top of the chain. */
|
|
524
|
+
html { color: var(--role-text-body); background-color: var(--role-bg-base); }
|
|
525
|
+
|
|
526
|
+
/* The contract names a family and the package shipped the token, but NOTHING
|
|
527
|
+
bound it to an element — so every app fell back to whatever it happened
|
|
528
|
+
to set itself, which is how crm7 ran Inter against a Geist contract and
|
|
529
|
+
the estate ran four families at once. Same defect as the heading ramp:
|
|
530
|
+
a token nobody applies is not a font, it is a variable. */
|
|
531
|
+
body { font-family: var(--font-body); }
|
|
532
|
+
code, kbd, samp, pre { font-family: var(--font-mono); }
|
|
533
|
+
|
|
534
|
+
h1 { color: var(--heading-color, var(--role-h1)); font-size: 25px; font-weight: 700; letter-spacing: var(--track-display); }
|
|
535
|
+
h2 { color: var(--heading-color, var(--role-h2)); font-size: 19px; }
|
|
536
|
+
h3 { color: var(--heading-color, var(--role-h3)); font-size: 16px; }
|
|
537
|
+
h4 { color: var(--heading-color, var(--role-h4)); font-size: 14px; }
|
|
538
|
+
h5 { color: var(--heading-color, var(--role-h5)); font-size: 13px; }
|
|
539
|
+
h6 {
|
|
540
|
+
color: var(--heading-color, var(--role-h6));
|
|
541
|
+
font-size: 11px;
|
|
542
|
+
text-transform: uppercase;
|
|
543
|
+
letter-spacing: var(--track-eyebrow);
|
|
544
|
+
font-weight: 700;
|
|
545
|
+
}
|
|
546
|
+
}
|