@bsuite/theme 0.4.2 → 0.6.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/README.md CHANGED
@@ -126,6 +126,18 @@ Braden keeps separate corporate identity tokens in `@bsuite/theme/braden-css`: B
126
126
 
127
127
  WCAG AA compliance: use semantic text tokens (`text-foreground`, `text-muted-foreground`, `text-text-on-primary`, `text-text-on-accent`) rather than raw `text-white`/`text-black`. Dark-surface text is capped at L=0.94 for extended-session comfort.
128
128
 
129
+ ## Text-role contract (v0.6.0)
130
+
131
+ Six measured tiers — `--role-text-heading`, `--role-text-body`, `--role-text-secondary`, `--role-text-muted`, `--role-text-subtle`, `--role-text-disabled` — each with documented WCAG contrast in both modes. Full numbers + methodology: `docs/TOKEN-MAPPING.md` §8.1.
132
+
133
+ ## Canonical gradient (v0.6.0)
134
+
135
+ Exactly one decorative accent gradient exists suite-wide — `--gradient-accent` / `.bsuite-accent-gradient` (`--role-primary` → `--role-accent`). Marketing heroes only, never functional text. `docs/TOKEN-MAPPING.md` §8.2.
136
+
137
+ ## Grid/dot doctrine (v0.6.0)
138
+
139
+ `<HeroGrid>` (public/pre-auth hero bands) and `<DotPattern>` (authenticated shells, `@bsuite/ui`) are mutually exclusive per context and enforced by `@bsuite/dry-lint`'s `no-grid-dot-doctrine-violation` rule. `docs/TOKEN-MAPPING.md` §8.3.
140
+
129
141
  ## License
130
142
 
131
143
  UNLICENSED — internal BSuite use only.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsuite/theme",
3
- "version": "0.4.2",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -164,6 +164,11 @@
164
164
  --shadow-sm: 0 1px 2px oklch(0 0 0 / 0.08);
165
165
  --shadow-md: 0 4px 6px oklch(0 0 0 / 0.10), 0 2px 4px oklch(0 0 0 / 0.06);
166
166
  --shadow-lg: 0 10px 15px oklch(0 0 0 / 0.10), 0 4px 6px oklch(0 0 0 / 0.05);
167
+
168
+ /* Card glow is a dark-mode-only device (see .dark below); light mode
169
+ stays border-only, consistent with the corporate "no neon glow" rule. */
170
+ --glow-card: none;
171
+ --glow-card-hover: none;
167
172
  }
168
173
 
169
174
  /* ============================================================
@@ -206,4 +211,20 @@
206
211
  --muted-foreground:var(--braden-dark-text-muted);
207
212
  --border: oklch(0.30 0.020 250);
208
213
  --input: oklch(0.30 0.020 250);
214
+
215
+ /*
216
+ * Card glow — Braden dark mode gets a restrained gold separation glow,
217
+ * NOT the D2C neon treatment (no cyan, lower alpha, same two-layer
218
+ * ring+halo shape for consumer-code parity with @bsuite/theme's D2C
219
+ * --glow-card). Source is --app-accent (Braden Gold), which is
220
+ * inherited from :root since this file does not rebind it per-mode.
221
+ * Deliberately more subdued than D2C to honour the "corporate
222
+ * subtle, no neon glow" shadow philosophy above, adapted for dark UI
223
+ * where SOME separation glow is still needed for surface legibility.
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);
209
230
  }
package/src/css/index.css CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * @bsuite/theme — root CSS entry point (D2C Neon Electric brand)
3
- * Version 0.3.0
3
+ * Version 0.6.0
4
4
  *
5
5
  * DUAL-BRAND ENTRY POINTS:
6
6
  *
@@ -47,6 +47,47 @@
47
47
  box-shadow 300ms ease;
48
48
  }
49
49
 
50
+ /* ============================================================
51
+ CANONICAL ACCENT GRADIENT — W3 §3.1
52
+ Exactly ONE gradient exists suite-wide: --role-primary -> --role-accent
53
+ (Electric Blue -> Cyan by default; follows tenant BrandingProvider
54
+ overrides automatically via the var() chain). Marketing/pre-auth hero
55
+ surfaces ONLY — never functional text, labels, or body copy. Apply to
56
+ a decorative element (underline, badge, background swatch) or to text
57
+ via `background-clip: text` at the call site — this utility only
58
+ supplies the gradient itself.
59
+ ============================================================ */
60
+ .bsuite-accent-gradient {
61
+ background-image: var(--gradient-accent);
62
+ }
63
+
64
+ /* ============================================================
65
+ HERO GRID — W3 §3.3 grid/dot doctrine
66
+ Public/pre-auth marketing hero bands ONLY. Localised to the hero
67
+ wrapper (which must be `position: relative`); never full-viewport,
68
+ never behind authenticated content. Mutually exclusive with the DOT
69
+ pattern (packages/ui DotPattern), which is authenticated-shell-only.
70
+ Consumed via the <HeroGrid> component in @bsuite/ui (packages/ui/src/
71
+ hero-grid.tsx) — do not hand-roll this pattern; the grid/dot lint rule
72
+ (@bsuite/dry-lint no-grid-dot-doctrine-violation) flags duplicates.
73
+ Lifted verbatim from braden's `.platform-hero-grid`
74
+ (braden/src/index.css) so the corporate and D2C marketing surfaces
75
+ share one visual language for this specific device.
76
+ ============================================================ */
77
+ .bsuite-hero-grid {
78
+ position: absolute;
79
+ inset: 0;
80
+ background-image:
81
+ linear-gradient(oklch(0 0 0 / 0.03) 1px, transparent 1px),
82
+ linear-gradient(90deg, oklch(0 0 0 / 0.03) 1px, transparent 1px);
83
+ background-size: 32px 32px;
84
+ }
85
+ .dark .bsuite-hero-grid {
86
+ background-image:
87
+ linear-gradient(oklch(1 0 0 / 0.03) 1px, transparent 1px),
88
+ linear-gradient(90deg, oklch(1 0 0 / 0.03) 1px, transparent 1px);
89
+ }
90
+
50
91
  /* ===== prefers-reduced-motion — cap animation duration ===== */
