@bsuite/theme 0.7.1 → 0.8.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 +2 -2
- package/src/css/braden.css +262 -209
- package/src/css/vars.css +93 -13
- package/src/preset-v4.css +24 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsuite/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
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 — 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
|
@@ -1,230 +1,283 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* @bsuite/theme — Braden Corporate brand baseline
|
|
3
|
-
* Version 0.
|
|
3
|
+
* Version 0.4.0
|
|
4
4
|
*
|
|
5
5
|
* Usage (braden.com.au only):
|
|
6
6
|
* @import '@bsuite/theme/braden-css';
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* EXCLUSIVELY for the braden.com.au Corporate brand. Never import this into a
|
|
9
|
+
* D2C app (crm7, conduit, BSU, R80.3, throughput), and never put Neon Electric
|
|
10
|
+
* colours on a Corporate surface.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
12
|
+
* GENERATED FROM THE CONTRACT, NOT HAND-TUNED.
|
|
13
|
+
* Every value below is copied from
|
|
14
|
+
* packages/theme/docs/braden-corporate-theme-source-of-truth.html
|
|
15
|
+
* which is authoritative. Operator ruling 2026-08-03: only colours present in
|
|
16
|
+
* the two source-of-truth documents are permitted in packages/. Verify with
|
|
17
|
+
* scripts/audit-palette-whitelist.py
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* WHAT CHANGED IN 0.4.0 AND WHY IT MATTERED
|
|
20
|
+
* 0.3.0 carried its own hand-built red/gold/navy ramps. 52 of its values
|
|
21
|
+
* appeared nowhere in the Corporate contract — close to the brand, but not the
|
|
22
|
+
* brand, and drifting further with every edit. This file also shipped a header
|
|
23
|
+
* asserting "purple error is a cross-brand system requirement", which was
|
|
24
|
+
* already false twice over: the 0.7.0 separation-first contract retired purple
|
|
25
|
+
* for semantics, and the Corporate contract binds error to --braden-indigo.
|
|
22
26
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* supported Tailwind engine for BSuite consumers.
|
|
27
|
+
* ERROR IS INDIGO HERE, NOT RED — deliberately, and for the opposite reason to
|
|
28
|
+
* D2C. D2C's primary is blue, so error must be red to separate from it.
|
|
29
|
+
* Corporate's primary IS red, so a red error would collide with the brand's
|
|
30
|
+
* own primary action colour. Same rule, inverted inputs.
|
|
28
31
|
*/
|
|
29
32
|
|
|
33
|
+
|
|
34
|
+
:root {
|
|
35
|
+
|
|
30
36
|
/* ============================================================
|
|
31
|
-
LAYER 1 —
|
|
37
|
+
LAYER 1 — BRAND PALETTE
|
|
38
|
+
Raw brand colours. Consumers must never bind to these directly.
|
|
32
39
|
============================================================ */
|
|
33
|
-
:
|
|
34
|
-
|
|
35
|
-
--braden-red-
|
|
36
|
-
--braden-
|
|
37
|
-
--braden-
|
|
38
|
-
--braden-
|
|
39
|
-
--braden-
|
|
40
|
-
--braden-
|
|
41
|
-
--braden-
|
|
42
|
-
--braden-
|
|
43
|
-
--braden-
|
|
44
|
-
--braden-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
--
|
|
52
|
-
--
|
|
53
|
-
--
|
|
54
|
-
--
|
|
55
|
-
--
|
|
56
|
-
--
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
--
|
|
63
|
-
--
|
|
64
|
-
--
|
|
65
|
-
--
|
|
66
|
-
--
|
|
67
|
-
--
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
--
|
|
73
|
-
--
|
|
74
|
-
--
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
--
|
|
80
|
-
--
|
|
81
|
-
--
|
|
82
|
-
--
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
--
|
|
88
|
-
--
|
|
89
|
-
--
|
|
90
|
-
|
|
91
|
-
--
|
|
92
|
-
--
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
--role-
|
|
101
|
-
--role-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
--role-
|
|
105
|
-
--role-
|
|
106
|
-
--role-
|
|
107
|
-
--role-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
--
|
|
142
|
-
--
|
|
143
|
-
--
|
|
144
|
-
--
|
|
145
|
-
--
|
|
146
|
-
--
|
|
147
|
-
--
|
|
148
|
-
--
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
--
|
|
165
|
-
--
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
40
|
+
--braden-red: oklch(0.488 0.170 17.6);
|
|
41
|
+
--braden-red-light: oklch(0.572 0.201 17.9);
|
|
42
|
+
--braden-red-dark: oklch(0.400 0.137 16.8);
|
|
43
|
+
--braden-gold: oklch(0.758 0.092 89.9);
|
|
44
|
+
--braden-gold-light: oklch(0.818 0.070 90.0);
|
|
45
|
+
--braden-bronze: oklch(0.698 0.110 89.2);
|
|
46
|
+
--braden-navy: oklch(0.356 0.039 249.0);
|
|
47
|
+
--braden-slate: oklch(0.710 0.018 201.3);
|
|
48
|
+
--braden-sky: oklch(0.653 0.135 242.7);
|
|
49
|
+
--braden-forest: oklch(0.663 0.160 152.4);
|
|
50
|
+
--braden-lavender: oklch(0.577 0.152 315.3);
|
|
51
|
+
--braden-indigo: oklch(0.400 0.200 270);
|
|
52
|
+
--braden-teal-deep: oklch(0.500 0.140 220);
|
|
53
|
+
--braden-aqua: oklch(0.800 0.130 198);
|
|
54
|
+
|
|
55
|
+
/* ============================================================
|
|
56
|
+
LAYER 2 — SURFACES AND BORDERS
|
|
57
|
+
============================================================ */
|
|
58
|
+
--role-bg-body: oklch(0.980 0.002 50);
|
|
59
|
+
--role-bg-surface: oklch(0.968 0.004 55);
|
|
60
|
+
--role-bg-panel: oklch(0.994 0.001 50);
|
|
61
|
+
--role-bg-input: oklch(0.972 0.003 250);
|
|
62
|
+
--role-bg-sunken: oklch(0.952 0.005 250);
|
|
63
|
+
--role-border: oklch(0.900 0.008 248);
|
|
64
|
+
--role-border-strong: oklch(0.840 0.012 248);
|
|
65
|
+
|
|
66
|
+
/* ============================================================
|
|
67
|
+
LAYER 3 — HEADING RAMP (six levels, per the contract)
|
|
68
|
+
============================================================ */
|
|
69
|
+
--role-h1: oklch(0.20 0.028 252);
|
|
70
|
+
--role-h2: oklch(0.25 0.034 250);
|
|
71
|
+
--role-h3: oklch(0.30 0.040 246);
|
|
72
|
+
--role-h4: oklch(0.35 0.046 240);
|
|
73
|
+
--role-h5: oklch(0.40 0.052 232);
|
|
74
|
+
--role-h6: oklch(0.44 0.058 224);
|
|
75
|
+
|
|
76
|
+
/* ============================================================
|
|
77
|
+
LAYER 3 — TEXT AND LINKS
|
|
78
|
+
============================================================ */
|
|
79
|
+
--role-text-heading: var(--role-h1);
|
|
80
|
+
--role-text-body: oklch(0.22 0.020 250);
|
|
81
|
+
--role-text-secondary: oklch(0.38 0.018 250);
|
|
82
|
+
--role-text-muted: oklch(0.52 0.018 250);
|
|
83
|
+
--role-text-subtle: oklch(0.60 0.012 250);
|
|
84
|
+
--role-text-disabled: oklch(0.72 0.010 250);
|
|
85
|
+
--role-text-strong: oklch(0.16 0.024 252);
|
|
86
|
+
--role-text-emphasis: oklch(0.42 0.070 240);
|
|
87
|
+
--role-link: oklch(0.480 0.140 245);
|
|
88
|
+
--role-link-hover: oklch(0.400 0.150 250);
|
|
89
|
+
--role-link-visited: oklch(0.450 0.120 300);
|
|
90
|
+
|
|
91
|
+
/* ============================================================
|
|
92
|
+
LAYER 3 — EDITORIAL (code, quote, mark, kbd)
|
|
93
|
+
============================================================ */
|
|
94
|
+
--role-code-text: oklch(0.400 0.090 230);
|
|
95
|
+
--role-code-bg: oklch(0.950 0.010 235);
|
|
96
|
+
--role-quote-text: oklch(0.400 0.060 25);
|
|
97
|
+
--role-quote-rule: var(--braden-gold);
|
|
98
|
+
--role-mark-bg: oklch(0.930 0.060 90);
|
|
99
|
+
--role-mark-text: oklch(0.24 0.040 60);
|
|
100
|
+
--role-kbd-bg: oklch(0.962 0.004 250);
|
|
101
|
+
--role-kbd-border: oklch(0.870 0.010 250);
|
|
102
|
+
|
|
103
|
+
/* ============================================================
|
|
104
|
+
LAYER 3 — SEMANTIC ROLES
|
|
105
|
+
error = --braden-indigo: Corporate primary is RED, so error cannot be.
|
|
106
|
+
============================================================ */
|
|
107
|
+
--role-primary: var(--braden-red);
|
|
108
|
+
--role-secondary: var(--braden-navy);
|
|
109
|
+
--role-accent: var(--braden-gold);
|
|
110
|
+
--role-error: var(--braden-indigo);
|
|
111
|
+
--role-destructive: var(--braden-indigo);
|
|
112
|
+
--role-warning: var(--braden-gold);
|
|
113
|
+
--role-success: var(--braden-aqua);
|
|
114
|
+
--role-info: var(--braden-teal-deep);
|
|
115
|
+
|
|
116
|
+
/* ═══ INFORMATIONAL SCALE — categorical, NOT semantic ═══
|
|
117
|
+
Added 2026-08-03 by operator ruling: "informational hues might be handy
|
|
118
|
+
to stand out against the style colour palette."
|
|
119
|
+
|
|
120
|
+
Role tokens describe STATE. They cannot describe CATEGORY — which
|
|
121
|
+
pipeline stage, which user role, which product tile. Apps that needed a
|
|
122
|
+
category reached for raw Tailwind instead, which is how one role-badge
|
|
123
|
+
table came to use purple, indigo, blue, violet and sky as its only
|
|
124
|
+
distinguishing signal.
|
|
125
|
+
|
|
126
|
+
NUMBERED, NOT NAMED, deliberately. Call one of these "moss green" and
|
|
127
|
+
it will be used for success within a week. A number says what these
|
|
128
|
+
are: interchangeable slots carrying no meaning of their own.
|
|
129
|
+
NEVER use an --info-* for state. State has role tokens.
|
|
130
|
+
|
|
131
|
+
Derived, not picked by eye: Vienot-Brettel-Mollon dichromat simulation,
|
|
132
|
+
DeltaE in OKLab, scored as the WORST case across normal, deuteranope and
|
|
133
|
+
protanope vision. Two constraints — perceptual distance keeps them
|
|
134
|
+
DISTINGUISHABLE, a >=28 degree hue gap keeps them looking CATEGORICAL
|
|
135
|
+
rather than like a light/dark pair of one colour. Every one is also
|
|
136
|
+
held away from the brand roles, which is what "stand out against the
|
|
137
|
+
style palette" requires: an informational badge must not read as a
|
|
138
|
+
primary action or an error.
|
|
139
|
+
Reproduce with scripts/design-informational-hues.py
|
|
140
|
+
|
|
141
|
+
LIMIT: worst-case separation is ~0.06-0.07, against >=0.156 for the
|
|
142
|
+
semantic pairs. That is 10x better than the purple-vs-blue pairing this
|
|
143
|
+
system rejected (0.006), but it is NOT the semantic standard and cannot
|
|
144
|
+
be — 8 mutually-distinct categories that also avoid 6 reserved brand
|
|
145
|
+
colours is a harder problem than 6 roles. So: colour is never the sole
|
|
146
|
+
signifier for a category. Always pair with a label or an icon. Past 8
|
|
147
|
+
categories, stop adding hues and add a second dimension. */
|
|
148
|
+
--info-1: oklch(0.660 0.120 0.0);
|
|
149
|
+
--info-2: oklch(0.660 0.120 30.0);
|
|
150
|
+
--info-3: oklch(0.620 0.120 72.0);
|
|
151
|
+
--info-4: oklch(0.460 0.120 132.0);
|
|
152
|
+
--info-5: oklch(0.780 0.120 162.0);
|
|
153
|
+
--info-6: oklch(0.740 0.120 222.0);
|
|
154
|
+
--info-7: oklch(0.660 0.120 252.0);
|
|
155
|
+
--info-8: oklch(0.620 0.120 324.0);
|
|
156
|
+
|
|
157
|
+
/* ═══ STAGE RAMP — progression, one hue, ascending lightness ═══
|
|
158
|
+
For an ORDERED sequence (pipeline stages), not a category set. A ramp
|
|
159
|
+
encodes direction: "further along" is visible at a glance, and it
|
|
160
|
+
survives greyscale and both dichromacies because lightness carries it.
|
|
161
|
+
Arbitrary per-stage hues cannot do that — a reader has to LEARN that
|
|
162
|
+
sky comes after amber.
|
|
163
|
+
|
|
164
|
+
Progression only. OUTCOMES are state, not stage: a hired candidate is
|
|
165
|
+
--role-success, a rejected one --role-error, a withdrawn one muted.
|
|
166
|
+
Do not extend this ramp to cover them. */
|
|
167
|
+
--stage-1: oklch(0.860 0.090 249.0);
|
|
168
|
+
--stage-2: oklch(0.780 0.090 249.0);
|
|
169
|
+
--stage-3: oklch(0.700 0.090 249.0);
|
|
170
|
+
--stage-4: oklch(0.620 0.090 249.0);
|
|
171
|
+
--stage-5: oklch(0.540 0.090 249.0);
|
|
172
|
+
--stage-6: oklch(0.460 0.090 249.0);
|
|
173
|
+
|
|
174
|
+
/* ============================================================
|
|
175
|
+
LAYER 3 — AA-SAFE TEXT RENDERINGS OF THE SATURATED ROLES
|
|
176
|
+
A saturated fill colour is not a legible text colour.
|
|
177
|
+
============================================================ */
|
|
178
|
+
--role-primary-text: oklch(0.566 0.170 17.6);
|
|
179
|
+
--role-accent-text: oklch(0.544 0.092 89.9);
|
|
180
|
+
--role-error-text: oklch(0.562 0.200 270);
|
|
181
|
+
--role-warning-text: oklch(0.544 0.092 89.9);
|
|
182
|
+
--role-success-text: oklch(0.515 0.130 198);
|
|
183
|
+
--role-info-text: oklch(0.519 0.140 220);
|
|
184
|
+
|
|
185
|
+
/* ============================================================
|
|
186
|
+
LAYER 3 — INVERSE TEXT ON COLOURED FILLS
|
|
187
|
+
============================================================ */
|
|
188
|
+
--text-on-light-fill: oklch(0.980 0.006 50);
|
|
189
|
+
--text-on-dark-fill: oklch(0.190 0.020 250);
|
|
190
|
+
--text-on-primary: var(--text-on-light-fill);
|
|
191
|
+
--text-on-error: var(--text-on-light-fill);
|
|
192
|
+
--text-on-accent: var(--text-on-dark-fill);
|
|
193
|
+
--text-on-warning: var(--text-on-dark-fill);
|
|
194
|
+
--text-on-success: var(--text-on-dark-fill);
|
|
195
|
+
--text-on-info: var(--text-on-light-fill);
|
|
196
|
+
|
|
197
|
+
/* ============================================================
|
|
198
|
+
DECORATION — shadow, glow, dot and grid
|
|
199
|
+
============================================================ */
|
|
200
|
+
--gradient-accent: linear-gradient(135deg, var(--braden-red), var(--braden-gold));
|
|
201
|
+
--shadow-xs: 0 1px 2px oklch(0.30 0.03 250 / 0.06);
|
|
202
|
+
--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
|
+
--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);
|
|
204
|
+
--shadow-lg: 0 14px 26px -8px oklch(0.28 0.04 250 / 0.14), 0 5px 10px -5px oklch(0.28 0.04 250 / 0.07);
|
|
170
205
|
--glow-card: none;
|
|
171
206
|
--glow-card-hover: none;
|
|
207
|
+
--panel-hairline: transparent;
|
|
208
|
+
--dot-color: oklch(0.42 0.030 250 / 0.14);
|
|
209
|
+
--dot-size: 16px;
|
|
210
|
+
--dot-radius: 1px;
|
|
211
|
+
--grid-color: oklch(0.40 0.030 250 / 0.07);
|
|
212
|
+
--grid-size: 32px;
|
|
213
|
+
|
|
214
|
+
/* ============================================================
|
|
215
|
+
TYPOGRAPHY AND GEOMETRY
|
|
216
|
+
============================================================ */
|
|
217
|
+
--font-display: 'Open Sans', system-ui, sans-serif;
|
|
218
|
+
--font-heading: Lato, 'Open Sans', system-ui, sans-serif;
|
|
219
|
+
--font-body: 'Open Sans', system-ui, sans-serif;
|
|
220
|
+
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
|
|
221
|
+
--track-display: -0.018em;
|
|
222
|
+
--track-heading: -0.008em;
|
|
223
|
+
--track-body: 0em;
|
|
224
|
+
--track-eyebrow: 0.10em;
|
|
225
|
+
--radius-sm: 4px;
|
|
226
|
+
--radius: 6px;
|
|
227
|
+
--radius-lg: 10px;
|
|
228
|
+
--row-pad: 11px;
|
|
172
229
|
}
|
|
173
230
|
|
|
174
231
|
/* ============================================================
|
|
175
|
-
|
|
232
|
+
DARK MODE — the contract's .dark block, verbatim.
|
|
233
|
+
Only tokens the contract actually flips appear here; anything
|
|
234
|
+
absent is mode-independent by design, not by omission.
|
|
176
235
|
============================================================ */
|
|
177
236
|
.dark {
|
|
178
|
-
|
|
179
|
-
--
|
|
180
|
-
--
|
|
181
|
-
--
|
|
182
|
-
--
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
--
|
|
186
|
-
--
|
|
187
|
-
--
|
|
188
|
-
--
|
|
189
|
-
--
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
--role-text-
|
|
193
|
-
--role-text-
|
|
194
|
-
--role-text-
|
|
195
|
-
--role-text-
|
|
196
|
-
--role-text-
|
|
197
|
-
--role-text-
|
|
198
|
-
|
|
199
|
-
--role-
|
|
200
|
-
--role-
|
|
201
|
-
--role-
|
|
202
|
-
--role-bg
|
|
203
|
-
--role-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
--
|
|
207
|
-
--
|
|
208
|
-
--
|
|
209
|
-
--
|
|
210
|
-
--
|
|
211
|
-
--
|
|
212
|
-
--
|
|
213
|
-
--
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
*/
|
|
225
|
-
--card-glow-source: var(--app-accent, var(--braden-gold-500));
|
|
226
|
-
--glow-card: 0 0 0 1px color-mix(in oklch, var(--card-glow-source) 18%, transparent),
|
|
227
|
-
0 0 10px -4px color-mix(in oklch, var(--card-glow-source) 20%, transparent);
|
|
228
|
-
--glow-card-hover: 0 0 0 1px color-mix(in oklch, var(--card-glow-source) 26%, transparent),
|
|
229
|
-
0 0 14px -4px color-mix(in oklch, var(--card-glow-source) 28%, transparent);
|
|
237
|
+
--role-bg-body: oklch(0.140 0.012 252);
|
|
238
|
+
--role-bg-surface: oklch(0.180 0.016 251);
|
|
239
|
+
--role-bg-panel: oklch(0.205 0.018 250);
|
|
240
|
+
--role-bg-input: oklch(0.245 0.020 250);
|
|
241
|
+
--role-bg-sunken: oklch(0.120 0.010 252);
|
|
242
|
+
--role-border: oklch(0.320 0.020 250);
|
|
243
|
+
--role-border-strong: oklch(0.420 0.024 250);
|
|
244
|
+
--role-h1: oklch(0.95 0.014 250);
|
|
245
|
+
--role-h2: oklch(0.92 0.018 250);
|
|
246
|
+
--role-h3: oklch(0.88 0.024 244);
|
|
247
|
+
--role-h4: oklch(0.84 0.032 236);
|
|
248
|
+
--role-h5: oklch(0.80 0.040 228);
|
|
249
|
+
--role-h6: oklch(0.75 0.048 220);
|
|
250
|
+
--role-text-body: oklch(0.93 0.014 250);
|
|
251
|
+
--role-text-secondary: oklch(0.80 0.016 250);
|
|
252
|
+
--role-text-muted: oklch(0.66 0.018 250);
|
|
253
|
+
--role-text-subtle: oklch(0.55 0.015 250);
|
|
254
|
+
--role-text-disabled: oklch(0.43 0.010 250);
|
|
255
|
+
--role-text-strong: oklch(0.97 0.010 250);
|
|
256
|
+
--role-text-emphasis: oklch(0.85 0.050 235);
|
|
257
|
+
--role-link: oklch(0.780 0.110 240);
|
|
258
|
+
--role-link-hover: oklch(0.860 0.095 225);
|
|
259
|
+
--role-link-visited: oklch(0.770 0.080 300);
|
|
260
|
+
--role-code-text: oklch(0.850 0.070 215);
|
|
261
|
+
--role-code-bg: oklch(0.250 0.026 230);
|
|
262
|
+
--role-quote-text: oklch(0.840 0.045 40);
|
|
263
|
+
--role-quote-rule: var(--braden-gold);
|
|
264
|
+
--role-mark-bg: oklch(0.360 0.060 85);
|
|
265
|
+
--role-mark-text: oklch(0.94 0.030 85);
|
|
266
|
+
--role-kbd-bg: oklch(0.250 0.020 250);
|
|
267
|
+
--role-kbd-border: oklch(0.400 0.022 250);
|
|
268
|
+
--role-primary-text: oklch(0.704 0.170 17.6);
|
|
269
|
+
--role-accent-text: oklch(0.683 0.092 89.9);
|
|
270
|
+
--role-error-text: oklch(0.711 0.200 270);
|
|
271
|
+
--role-warning-text: oklch(0.683 0.092 89.9);
|
|
272
|
+
--role-success-text: oklch(0.662 0.130 198);
|
|
273
|
+
--role-info-text: oklch(0.667 0.140 220);
|
|
274
|
+
--shadow-xs: 0 1px 2px oklch(0.07 0.01 252 / 0.5);
|
|
275
|
+
--shadow-sm: 0 1px 3px oklch(0.07 0.01 252 / 0.55);
|
|
276
|
+
--shadow-md: 0 6px 16px -6px oklch(0.07 0.01 252 / 0.6);
|
|
277
|
+
--shadow-lg: 0 18px 34px -12px oklch(0.07 0.01 252 / 0.68);
|
|
278
|
+
--glow-card: 0 0 0 1px color-mix(in oklch, var(--braden-gold) 18%, transparent), 0 0 10px -4px color-mix(in oklch, var(--braden-gold) 20%, transparent);
|
|
279
|
+
--glow-card-hover: 0 0 0 1px color-mix(in oklch, var(--braden-gold) 30%, transparent), 0 0 16px -4px color-mix(in oklch, var(--braden-gold) 32%, transparent);
|
|
280
|
+
--panel-hairline: color-mix(in oklch, var(--braden-gold) 26%, transparent);
|
|
281
|
+
--dot-color: oklch(0.72 0.060 90 / 0.16);
|
|
282
|
+
--grid-color: oklch(0.82 0.020 250 / 0.055);
|
|
230
283
|
}
|
package/src/css/vars.css
CHANGED
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
* Pure white (L=1.0) and pure black (L=0) are BANNED in EVERY role —
|
|
31
31
|
* text, surface, border, ring and shadow alike (operator ruling
|
|
32
32
|
* 2026-08-02). The prior text-only scoping of this rule is what let
|
|
33
|
-
* --light-bg-accent ship as oklch(1 0 0.5)
|
|
33
|
+
* --light-bg-accent ship as oklch(1 0 0.5) — theme-audit-ok: naming the
|
|
34
|
+
* value that was REMOVED — and render every shadcn card
|
|
34
35
|
* and popover pure white. "White" here means oklch(0.982 0.002 248)
|
|
35
36
|
* (#f8f9fa); the lightest surface is bg-panel at L=0.994.
|
|
36
37
|
*
|
|
@@ -74,7 +75,7 @@
|
|
|
74
75
|
--light-bg-primary: oklch(0.961 0 0.5);
|
|
75
76
|
--light-bg-secondary: oklch(0.982 0.002 248);
|
|
76
77
|
--light-bg-tertiary: oklch(0.963 0.003 228.9);
|
|
77
|
-
/* bg-panel. Was oklch(1 0 0.5) — pure white, and since --card and
|
|
78
|
+
/* bg-panel. theme-audit-ok: prose. Was oklch(1 0 0.5) — pure white, and since --card and
|
|
78
79
|
--popover bridge to it, every shadcn card and popover in all five D2C
|
|
79
80
|
apps rendered pure white in light mode from this one line. */
|
|
80
81
|
--light-bg-accent: oklch(0.994 0.002 260);
|
|
@@ -126,6 +127,64 @@
|
|
|
126
127
|
--role-success: var(--neon-electric-teal); /* was green — 0.043 from red */
|
|
127
128
|
--role-info: var(--neon-electric-cyan); /* oklch(0.769 0.132 191.7) */
|
|
128
129
|
|
|
130
|
+
/* ═══ INFORMATIONAL SCALE — categorical, NOT semantic ═══
|
|
131
|
+
Added 2026-08-03 by operator ruling: "informational hues might be handy
|
|
132
|
+
to stand out against the style colour palette."
|
|
133
|
+
|
|
134
|
+
Role tokens describe STATE. They cannot describe CATEGORY — which
|
|
135
|
+
pipeline stage, which user role, which product tile. Apps that needed a
|
|
136
|
+
category reached for raw Tailwind instead, which is how one role-badge
|
|
137
|
+
table came to use purple, indigo, blue, violet and sky as its only
|
|
138
|
+
distinguishing signal.
|
|
139
|
+
|
|
140
|
+
NUMBERED, NOT NAMED, deliberately. Call one of these "moss green" and
|
|
141
|
+
it will be used for success within a week. A number says what these
|
|
142
|
+
are: interchangeable slots carrying no meaning of their own.
|
|
143
|
+
NEVER use an --info-* for state. State has role tokens.
|
|
144
|
+
|
|
145
|
+
Derived, not picked by eye: Vienot-Brettel-Mollon dichromat simulation,
|
|
146
|
+
DeltaE in OKLab, scored as the WORST case across normal, deuteranope and
|
|
147
|
+
protanope vision. Two constraints — perceptual distance keeps them
|
|
148
|
+
DISTINGUISHABLE, a >=28 degree hue gap keeps them looking CATEGORICAL
|
|
149
|
+
rather than like a light/dark pair of one colour. Every one is also
|
|
150
|
+
held away from the brand roles, which is what "stand out against the
|
|
151
|
+
style palette" requires: an informational badge must not read as a
|
|
152
|
+
primary action or an error.
|
|
153
|
+
Reproduce with scripts/design-informational-hues.py
|
|
154
|
+
|
|
155
|
+
LIMIT: worst-case separation is ~0.06-0.07, against >=0.156 for the
|
|
156
|
+
semantic pairs. That is 10x better than the purple-vs-blue pairing this
|
|
157
|
+
system rejected (0.006), but it is NOT the semantic standard and cannot
|
|
158
|
+
be — 8 mutually-distinct categories that also avoid 6 reserved brand
|
|
159
|
+
colours is a harder problem than 6 roles. So: colour is never the sole
|
|
160
|
+
signifier for a category. Always pair with a label or an icon. Past 8
|
|
161
|
+
categories, stop adding hues and add a second dimension. */
|
|
162
|
+
--info-1: oklch(0.460 0.140 0.0);
|
|
163
|
+
--info-2: oklch(0.700 0.140 42.0);
|
|
164
|
+
--info-3: oklch(0.540 0.140 138.0);
|
|
165
|
+
--info-4: oklch(0.700 0.140 168.0);
|
|
166
|
+
--info-5: oklch(0.780 0.140 228.0);
|
|
167
|
+
--info-6: oklch(0.700 0.140 258.0);
|
|
168
|
+
--info-7: oklch(0.500 0.140 300.0);
|
|
169
|
+
--info-8: oklch(0.460 0.140 330.0);
|
|
170
|
+
|
|
171
|
+
/* ═══ STAGE RAMP — progression, one hue, ascending lightness ═══
|
|
172
|
+
For an ORDERED sequence (pipeline stages), not a category set. A ramp
|
|
173
|
+
encodes direction: "further along" is visible at a glance, and it
|
|
174
|
+
survives greyscale and both dichromacies because lightness carries it.
|
|
175
|
+
Arbitrary per-stage hues cannot do that — a reader has to LEARN that
|
|
176
|
+
sky comes after amber.
|
|
177
|
+
|
|
178
|
+
Progression only. OUTCOMES are state, not stage: a hired candidate is
|
|
179
|
+
--role-success, a rejected one --role-error, a withdrawn one muted.
|
|
180
|
+
Do not extend this ramp to cover them. */
|
|
181
|
+
--stage-1: oklch(0.860 0.130 262.9);
|
|
182
|
+
--stage-2: oklch(0.780 0.130 262.9);
|
|
183
|
+
--stage-3: oklch(0.700 0.130 262.9);
|
|
184
|
+
--stage-4: oklch(0.620 0.130 262.9);
|
|
185
|
+
--stage-5: oklch(0.540 0.130 262.9);
|
|
186
|
+
--stage-6: oklch(0.460 0.130 262.9);
|
|
187
|
+
|
|
129
188
|
/* AA-safe text renderings of the saturated roles on light surfaces.
|
|
130
189
|
A saturated fill colour is not a legible text colour; these are the
|
|
131
190
|
text variants and are the ONLY correct token for coloured type. */
|
|
@@ -138,7 +197,18 @@
|
|
|
138
197
|
/* Text roles — six-tier scale (W3 §3.1 contract). Every tier below is
|
|
139
198
|
measured against --role-bg-body (the conservative floor); all tiers
|
|
140
199
|
score HIGHER against --role-bg-panel/--role-bg-surface. */
|
|
141
|
-
|
|
200
|
+
/* Six-level heading ramp — contract §heading-ramp. Each level shifts hue as
|
|
201
|
+
well as lightness, so the hierarchy reads in greyscale and under both
|
|
202
|
+
dichromacies, not only by size. --role-text-heading is h1 by definition;
|
|
203
|
+
it used to carry its own literal (chroma 0.018 against the contract's
|
|
204
|
+
0.020) which is how a token drifts off-palette one hair at a time. */
|
|
205
|
+
--role-h1: oklch(0.20 0.020 263);
|
|
206
|
+
--role-h2: oklch(0.26 0.026 258);
|
|
207
|
+
--role-h3: oklch(0.31 0.034 248);
|
|
208
|
+
--role-h4: oklch(0.36 0.042 236);
|
|
209
|
+
--role-h5: oklch(0.41 0.050 222);
|
|
210
|
+
--role-h6: oklch(0.45 0.058 208);
|
|
211
|
+
--role-text-heading: var(--role-h1); /* 16.17:1 ✓ AAA */
|
|
142
212
|
--role-text-body: var(--light-text-primary); /* 15.46:1 ✓ AAA */
|
|
143
213
|
--role-text-secondary: var(--light-text-secondary); /* 8.94:1 ✓ AAA */
|
|
144
214
|
--role-text-muted: var(--light-text-muted); /* 4.92:1 ✓ AA normal */
|
|
@@ -154,7 +224,7 @@
|
|
|
154
224
|
--role-border: var(--light-border);
|
|
155
225
|
--role-border-strong: oklch(0.880 0.008 250);
|
|
156
226
|
|
|
157
|
-
/* Scrim — the veil behind a modal, and the reason `bg-black/N` survived
|
|
227
|
+
/* Scrim — the veil behind a modal, and the reason `bg-black/N` survived theme-audit-ok: prose
|
|
158
228
|
everywhere else. It was the single most duplicated pure endpoint in the
|
|
159
229
|
tree (9 sites across nav-core, page-builder and schema-builder) for one
|
|
160
230
|
reason: no overlay role existed, so every author reached for the only
|
|
@@ -301,7 +371,7 @@
|
|
|
301
371
|
NO sRGB FALLBACK — DELIBERATELY REMOVED 2026-08-02.
|
|
302
372
|
Precedent: precedent__bsuite__20260802__theme_conformance_is_absolute.
|
|
303
373
|
|
|
304
|
-
The `@supports not (color: oklch(0 0 0))` block that stood here shipped
|
|
374
|
+
theme-audit-ok: prose. The `@supports not (color: oklch(0 0 0))` block that stood here shipped
|
|
305
375
|
a parallel hex palette. It is gone, and must not be reinstated:
|
|
306
376
|
|
|
307
377
|
· OKLCH has been Baseline-available in every evergreen engine since
|
|
@@ -320,12 +390,13 @@
|
|
|
320
390
|
DARK MODE — Layer 2 surface overrides + Layer 3 role rebind
|
|
321
391
|
============================================================ */
|
|
322
392
|
.dark {
|
|
323
|
-
/* Dark surface primitives
|
|
393
|
+
/* Dark surface primitives.
|
|
394
|
+
--dark-bg-tertiary, -quaternary, -accent and --dark-hover were DELETED
|
|
395
|
+
2026-08-03: four tokens, zero consumers anywhere in the monorepo or the
|
|
396
|
+
five apps, and none of their values appears in the contract. A palette
|
|
397
|
+
entry nobody binds to is not a spare part — it is a second source of
|
|
398
|
+
truth waiting for someone to reach for it. */
|
|
324
399
|
--dark-bg-primary: oklch(0.166 0.026 269.4);
|
|
325
|
-
--dark-bg-secondary: oklch(0.242 0.03 269.9);
|
|
326
|
-
--dark-bg-tertiary: oklch(0.326 0.036 266.7);
|
|
327
|
-
--dark-bg-quaternary: oklch(0.39 0.035 265);
|
|
328
|
-
--dark-bg-accent: oklch(0.292 0.034 270);
|
|
329
400
|
|
|
330
401
|
/* D2C dark text scale — hue 260, capped L=0.94.
|
|
331
402
|
Pure white (L=1.0) is BANNED as a text token.
|
|
@@ -342,10 +413,19 @@
|
|
|
342
413
|
--dark-text-disabled: oklch(0.44 0.010 260); /* disabled — 2.48:1 ✗ fails AA at any size — MUST pair with icon cue */
|
|
343
414
|
|
|
344
415
|
--dark-border: oklch(0.428 0.015 248.2);
|
|
345
|
-
|
|
416
|
+
|
|
417
|
+
/* Six-level heading ramp — contract §heading-ramp. Every level is a
|
|
418
|
+
DIFFERENT hue as well as a different lightness, so the hierarchy survives
|
|
419
|
+
greyscale and both dichromacies rather than relying on size alone. */
|
|
420
|
+
--role-h1: oklch(0.95 0.016 262);
|
|
421
|
+
--role-h2: oklch(0.92 0.018 264);
|
|
422
|
+
--role-h3: oklch(0.88 0.026 240);
|
|
423
|
+
--role-h4: oklch(0.84 0.040 220);
|
|
424
|
+
--role-h5: oklch(0.80 0.052 200);
|
|
425
|
+
--role-h6: oklch(0.75 0.062 195);
|
|
346
426
|
|
|
347
427
|
/* Rebind roles to dark-mode surface values — six-tier scale (W3 §3.1). */
|
|
348
|
-
--role-text-heading:
|
|
428
|
+
--role-text-heading: var(--role-h1);
|
|
349
429
|
--role-text-body: var(--dark-text-primary); /* 16.16:1 ✓ AAA */
|
|
350
430
|
--role-text-secondary: var(--dark-text-secondary); /* 11.04:1 ✓ AAA */
|
|
351
431
|
--role-text-muted: var(--dark-text-muted); /* 6.69:1 ✓ AA normal */
|
|
@@ -353,7 +433,7 @@
|
|
|
353
433
|
--role-text-disabled: var(--dark-text-disabled); /* 2.48:1 ✗ icon cue required */
|
|
354
434
|
|
|
355
435
|
--role-bg-body: var(--dark-bg-primary);
|
|
356
|
-
--role-bg-surface:
|
|
436
|
+
--role-bg-surface: oklch(0.212 0.028 269);
|
|
357
437
|
--role-bg-panel: oklch(0.190 0.020 260);
|
|
358
438
|
--role-bg-input: oklch(0.240 0.020 260);
|
|
359
439
|
--role-bg-sunken: oklch(0.145 0.022 268);
|
package/src/preset-v4.css
CHANGED
|
@@ -102,6 +102,30 @@
|
|
|
102
102
|
--color-warning-text: var(--role-warning-text);
|
|
103
103
|
--color-success-text: var(--role-success-text);
|
|
104
104
|
|
|
105
|
+
/* Informational scale — CATEGORICAL, never state. `bg-info-3`, `text-info-5`.
|
|
106
|
+
Numbered because they are interchangeable slots with no meaning of their
|
|
107
|
+
own; naming one invites it to be used as a semantic. Pair every one with a
|
|
108
|
+
label or icon — their separation is deliberately below the semantic
|
|
109
|
+
standard and cannot carry meaning alone. See --info-* in vars.css. */
|
|
110
|
+
--color-info-1: var(--info-1);
|
|
111
|
+
--color-info-2: var(--info-2);
|
|
112
|
+
--color-info-3: var(--info-3);
|
|
113
|
+
--color-info-4: var(--info-4);
|
|
114
|
+
--color-info-5: var(--info-5);
|
|
115
|
+
--color-info-6: var(--info-6);
|
|
116
|
+
--color-info-7: var(--info-7);
|
|
117
|
+
--color-info-8: var(--info-8);
|
|
118
|
+
|
|
119
|
+
/* Stage ramp — ORDERED progression. `bg-stage-1` .. `bg-stage-6`, lightness
|
|
120
|
+
descending, so "further along" reads at a glance and survives greyscale.
|
|
121
|
+
Outcomes are NOT stages: hired is --role-success, rejected --role-error. */
|
|
122
|
+
--color-stage-1: var(--stage-1);
|
|
123
|
+
--color-stage-2: var(--stage-2);
|
|
124
|
+
--color-stage-3: var(--stage-3);
|
|
125
|
+
--color-stage-4: var(--stage-4);
|
|
126
|
+
--color-stage-5: var(--stage-5);
|
|
127
|
+
--color-stage-6: var(--stage-6);
|
|
128
|
+
|
|
105
129
|
/* Per-app accent (Layer 5). Resolves through [data-app] on <body>,
|
|
106
130
|
falling back to the shared accent when no app is declared. */
|
|
107
131
|
--color-app-accent: var(--app-accent, var(--role-accent));
|