51
92
  @media (prefers-reduced-motion: reduce) {
52
93
  *,
package/src/css/vars.css CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * @bsuite/theme — CSS custom properties (:root + .dark)
3
- * Version 0.3.0
3
+ * Version 0.6.0
4
4
  *
5
5
  * Architecture — 5 layers (inner → outer):
6
6
  * 1. Palette — frozen oklch primitives (--neon-electric-*, --braden-*)
@@ -52,12 +52,17 @@
52
52
  --light-bg-accent: oklch(1 0 0.5);
53
53
 
54
54
  /* D2C light text scale — hue 260 (cool blue-grey), capped L=0.22.
55
- Verified WCAG AA/AAA on --light-bg-primary oklch(0.961 0 0.5). */
56
- --light-text-primary: oklch(0.22 0.015 260); /* 13.2:1 AAA */
57
- --light-text-secondary: oklch(0.38 0.018 260); /* 8.1:1 ✓ AAA */
58
- --light-text-muted: oklch(0.52 0.018 260); /* 4.9:1 AA */
59
- --light-text-subtle: oklch(0.60 0.012 260); /* 3.8:1 AA large */
60
- --light-text-disabled: oklch(0.72 0.010 260); /* pair with icon cue */
55
+ Measured WCAG contrast vs --light-bg-primary oklch(0.961 0 0.5), OKLCH ->
56
+ linear-sRGB -> WCAG relative-luminance pipeline, round-trip-verified
57
+ against 6 known hex references (packages/theme/docs/TOKEN-MAPPING.md §8).
58
+ Ratios re-measured 2026-07-17 (W3 §3.1) supersede prior approximate
59
+ inline values; vs --light-bg-accent (bg-panel, pure white) is always
60
+ HIGHER, so bg-body is the conservative floor for every tier below. */
61
+ --light-text-primary: oklch(0.22 0.015 260); /* 15.46:1 ✓ AAA (large+normal) */
62
+ --light-text-secondary: oklch(0.38 0.018 260); /* 8.94:1 ✓ AAA (large+normal) */
63
+ --light-text-muted: oklch(0.52 0.018 260); /* 4.92:1 ✓ AA (normal, 4.5:1 floor) */
64
+ --light-text-subtle: oklch(0.60 0.012 260); /* 3.52:1 ✓ AA large only (3:1) — ✗ AA normal */
65
+ --light-text-disabled: oklch(0.72 0.010 260); /* 2.21:1 ✗ fails AA at any size — MUST pair with icon cue */
61
66
  /* Legacy aliases (kept for consumers that haven't migrated) */
62
67
  --light-text-tertiary: var(--light-text-muted);
63
68
  --light-text-quaternary: var(--light-text-subtle);
@@ -91,13 +96,15 @@
91
96
  --role-success: var(--neon-electric-green); /* oklch(0.723 0.192 149.6) */
92
97
  --role-info: var(--neon-electric-cyan); /* oklch(0.769 0.132 191.7) */
93
98
 
94
- /* Text roles */
95
- --role-text-body: var(--light-text-primary);
96
- --role-text-secondary: var(--light-text-secondary);
97
- --role-text-muted: var(--light-text-muted);
98
- --role-text-subtle: var(--light-text-subtle);
99
- --role-text-disabled: var(--light-text-disabled);
100
- --role-text-heading: oklch(0.20 0.018 263); /* slightly darker + bluer — 14.5:1 ✓ AAA */
99
+ /* Text roles — six-tier scale (W3 §3.1 contract). Every tier below is
100
+ measured against --role-bg-body (the conservative floor); all tiers
101
+ score HIGHER against --role-bg-panel/--role-bg-surface. */
102
+ --role-text-heading: oklch(0.20 0.018 263); /* 16.17:1 ✓ AAA (slightly darker + bluer than body) */
103
+ --role-text-body: var(--light-text-primary); /* 15.46:1 ✓ AAA */
104
+ --role-text-secondary: var(--light-text-secondary); /* 8.94:1 ✓ AAA */
105
+ --role-text-muted: var(--light-text-muted); /* 4.92:1 ✓ AA normal */
106
+ --role-text-subtle: var(--light-text-subtle); /* 3.52:1 ✓ AA large only */
107
+ --role-text-disabled: var(--light-text-disabled); /* 2.21:1 ✗ icon cue required */
101
108
 
102
109
  /* Surface roles */
103
110
  --role-bg-body: var(--light-bg-primary);
@@ -154,6 +161,17 @@
154
161
  --color-accent-text: oklch(0.486 0.084 191.5); /* Dark cyan — AA on light bg */
155
162
  --color-primary-text: oklch(0.485 0.243 263.6); /* Saturated blue — AA on light bg */
156
163
 
164
+ /* ============================================================
165
+ CANONICAL ACCENT GRADIENT (W3 §3.1) — exactly ONE gradient
166
+ exists suite-wide. Marketing/pre-auth heroes ONLY — never on
167
+ functional text (labels, body copy, buttons). Consumed via the
168
+ `.bsuite-accent-gradient` utility (utilities.css). App-local
169
+ decorative gradients (e.g. crm7's `.crm7-gradient`) are
170
+ quarantined and must not be used for new work — see
171
+ docs/TOKEN-MAPPING.md §8.
172
+ ============================================================ */
173
+ --gradient-accent: linear-gradient(135deg, var(--role-primary), var(--role-accent));
174
+
157
175
  /* ============================================================
158
176
  GLOW + SHADOW MULTIPLIERS
159
177
  ============================================================ */
@@ -161,24 +179,52 @@
161
179
  --shadow-strength: 1;
162
180
 
163
181
  /* ============================================================
164
- SRGB FALLBACKS (for browsers without oklch support)
165
- Tested against Chrome 105-. Values are closest sRGB equivalents.
182
+ CARD SURFACE GLOW subtle neon separation, dark mode only.
183
+ Light mode keeps the existing border-only treatment (--role-border);
184
+ these resolve to `none` here so any consumer applying the utility
185
+ unconditionally (i.e. without a `dark:` guard) degrades safely.
166
186
  ============================================================ */
167
- @supports not (color: oklch(0 0 0)) {
168
- --neon-electric-blue: #3b82f6;
169
- --neon-electric-cyan: #06b6d4;
170
- --neon-electric-indigo: #6366f1;
171
- --neon-electric-purple: #a855f7;
172
- --neon-electric-magenta: #f472b6;
187
+ --card-glow-source: var(--app-accent, var(--role-accent));
188
+ --glow-card: none;
189
+ --glow-card-hover: none;
190
+ }
191
+
192
+ /* ============================================================
193
+ SRGB FALLBACKS (for browsers without oklch support) — W3 §3.4
194
+ True-source hexes, each re-derived directly from its oklch token
195
+ (OKLCH -> OKLab -> linear-sRGB -> gamma-encoded sRGB) rather than
196
+ eyeballed against a Tailwind palette. Round-trip-verified: every
197
+ value below computes ΔE(OKLab) < 0.01 against its oklch token —
198
+ at least twice inside the < 0.02 acceptance bar (most values are
199
+ one to two orders of magnitude inside it; see docs/TOKEN-MAPPING.md
200
+ §8 for the full per-token ΔE table). The prior Tailwind-palette-
201
+ derived hexes were ΔE 0.03-0.10 off.
202
+
203
+ Deliberately declared at the TOP LEVEL, not nested inside another
204
+ `:root { }` block: engines that lack `oklch()` support predate CSS
205
+ nesting too, so a `@supports` block nested inside `:root` would be
206
+ silently dropped by exactly the browsers it's meant to serve when
207
+ this file is consumed raw (unbundled). It works when bundled
208
+ through the Tailwind v4 / LightningCSS pipeline only because that
209
+ pipeline hoists nested at-rules — this top-level form is correct
210
+ for both the raw and the bundled case.
211
+ ============================================================ */
212
+ @supports not (color: oklch(0 0 0)) {
213
+ :root {
214
+ --neon-electric-blue: #2563eb;
215
+ --neon-electric-cyan: #00cec9;
216
+ --neon-electric-indigo: #4f46e5;
217
+ --neon-electric-purple: #6c5ce7;
218
+ --neon-electric-magenta: #fd79a8;
173
219
  --neon-electric-pink: #ec4899;
174
- --neon-electric-coral: #f87171;
175
- --neon-electric-orange: #f97316;
176
- --neon-electric-yellow: #fbbf24;
220
+ --neon-electric-coral: #ff4757;
221
+ --neon-electric-orange: #ff7675;
222
+ --neon-electric-yellow: #fdcb6e;
177
223
  --neon-electric-green: #22c55e;
178
- --neon-electric-lavender: #c084fc;
179
- --role-error: #a855f7;
180
- --role-destructive: #a855f7;
181
- --light-text-primary: #1e1f2e;
224
+ --neon-electric-lavender: #a29bfe;
225
+ --role-error: #6c5ce7;
226
+ --role-destructive: #6c5ce7;
227
+ --light-text-primary: #171b22;
182
228
  --dark-text-primary: #e8eaf6;
183
229
  }
184
230
  }
@@ -197,12 +243,16 @@
197
243
  /* D2C dark text scale — hue 260, capped L=0.94.
198
244
  Pure white (L=1.0) is BANNED as a text token.
199
245
  Eye-strain: 16:1 → ~14:1; perceived glare drops significantly.
200
- Verified WCAG AA on --dark-bg-primary oklch(0.166 0.026 269.4). */
201
- --dark-text-primary: oklch(0.94 0.012 260); /* body — ~13.9:1 AAA */
202
- --dark-text-secondary: oklch(0.82 0.015 260); /* labels — ~9.8:1 AAA */
203
- --dark-text-muted: oklch(0.68 0.018 260); /* metadata — ~5.6:1 AA */
204
- --dark-text-subtle: oklch(0.56 0.015 260); /* placeholders — ~3.6:1 ✓ AA large */
205
- --dark-text-disabled: oklch(0.44 0.010 260); /* disabled pair with icon cue */
246
+ Measured WCAG contrast vs --dark-bg-primary oklch(0.166 0.026 269.4)
247
+ via the same OKLCH -> linear-sRGB -> WCAG pipeline as the light scale
248
+ above (re-measured 2026-07-17, W3 §3.1 supersedes prior approximate
249
+ inline values). vs --role-bg-surface/--role-bg-panel is always HIGHER,
250
+ so bg-body is the conservative floor. */
251
+ --dark-text-primary: oklch(0.94 0.012 260); /* body 16.16:1 AAA */
252
+ --dark-text-secondary: oklch(0.82 0.015 260); /* labels — 11.04:1 ✓ AAA */
253
+ --dark-text-muted: oklch(0.68 0.018 260); /* metadata — 6.69:1 ✓ AA (normal) */
254
+ --dark-text-subtle: oklch(0.56 0.015 260); /* placeholders — 4.14:1 ✓ AA large only — ✗ AA normal */
255
+ --dark-text-disabled: oklch(0.44 0.010 260); /* disabled — 2.48:1 ✗ fails AA at any size — MUST pair with icon cue */
206
256
  /* Legacy aliases */
207
257
  --dark-text-tertiary: var(--dark-text-muted);
208
258
  --dark-text-quaternary: var(--dark-text-disabled);
@@ -210,13 +260,13 @@
210
260
  --dark-border: oklch(0.428 0.015 248.2);
211
261
  --dark-hover: oklch(0.39 0.035 265);
212
262
 
213
- /* Rebind roles to dark-mode surface values */
214
- --role-text-body: var(--dark-text-primary);
215
- --role-text-secondary: var(--dark-text-secondary);
216
- --role-text-muted: var(--dark-text-muted);
217
- --role-text-subtle: var(--dark-text-subtle);
218
- --role-text-disabled: var(--dark-text-disabled);
219
- --role-text-heading: oklch(0.92 0.014 260); /* ~12.8:1 AAA */
263
+ /* Rebind roles to dark-mode surface values — six-tier scale (W3 §3.1). */
264
+ --role-text-heading: oklch(0.92 0.014 260); /* 15.21:1 ✓ AAA */
265
+ --role-text-body: var(--dark-text-primary); /* 16.16:1 ✓ AAA */
266
+ --role-text-secondary: var(--dark-text-secondary); /* 11.04:1 ✓ AAA */
267
+ --role-text-muted: var(--dark-text-muted); /* 6.69:1 ✓ AA normal */
268
+ --role-text-subtle: var(--dark-text-subtle); /* 4.14:1 ✓ AA large only */
269
+ --role-text-disabled: var(--dark-text-disabled); /* 2.48:1 icon cue required */
220
270
 
221
271
  --role-bg-body: var(--dark-bg-primary);
222
272
  --role-bg-surface: var(--dark-bg-secondary);
@@ -233,5 +283,22 @@
233
283
  --glow-strength: 0.6;
234
284
  --shadow-strength: 0.8;
235
285
 
286
+ /*
287
+ * Card glow — subtle neon separation for card surfaces, dark mode only.
288
+ * Derived from the app's accent colour (--app-accent, falling back to
289
+ * the shared --role-accent default) via color-mix() so per-tenant
290
+ * BrandingProvider overrides and per-app brand accents (Conduit green,
291
+ * R80.3 amber, etc.) automatically re-colour the glow. Two layers:
292
+ * a 1px accent ring (edge definition, replaces a hard border) plus a
293
+ * soft negative-spread halo (ambient separation). Kept subtle by
294
+ * design — this is surface separation, not a decorative outline.
295
+ */
296
+ --glow-card:
297
+ 0 0 0 1px color-mix(in oklch, var(--card-glow-source) 30%, transparent),
298
+ 0 0 14px -4px color-mix(in oklch, var(--card-glow-source) 35%, transparent);
299
+ --glow-card-hover:
300
+ 0 0 0 1px color-mix(in oklch, var(--card-glow-source) 42%, transparent),
301
+ 0 0 20px -4px color-mix(in oklch, var(--card-glow-source) 48%, transparent);
302
+
236
303
  /* Shadcn bridge picks up rebindings automatically via var() chain */
237
304
  }
@@ -1,22 +0,0 @@
1
- /*
2
- * runtime-branding.css — populated at runtime by <BrandingProvider>
3
- *
4
- * DO NOT add static values here. BrandingProvider reads the tenant's
5
- * branding JSON from Supabase and calls
6
- * document.documentElement.style.setProperty('--primary', value)
7
- * for each key. This file is a documentation placeholder only.
8
- *
9
- * Keys populated at runtime (if tenant has branding configured):
10
- * --primary (oklch string)
11
- * --accent (oklch string)
12
- * --accent-primary (oklch string)
13
- * --app-primary (oklch string)
14
- * --app-accent (oklch string)
15
- * Logo URLs are injected via JS into CSS vars:
16
- * --logo-url (url() string)
17
- * --mark-url (url() string)
18
- */
19
-
20
- :root[data-branding-loaded="true"] {
21
- /* intentionally empty — BrandingProvider injects vars directly via JS */
22
- }
@@ -1,43 +0,0 @@
1
- /*
2
- * @bsuite/theme — tokens-brand-corporate-braden.css
3
- *
4
- * Braden corporate brand deviation.
5
- * Applied via .theme-braden or data-brand="braden" — NEVER auto-applied.
6
- *
7
- * ⚠️ This file is EXEMPT from the D2C oklch-only rule (BRADEN-EXEMPT).
8
- * Do NOT import this file from index.css or any D2C app entry point.
9
- * Braden's own stylesheet opts in explicitly.
10
- *
11
- * Source of truth: TOKEN-MAPPING.md §4 + §6
12
- */
13
-
14
- .theme-braden,
15
- [data-brand="braden"] {
16
-
17
- /* ===== Braden brand primitives ===== */
18
- --app-primary: oklch(0.488 0.170 17.6); /* Braden Red */
19
- --app-accent: oklch(0.769 0.096 90.9); /* Braden Gold */
20
- --app-primary-dark: oklch(0.400 0.137 16.8); /* Braden Red — pressed/hover state */
21
-
22
- /* ===== Background ===== */
23
- --bg-body: oklch(1 0 0);
24
- --bg-panel: oklch(1 0 0);
25
- --bg-surface: oklch(0.975 0 0);
26
-
27
- /* ===== Text ===== */
28
- --text-primary: oklch(0.156 0 0);
29
-
30
- /* ===== Border ===== */
31
- --border-color: oklch(0.916 0 0);
32
-
33
- /* ===== Accent / brand ===== */
34
- --accent-primary: oklch(0.488 0.170 17.6); /* Braden Red */
35
- --accent-secondary: oklch(0.769 0.096 90.9); /* Braden Gold */
36
-
37
- /* ===== shadcn/ui bridge tokens ===== */
38
- --primary: oklch(0.488 0.170 17.6); /* Braden Red */
39
- --primary-foreground: oklch(1 0 0);
40
- --accent: oklch(0.769 0.096 90.9); /* Braden Gold */
41
- --accent-foreground: oklch(0.156 0 0);
42
- --ring: oklch(0.488 0.170 17.6); /* Braden Red ring */
43
- }
@@ -1,86 +0,0 @@
1
- /*
2
- * @bsuite/theme — tokens-dark.css
3
- *
4
- * Full semantic token set for the dark theme.
5
- * Applied to .dark.
6
- *
7
- * All values are in OKLCH — no hex, no hsl() wrappers.
8
- * Source of truth: TOKEN-MAPPING.md v0.2.0 + vars.css
9
- */
10
-
11
- .dark {
12
-
13
- /* ===== Text ===== */
14
- --text-primary: oklch(0.94 0.012 260);
15
- --text-secondary: oklch(0.82 0.015 260);
16
- --text-muted: oklch(0.68 0.018 260);
17
- --text-subtle: oklch(0.56 0.015 260);
18
- --text-disabled: oklch(0.44 0.010 260);
19
- --text-on-primary: oklch(0.99 0 0);
20
- --text-on-surface: oklch(0.94 0.012 260);
21
- --color-primary-text: oklch(0.623 0.188 259.8); /* WCAG AA blue text on dark bg */
22
- --color-accent-text: oklch(0.769 0.132 191.7); /* WCAG AA cyan text on dark bg */
23
-
24
- /* ===== Background ===== */
25
- --bg-body: oklch(0.166 0.026 269.4); /* deep navy */
26
- --bg-surface: oklch(0.19 0.02 260);
27
- --bg-panel: oklch(0.242 0.03 269.9);
28
- --bg-tertiary: oklch(0.326 0.036 266.7);
29
- --bg-elevated: oklch(0.292 0.034 270);
30
- --bg-input: oklch(0.242 0.03 269.9 / 0.6);
31
- --bg-hover: oklch(0.39 0.035 265);
32
- --bg-selected: oklch(0.546 0.215 262.9 / 0.15);
33
-
34
- /* ===== Border ===== */
35
- --border-color: oklch(0.769 0.132 191.7 / 0.15);
36
- --border-color-strong: oklch(0.769 0.132 191.7 / 0.3);
37
- --border-shell: oklch(0.769 0.132 191.7 / 0.18);
38
-
39
- /* ===== Accent / brand ===== */
40
- --accent-primary: oklch(0.546 0.215 262.9); /* Electric Blue */
41
- --accent-secondary: oklch(0.769 0.132 191.7); /* Electric Cyan */
42
-
43
- /* ===== Status (same values in both themes) ===== */
44
- --color-success: oklch(0.697 0.135 172.1);
45
- --color-warning: oklch(0.868 0.125 81.4);
46
- --color-error: oklch(0.568 0.202 283.1);
47
- --color-info: oklch(0.769 0.132 191.7);
48
-
49
- /* ===== shadcn/ui bridge tokens ===== */
50
- --background: oklch(0.166 0.026 269.4);
51
- --foreground: var(--text-primary);
52
- --card: oklch(0.242 0.03 269.9);
53
- --card-foreground: var(--text-primary);
54
- --popover: oklch(0.292 0.034 270);
55
- --popover-foreground: var(--text-primary);
56
- --primary: oklch(0.769 0.132 191.7); /* Cyan as primary in dark */
57
- --primary-foreground: oklch(0.166 0.026 269.4);
58
- --secondary: oklch(0.242 0.03 269.9);
59
- --secondary-foreground: var(--text-primary);
60
- --muted: oklch(0.242 0.03 269.9);
61
- --muted-foreground: var(--text-muted);
62
- --accent: oklch(0.546 0.215 262.9); /* Blue as accent in dark */
63
- --accent-foreground: var(--text-primary);
64
- --destructive: var(--color-error);
65
- --destructive-foreground: oklch(0.99 0 0);
66
- --border: oklch(0.769 0.132 191.7 / 0.15);
67
- --input: oklch(0.769 0.132 191.7 / 0.15);
68
- --ring: oklch(0.769 0.132 191.7); /* Cyan ring in dark */
69
-
70
- /* ===== Box shadows — deep blacks + neon cyan glow ===== */
71
- --shadow-elev-0: none;
72
- --shadow-elev-1: 0 1px 3px oklch(0 0 0 / 0.3);
73
- --shadow-elev-2: 0 4px 8px oklch(0 0 0 / 0.4), 0 1px 3px oklch(0 0 0 / 0.3);
74
- --shadow-elev-3: 0 8px 16px oklch(0 0 0 / 0.5), 0 4px 8px oklch(0 0 0 / 0.3);
75
- --shadow-elev-4: 0 16px 32px oklch(0 0 0 / 0.6), 0 8px 16px oklch(0 0 0 / 0.4);
76
-
77
- /* ===== Neon glow helpers ===== */
78
- --glow-button: 0 0 20px oklch(0.769 0.132 191.7 / 0.3);
79
- --glow-button-hover: 0 0 30px oklch(0.769 0.132 191.7 / 0.5);
80
- --glow-card: 0 0 15px oklch(0.769 0.132 191.7 / 0.08);
81
- --glow-card-hover: 0 0 25px oklch(0.769 0.132 191.7 / 0.15);
82
-
83
- /* ===== Button shadow ===== */
84
- --button-shadow: 0 0 20px oklch(0.769 0.132 191.7 / 0.3);
85
- --button-hover-shadow: 0 0 30px oklch(0.769 0.132 191.7 / 0.5);
86
- }
@@ -1,34 +0,0 @@
1
- /*
2
- * @bsuite/theme — tokens-high-contrast.css
3
- *
4
- * AAA accessibility overrides for users who have opted into
5
- * "More Contrast" in their OS/browser settings.
6
- *
7
- * Kept intentionally minimal — only tokens that fail AAA at default
8
- * values are overridden. All other tokens inherit from tokens-light.css
9
- * or tokens-dark.css.
10
- *
11
- * Source of truth: TOKEN-MAPPING.md v0.2.0
12
- */
13
-
14
- @media (prefers-contrast: more) {
15
-
16
- /* ----- Light mode overrides (default / :root) ----- */
17
- :root,
18
- .light {
19
- --text-primary: oklch(0 0 0); /* pure black */
20
- --text-secondary: oklch(0.1 0 0);
21
- --text-muted: oklch(0.2 0 0);
22
- --border-color: oklch(0 0 0);
23
- --border-color-strong: oklch(0 0 0);
24
- }
25
-
26
- /* ----- Dark mode overrides ----- */
27
- .dark {
28
- --text-primary: oklch(1 0 0); /* pure white */
29
- --text-secondary: oklch(0.9 0 0);
30
- --text-muted: oklch(0.8 0 0);
31
- --border-color: oklch(1 0 0);
32
- --border-color-strong: oklch(1 0 0);
33
- }
34
- }
@@ -1,89 +0,0 @@
1
- /*
2
- * @bsuite/theme — tokens-light.css
3
- *
4
- * Full semantic token set for the light theme.
5
- * Applied to :root and .light.
6
- *
7
- * All values are in OKLCH — no hex, no hsl() wrappers.
8
- * Source of truth: TOKEN-MAPPING.md v0.2.0
9
- */
10
-
11
- :root,
12
- .light {
13
-
14
- /* ===== Text ===== */
15
- --text-primary: oklch(0.22 0.015 260);
16
- --text-secondary: oklch(0.38 0.018 260);
17
- --text-muted: oklch(0.52 0.018 260);
18
- --text-subtle: oklch(0.60 0.012 260);
19
- --text-disabled: oklch(0.72 0.010 260);
20
- --text-on-primary: oklch(0.99 0 0);
21
- --text-on-surface: oklch(0.22 0.015 260);
22
- --color-primary-text: oklch(0.485 0.243 263.6); /* WCAG AA blue text on light bg */
23
- --color-accent-text: oklch(0.486 0.084 191.5); /* WCAG AA cyan text on light bg */
24
-
25
- /* ===== Background ===== */
26
- --bg-body: oklch(0.961 0 0.5);
27
- --bg-surface: oklch(0.982 0.002 248);
28
- --bg-panel: oklch(1 0 0);
29
- --bg-tertiary: oklch(0.963 0.003 228.9);
30
- --bg-elevated: oklch(1 0 0);
31
- --bg-input: oklch(0.982 0.002 248);
32
- --bg-hover: oklch(0.963 0.003 228.9);
33
- --bg-selected: oklch(0.546 0.215 262.9 / 0.08);
34
-
35
- /* ===== Border ===== */
36
- --border-color: oklch(0.916 0.006 248);
37
- --border-color-strong: oklch(0.741 0.022 250 / 0.5);
38
- --border-shell: oklch(0.741 0.022 250 / 0.2);
39
-
40
- /* ===== Accent / brand ===== */
41
- --accent-primary: oklch(0.546 0.215 262.9); /* Electric Blue */
42
- --accent-secondary: oklch(0.769 0.132 191.7); /* Electric Cyan */
43
-
44
- /* ===== Status ===== */
45
- --color-success: oklch(0.697 0.135 172.1);
46
- --color-warning: oklch(0.868 0.125 81.4);
47
- --color-error: oklch(0.568 0.202 283.1);
48
- --color-info: oklch(0.769 0.132 191.7);
49
-
50
- /* ===== shadcn/ui bridge tokens ===== */
51
- --background: oklch(0.961 0 0.5);
52
- --foreground: var(--text-primary);
53
- --card: oklch(1 0 0);
54
- --card-foreground: var(--text-primary);
55
- --popover: oklch(1 0 0);
56
- --popover-foreground: var(--text-primary);
57
- --primary: oklch(0.546 0.215 262.9); /* Electric Blue */
58
- --primary-foreground: var(--text-on-primary);
59
- --secondary: oklch(0.961 0 0.5);
60
- --secondary-foreground: var(--text-primary);
61
- --muted: oklch(0.961 0 0.5);
62
- --muted-foreground: var(--text-muted);
63
- --accent: oklch(0.769 0.132 191.7); /* Electric Cyan */
64
- --accent-foreground: oklch(0.17 0.02 260);
65
- --destructive: var(--color-error);
66
- --destructive-foreground: oklch(0.99 0 0);
67
- --border: oklch(0.916 0.006 248); /* from hsl(214.3 31.8% 91.4%) */
68
- --input: oklch(0.916 0.006 248);
69
- --ring: oklch(0.546 0.215 262.9); /* Electric Blue */
70
-
71
- /* ===== Border-radius ===== */
72
- --radius: 0.5rem;
73
-
74
- /* ===== Box shadows — soft neutral, no neon glow in light mode ===== */
75
- --shadow-elev-0: none;
76
- --shadow-elev-1: 0 1px 2px oklch(0 0 0 / 0.05);
77
- --shadow-elev-2: 0 4px 6px oklch(0 0 0 / 0.07), 0 2px 4px oklch(0 0 0 / 0.06);
78
- --shadow-elev-3: 0 10px 15px oklch(0 0 0 / 0.1), 0 4px 6px oklch(0 0 0 / 0.05);
79
- --shadow-elev-4: 0 20px 25px oklch(0 0 0 / 0.1), 0 10px 10px oklch(0 0 0 / 0.04);
80
-
81
- /* ===== Glow helpers — disabled in light mode ===== */
82
- --glow-button: none;
83
- --glow-button-hover: none;
84
- --glow-card: none;
85
- --glow-card-hover: none;
86
-
87
- /* ===== Button shadow — transparent/none in light mode ===== */
88
- --button-shadow: none;
89
- }