@exxatdesignux/ui 0.2.18 → 0.2.19
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/CHANGELOG.md +15 -0
- package/consumer-extras/AGENTS.md +76 -0
- package/consumer-extras/README.md +5 -1
- package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +14 -3
- package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +37 -0
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +21 -6
- package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +57 -0
- package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +4 -2
- package/consumer-extras/patterns/consumer-app-pattern.md +39 -0
- package/consumer-extras/patterns/consumer-upgrade-checklist.md +20 -0
- package/consumer-extras/patterns/data-views-pattern.md +40 -3
- package/consumer-extras/patterns/focused-workflow-page-pattern.md +84 -0
- package/consumer-extras/patterns/shell-surface-elevation-pattern.md +5 -3
- package/package.json +2 -1
- package/src/components/ui/button-group.tsx +81 -0
- package/src/components/ui/button.tsx +4 -4
- package/src/globals.css +7 -1858
- package/src/theme.css +10 -1126
- package/src/tokens/README.md +15 -0
- package/src/tokens/base.css +337 -0
- package/src/tokens/high-contrast.css +1195 -0
- package/src/tokens/layers.css +224 -0
- package/src/tokens/tailwind-bridge.css +118 -0
- package/src/tokens/themes.css +201 -0
- package/template/AGENTS.md +60 -22
- package/template/app/(app)/dashboard/loading.tsx +3 -15
- package/template/app/(app)/dashboard/page.tsx +2 -14
- package/template/app/(app)/data-list/layout.tsx +43 -0
- package/template/app/(app)/data-list/page.tsx +2 -2
- package/template/app/(app)/examples/focused-workflow/page.tsx +5 -0
- package/template/app/(app)/examples/page.tsx +1 -0
- package/template/app/(app)/loading.tsx +1 -18
- package/template/app/(app)/question-bank/find/page.tsx +2 -1
- package/template/app/(app)/question-bank/library/page.tsx +2 -1
- package/template/app/(app)/question-bank/list/page.tsx +2 -1
- package/template/app/(app)/question-bank/new/page.tsx +15 -23
- package/template/app/(app)/question-bank/page.tsx +2 -1
- package/template/app/(app)/settings/page.tsx +4 -5
- package/template/app/globals.css +7 -1964
- package/template/components/app-route-loading.tsx +14 -0
- package/template/components/app-sidebar.tsx +70 -55
- package/template/components/data-views/index.ts +37 -9
- package/template/components/data-views/list-page-calendar-view.tsx +593 -0
- package/template/components/data-views/list-page-connected-view-body.tsx +66 -0
- package/template/components/data-views/list-page-folder-columns-panel.tsx +345 -0
- package/template/components/data-views/list-page-split-hub-chrome.tsx +8 -0
- package/template/components/examples/focused-workflow-showcase.tsx +183 -0
- package/template/components/list-hub-board-view.tsx +68 -0
- package/template/components/list-hub-client.tsx +186 -0
- package/template/components/list-hub-list-view.tsx +36 -0
- package/template/components/list-hub-panel-activator.tsx +8 -0
- package/template/components/list-hub-secondary-nav.tsx +121 -0
- package/template/components/list-hub-table.tsx +336 -0
- package/template/components/new-question-composer.tsx +6 -24
- package/template/components/product-switcher.tsx +3 -2
- package/template/components/question-bank-client.tsx +4 -1
- package/template/components/question-bank-folder-columns-panel.tsx +104 -0
- package/template/components/question-bank-table.tsx +143 -485
- package/template/components/secondary-panel/nav-link-rows.tsx +83 -0
- package/template/components/secondary-panel.tsx +4 -44
- package/template/components/secondary-panels/list-hub-panel.tsx +39 -0
- package/template/components/secondary-panels/question-bank-panel.tsx +39 -0
- package/template/components/secondary-panels/registry.tsx +15 -0
- package/template/components/settings-appearance-card.tsx +3 -2
- package/template/components/settings-client.tsx +59 -15
- package/template/components/settings-form-row.tsx +9 -4
- package/template/components/table-properties/drawer-button.tsx +13 -0
- package/template/components/table-properties/drawer.tsx +65 -4
- package/template/components/templates/focused-workflow-layouts.tsx +448 -0
- package/template/components/templates/focused-workflow-page-template.tsx +69 -0
- package/template/components/templates/list-page.tsx +29 -5
- package/template/components/templates/nested-secondary-panel-shell.tsx +2 -1
- package/template/components/templates/page-loading-shell.tsx +262 -0
- package/template/components/ui/button-group.tsx +1 -0
- package/template/docs/consumer-app-pattern.md +39 -0
- package/template/docs/data-views-pattern.md +40 -3
- package/template/docs/drawer-vs-dialog-pattern.md +3 -1
- package/template/docs/focused-workflow-page-pattern.md +84 -0
- package/template/docs/shell-surface-elevation-pattern.md +5 -3
- package/template/lib/command-menu-search-data.ts +11 -27
- package/template/lib/data-list-display-options.ts +16 -2
- package/template/lib/data-list-view-registry.ts +104 -0
- package/template/lib/data-list-view-surface.ts +15 -1
- package/template/lib/data-list-view.ts +10 -1
- package/template/lib/data-view-dashboard-storage.ts +38 -35
- package/template/lib/hub-connected-view-renderers.ts +58 -0
- package/template/lib/list-hub-nav.ts +121 -0
- package/template/lib/list-hub-supported-views.ts +10 -0
- package/template/lib/list-page-table-properties.ts +3 -7
- package/template/lib/list-status-badges.ts +4 -97
- package/template/lib/mock/list-hub-directory.ts +27 -0
- package/template/lib/mock/list-hub-kpi.ts +27 -0
- package/template/lib/mock/navigation.tsx +1 -0
- package/template/lib/page-loading-variant.ts +40 -0
- package/template/lib/question-bank-supported-views.ts +13 -0
- package/template/lib/table-state-lifecycle.ts +2 -2
- package/template/app/(app)/data-list/[id]/page.tsx +0 -44
- package/template/app/(app)/data-list/new/page.tsx +0 -34
- package/template/components/compliance-board-view.tsx +0 -142
- package/template/components/compliance-client.tsx +0 -92
- package/template/components/compliance-list-view.tsx +0 -54
- package/template/components/compliance-page-header.tsx +0 -89
- package/template/components/compliance-table.tsx +0 -612
- package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
- package/template/components/data-view-dashboard-charts-team.tsx +0 -971
- package/template/components/data-view-dashboard-charts.tsx +0 -1503
- package/template/components/new-placement-back-btn.tsx +0 -28
- package/template/components/new-placement-form.tsx +0 -1068
- package/template/components/placement-board-card.tsx +0 -262
- package/template/components/placement-detail.tsx +0 -438
- package/template/components/placements-board-view.tsx +0 -404
- package/template/components/placements-client.tsx +0 -252
- package/template/components/placements-list-view.tsx +0 -171
- package/template/components/placements-page-header.tsx +0 -166
- package/template/components/placements-table-cells.test.tsx +0 -22
- package/template/components/placements-table-cells.tsx +0 -173
- package/template/components/placements-table-columns.tsx +0 -640
- package/template/components/placements-table.tsx +0 -1642
- package/template/components/rotations-empty-state.tsx +0 -50
- package/template/components/rotations-panel-activator.tsx +0 -8
- package/template/components/sites-all-client.tsx +0 -154
- package/template/components/sites-board-view.tsx +0 -67
- package/template/components/sites-list-view.tsx +0 -42
- package/template/components/sites-table.tsx +0 -382
- package/template/components/team-board-view.tsx +0 -122
- package/template/components/team-client.tsx +0 -100
- package/template/components/team-list-view.tsx +0 -59
- package/template/components/team-page-header.tsx +0 -92
- package/template/components/team-table.tsx +0 -693
- package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
- package/template/lib/mock/compliance-kpi.ts +0 -61
- package/template/lib/mock/compliance.ts +0 -146
- package/template/lib/mock/placements-kpi.ts +0 -134
- package/template/lib/mock/placements.ts +0 -183
- package/template/lib/mock/sites-directory.ts +0 -16
- package/template/lib/mock/sites-kpi.ts +0 -25
- package/template/lib/mock/team-kpi.ts +0 -60
- package/template/lib/mock/team.ts +0 -118
- package/template/lib/placement-board-card-layout.ts +0 -79
- package/template/lib/placement-lifecycle.ts +0 -5
package/src/theme.css
CHANGED
|
@@ -1,1132 +1,16 @@
|
|
|
1
1
|
/* ==========================================================================
|
|
2
|
-
EXXAT DESIGN SYSTEM —
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
• Text contrast ≥ 4.5 : 1 (SC 1.4.3)
|
|
6
|
-
• UI control contrast ≥ 3 : 1 (SC 1.4.11)
|
|
7
|
-
• Focus ring ≥ 3 : 1 (SC 2.4.11)
|
|
8
|
-
• Touch targets ≥ 44 × 44 px (SC 2.5.5 / mobile)
|
|
2
|
+
EXXAT DESIGN SYSTEM — theme.css (tokens only)
|
|
3
|
+
Tailwind, tw-animate-css, and @source are imported by the consuming app's
|
|
4
|
+
globals.css — NOT here. Re-exports the same token modules as globals.css.
|
|
9
5
|
========================================================================== */
|
|
10
6
|
|
|
11
|
-
/* Tailwind, tw-animate-css, and shadcn/tailwind.css are imported by the
|
|
12
|
-
consuming app's globals.css — NOT here. This file only provides the
|
|
13
|
-
design-system theme tokens, custom variants, and base-layer styles. */
|
|
14
|
-
|
|
15
|
-
/* RTL layout direction support */
|
|
16
7
|
@custom-variant dark (&:is(.dark *));
|
|
17
8
|
|
|
18
|
-
/*
|
|
19
|
-
|
|
20
|
-
-------------------------------------------------------------------------- */
|
|
21
|
-
@theme inline {
|
|
22
|
-
/* Typography */
|
|
23
|
-
--font-heading: "ivypresto-text";
|
|
24
|
-
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
25
|
-
/* Minimum product text: 11px — use `text-xs` or larger; avoid arbitrary classes below 11px */
|
|
26
|
-
--text-xs: 0.6875rem; /* 11px at 16px root */
|
|
27
|
-
|
|
28
|
-
/* Semantic color map */
|
|
29
|
-
--color-background: var(--background);
|
|
30
|
-
--color-foreground: var(--foreground);
|
|
31
|
-
--color-card: var(--card);
|
|
32
|
-
--color-card-foreground: var(--card-foreground);
|
|
33
|
-
--color-popover: var(--popover);
|
|
34
|
-
--color-popover-foreground: var(--popover-foreground);
|
|
35
|
-
--color-primary: var(--primary);
|
|
36
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
37
|
-
--color-secondary: var(--secondary);
|
|
38
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
39
|
-
--color-muted: var(--muted);
|
|
40
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
41
|
-
--color-accent: var(--accent);
|
|
42
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
43
|
-
--color-destructive: var(--destructive);
|
|
44
|
-
--color-destructive-foreground: var(--destructive-foreground);
|
|
45
|
-
--color-border: var(--border);
|
|
46
|
-
--color-border-control: var(--border-control);
|
|
47
|
-
--color-input: var(--input);
|
|
48
|
-
--color-input-background: var(--input-background);
|
|
49
|
-
--color-ring: var(--ring);
|
|
50
|
-
/* Modal / sheet / drawer scrim */
|
|
51
|
-
--color-overlay: var(--overlay);
|
|
52
|
-
|
|
53
|
-
/* Chart tokens */
|
|
54
|
-
--color-chart-1: var(--chart-1);
|
|
55
|
-
--color-chart-2: var(--chart-2);
|
|
56
|
-
--color-chart-3: var(--chart-3);
|
|
57
|
-
--color-chart-4: var(--chart-4);
|
|
58
|
-
--color-chart-5: var(--chart-5);
|
|
59
|
-
|
|
60
|
-
/* Chip / badge tokens (AA-compliant on light bg: ≥ 4.5:1) */
|
|
61
|
-
--color-chip-1: var(--chip-1);
|
|
62
|
-
--color-chip-2: var(--chip-2);
|
|
63
|
-
--color-chip-3: var(--chip-3);
|
|
64
|
-
--color-chip-4: var(--chip-4);
|
|
65
|
-
--color-chip-5: var(--chip-5);
|
|
66
|
-
--color-chip-destructive: var(--chip-destructive);
|
|
67
|
-
|
|
68
|
-
/* Brand accent + Exxat One tint scale (see :root --brand-*) */
|
|
69
|
-
--color-brand: var(--brand-color);
|
|
70
|
-
--color-brand-dark: var(--brand-color-dark);
|
|
71
|
-
--color-brand-foreground: var(--brand-foreground);
|
|
72
|
-
--color-brand-tint: var(--brand-tint);
|
|
73
|
-
--color-brand-tint-light: var(--brand-tint-light);
|
|
74
|
-
--color-brand-tint-subtle: var(--brand-tint-subtle);
|
|
75
|
-
--color-brand-color-light: var(--brand-color-light);
|
|
76
|
-
--color-brand-deep: var(--brand-color-deep);
|
|
77
|
-
|
|
78
|
-
/* Prism banner highlight */
|
|
79
|
-
--color-banner-prism: var(--banner-prism-bg);
|
|
80
|
-
|
|
81
|
-
/* Sidebar */
|
|
82
|
-
--color-sidebar: var(--sidebar);
|
|
83
|
-
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
84
|
-
--color-sidebar-primary: var(--sidebar-primary);
|
|
85
|
-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
86
|
-
--color-sidebar-accent: var(--sidebar-accent);
|
|
87
|
-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
88
|
-
--color-sidebar-border: var(--sidebar-border);
|
|
89
|
-
--color-sidebar-ring: var(--sidebar-ring);
|
|
90
|
-
/* WCAG 1.4.3 — always mixed against real --sidebar (lavender / rose / dark) */
|
|
91
|
-
--color-sidebar-section-label: var(--sidebar-section-label-foreground);
|
|
92
|
-
|
|
93
|
-
/* Interactive hover — single source for ghost controls, lists, table chrome */
|
|
94
|
-
--color-interactive-hover: var(--interactive-hover);
|
|
95
|
-
--color-interactive-hover-foreground: var(--interactive-hover-foreground);
|
|
96
|
-
--color-interactive-hover-subtle: var(--interactive-hover-subtle);
|
|
97
|
-
--color-interactive-hover-soft: var(--interactive-hover-soft);
|
|
98
|
-
--color-interactive-hover-medium: var(--interactive-hover-medium);
|
|
99
|
-
--color-interactive-hover-strong: var(--interactive-hover-strong);
|
|
100
|
-
--color-interactive-hover-row: var(--interactive-hover-row);
|
|
101
|
-
|
|
102
|
-
/* Border-radius scale (4 px base → 8 px default) */
|
|
103
|
-
--radius-sm: 4px;
|
|
104
|
-
--radius-md: 8px;
|
|
105
|
-
--radius-lg: 12px;
|
|
106
|
-
--radius-xl: 16px;
|
|
107
|
-
--radius-2xl: 20px;
|
|
108
|
-
--radius-3xl: 24px;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/* ==========================================================================
|
|
112
|
-
:root — Exxat One · Light Mode (brand hue 286.1)
|
|
113
|
-
All contrast ratios verified against oklch(1 0 0) white background.
|
|
114
|
-
========================================================================== */
|
|
115
|
-
:root {
|
|
116
|
-
/* font-size intentionally NOT overridden here.
|
|
117
|
-
Shadcn components assume 1rem = 16px (browser default).
|
|
118
|
-
Overriding to 87.5% / 14px breaks rem-based spacing on all devices,
|
|
119
|
-
and compounds incorrectly at Windows 150% system zoom.
|
|
120
|
-
If a compact 14px density is needed, apply it at the component/page level
|
|
121
|
-
via a scoped class, not globally on :root. */
|
|
122
|
-
/* Minimum readable UI copy: 11px (`text-xs` / --text-xs). Do not use smaller arbitrary sizes. */
|
|
123
|
-
|
|
124
|
-
/* ── Typography ─────────────────────────────────────────────── */
|
|
125
|
-
/* Ivy Presto loaded via Adobe Fonts Kit wuk5wqn (use.typekit.net) */
|
|
126
|
-
--font-heading: "ivypresto-text";
|
|
127
|
-
|
|
128
|
-
/* ── Brand — Exxat One (hue 286.1) ─────────────────────────── */
|
|
129
|
-
/* Primary surface — requested token */
|
|
130
|
-
--brand-tint: oklch(0.9676 0.016 286.1);
|
|
131
|
-
/* Lighter / darker washes (same hue) for nested UI & hovers */
|
|
132
|
-
--brand-tint-light: oklch(0.993 0.007 286.1);
|
|
133
|
-
--brand-tint-subtle: oklch(0.935 0.024 286.1);
|
|
134
|
-
/* Interactive accent + scale (readable on white; pairs with --brand-tint) */
|
|
135
|
-
--brand-color: oklch(0.50 0.14 286.1);
|
|
136
|
-
--brand-color-light: oklch(0.78 0.09 286.1);
|
|
137
|
-
--brand-color-dark: oklch(0.38 0.11 286.1);
|
|
138
|
-
--brand-color-deep: oklch(0.28 0.085 286.1);
|
|
139
|
-
--brand-foreground: oklch(0.985 0 0);
|
|
140
|
-
/* Fixed swatches for brand picker (Exxat One vs Prism), independent of active theme */
|
|
141
|
-
--brand-preview-one: var(--brand-tint);
|
|
142
|
-
--brand-preview-prism: oklch(0.57 0.24 342);
|
|
143
|
-
|
|
144
|
-
/* ── Surfaces ────────────────────────────────────────────────── */
|
|
145
|
-
--background: oklch(1 0 0);
|
|
146
|
-
--foreground: oklch(0.145 0 0); /* ≈ #1A1A1A — 17:1 on white ✓ */
|
|
147
|
-
--card: oklch(1 0 0);
|
|
148
|
-
--card-foreground: oklch(0.145 0 0);
|
|
149
|
-
--popover: oklch(1 0 0);
|
|
150
|
-
--popover-foreground: oklch(0.145 0 0);
|
|
151
|
-
|
|
152
|
-
/* ── Primary ─────────────────────────────────────────────────── */
|
|
153
|
-
/* Default / ghost / solid primary actions — neutral charcoal (style guide) */
|
|
154
|
-
--primary: oklch(0.3457 0.0052 286.13);
|
|
155
|
-
--primary-foreground: oklch(0.985 0 0);
|
|
156
|
-
|
|
157
|
-
/* ── Secondary / Muted / Accent ──────────────────────────────── */
|
|
158
|
-
--secondary: oklch(0.95 0.0058 264.53);
|
|
159
|
-
--secondary-foreground: oklch(0.082 0 0);
|
|
160
|
-
--muted: oklch(0.945 0.002 270);
|
|
161
|
-
--muted-foreground: oklch(0.50 0.012 270); /* 5.5:1 on white ✓ */
|
|
162
|
-
--accent: oklch(0.925 0.005 260);
|
|
163
|
-
--accent-foreground: oklch(0.082 0 0);
|
|
164
|
-
|
|
165
|
-
/* ── Destructive ─────────────────────────────────────────────── */
|
|
166
|
-
/* oklch(0.55 0.22 25) ≈ #C0392B — 5.1:1 on white ✓ */
|
|
167
|
-
--destructive: oklch(0.55 0.22 25);
|
|
168
|
-
--destructive-foreground: oklch(1 0 0);
|
|
169
|
-
|
|
170
|
-
/* ── Borders ─────────────────────────────────────────────────── */
|
|
171
|
-
/* Decorative: cards, dividers — no AA contrast requirement */
|
|
172
|
-
--border: oklch(0.92 0.002 270);
|
|
173
|
-
|
|
174
|
-
/* Form-field boundary — WCAG 1.4.11 requires 3:1 against bg.
|
|
175
|
-
--border-control-3: L=0.62 ≈ #90929A → borderline; use with care.
|
|
176
|
-
--border-control-35: L=0.25 → ≥ 3.5:1 — recommended for inputs. */
|
|
177
|
-
--border-control: oklch(0.82 0.004 270); /* subtle (layout use only) */
|
|
178
|
-
--border-control-3: oklch(0.6196 0.0092 270); /* ≈ 3:1 on white (minimum) */
|
|
179
|
-
--border-control-35: oklch(0.25 0.01 270); /* ≈ 3.5:1+ (recommended) */
|
|
180
|
-
--control-border: var(--border-control-3); /* default form-field border */
|
|
181
|
-
|
|
182
|
-
/* ── Input ───────────────────────────────────────────────────── */
|
|
183
|
-
/* Raised contrast target for control outlines (>= 3.1:1 on white). */
|
|
184
|
-
--input: oklch(0.62 0.01 264.52);
|
|
185
|
-
--input-background: oklch(0.97 0.002 270);
|
|
186
|
-
|
|
187
|
-
/* ── Focus ring ──────────────────────────────────────────────── */
|
|
188
|
-
/* 3:1+ contrast on both light & dark backgrounds (WCAG 2.4.11) */
|
|
189
|
-
--ring: oklch(0.25 0 0);
|
|
190
|
-
|
|
191
|
-
/* ── Charts ──────────────────────────────────────────────────── */
|
|
192
|
-
--chart-1: oklch(0.55 0.22 264.116);
|
|
193
|
-
--chart-2: oklch(0.48 0.15 184.704);
|
|
194
|
-
--chart-3: oklch(0.32 0.08 227.392);
|
|
195
|
-
--chart-4: oklch(0.65 0.18 84.429);
|
|
196
|
-
--chart-5: oklch(0.58 0.18 70.08);
|
|
197
|
-
|
|
198
|
-
/* ── Chip / Badge ────────────────────────────────────────────── */
|
|
199
|
-
/* AA-compliant on white: all ≥ 4.5:1 (WCAG 1.4.3) */
|
|
200
|
-
--chip-1: oklch(0.38 0.18 264); /* indigo */
|
|
201
|
-
--chip-2: oklch(0.35 0.14 184); /* teal */
|
|
202
|
-
--chip-3: oklch(0.32 0.08 227); /* slate */
|
|
203
|
-
--chip-4: oklch(0.42 0.12 84); /* amber */
|
|
204
|
-
--chip-5: oklch(0.42 0.14 70); /* orange */
|
|
205
|
-
--chip-destructive: oklch(0.40 0.18 25); /* red */
|
|
206
|
-
|
|
207
|
-
/* ── Prism promo banner ──────────────────────────────────────── */
|
|
208
|
-
/* Rose hue 343 — used universally regardless of active theme */
|
|
209
|
-
--banner-prism-bg: oklch(0.97 0.02 343);
|
|
210
|
-
|
|
211
|
-
/* ── Sidebar — Exxat One brand tint ───────────────────────────── */
|
|
212
|
-
--sidebar: var(--brand-tint);
|
|
213
|
-
--sidebar-foreground: oklch(0.145 0 0);
|
|
214
|
-
--sidebar-primary: oklch(0.082 0 0);
|
|
215
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
216
|
-
--sidebar-accent: oklch(0.945 0.025 286.1);
|
|
217
|
-
--sidebar-accent-foreground: oklch(0.3457 0.0052 286.13);
|
|
218
|
-
--sidebar-border: oklch(0.92 0.025 286.1);
|
|
219
|
-
--sidebar-ring: oklch(0.25 0 0);
|
|
220
|
-
/* Nav section titles — ≥4.5:1 vs --sidebar (not vs page white) */
|
|
221
|
-
--sidebar-section-label-foreground: color-mix(in oklch, var(--sidebar-foreground) 58%, var(--sidebar));
|
|
222
|
-
/* Nested secondary rail — soft brand wash on canvas (not pure `--background`, not full `--sidebar`). */
|
|
223
|
-
--secondary-panel-bg: color-mix(in oklch, var(--background) 50%, var(--sidebar) 50%);
|
|
224
|
-
|
|
225
|
-
/* Browser UI (meta theme-color) — aligned with --brand-tint */
|
|
226
|
-
--theme-color-chrome: #f3f2f8;
|
|
227
|
-
|
|
228
|
-
/* ── Border radius ───────────────────────────────────────────── */
|
|
229
|
-
--radius: 0.5rem; /* 8px base */
|
|
230
|
-
|
|
231
|
-
/* ── Density / touch targets ─────────────────────────────────── */
|
|
232
|
-
--scaling: 1;
|
|
233
|
-
--control-height: calc(40px * var(--scaling));
|
|
234
|
-
--control-height-sm: calc(32px * var(--scaling));
|
|
235
|
-
/* WCAG 2.5.5 — minimum 44×44 px touch target (mobile only) */
|
|
236
|
-
--control-height-touch: 44px;
|
|
237
|
-
--control-padding-y: calc(8px * var(--scaling));
|
|
238
|
-
--control-padding-x: calc(12px * var(--scaling));
|
|
239
|
-
--table-row-height: calc(48px * var(--scaling));
|
|
240
|
-
|
|
241
|
-
/* ── Interactive hover (ties to --muted / --accent — theme overrides apply) ─ */
|
|
242
|
-
--interactive-hover: var(--muted);
|
|
243
|
-
--interactive-hover-foreground: var(--foreground);
|
|
244
|
-
--interactive-hover-subtle: color-mix(in oklch, var(--muted) 50%, transparent);
|
|
245
|
-
--interactive-hover-soft: color-mix(in oklch, var(--muted) 40%, transparent);
|
|
246
|
-
--interactive-hover-medium: color-mix(in oklch, var(--muted) 60%, transparent);
|
|
247
|
-
--interactive-hover-strong: color-mix(in oklch, var(--muted) 70%, transparent);
|
|
248
|
-
--interactive-hover-row: color-mix(in oklch, var(--accent) 50%, transparent);
|
|
249
|
-
|
|
250
|
-
/* ── Transitions ─────────────────────────────────────────────── */
|
|
251
|
-
--transition-fast: 0.15s ease;
|
|
252
|
-
--transition-normal: 0.25s ease-in-out;
|
|
253
|
-
--transition-colors: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
|
|
254
|
-
|
|
255
|
-
/* ── Shadows ─────────────────────────────────────────────────── */
|
|
256
|
-
--shadow-sm: oklch(0 0 0 / 0.08) 0px 1px 2px 0px;
|
|
257
|
-
--shadow-md: oklch(0 0 0 / 0.08) 0px 2px 4px -1px, oklch(0 0 0 / 0.06) 0px 1px 2px;
|
|
258
|
-
--shadow-lg: oklch(0 0 0 / 0.10) 0px 4px 8px -2px, oklch(0 0 0 / 0.06) 0px 2px 4px;
|
|
259
|
-
|
|
260
|
-
/* ── Sticky column edge fade (data tables) ───────────────────── */
|
|
261
|
-
--sticky-edge-fade: oklch(0 0 0 / 0.08);
|
|
262
|
-
|
|
263
|
-
/* ── Overlay scrim (Sheet / Drawer / Dialog) ─────────────────── */
|
|
264
|
-
/* Softer than raw bg-black/10; follows foreground hue (not pure black) */
|
|
265
|
-
--overlay: color-mix(in oklch, var(--foreground) 5%, transparent);
|
|
266
|
-
|
|
267
|
-
/* ── Avatar initials (table) ───────────────────────────────────── */
|
|
268
|
-
/* Soft brand wash + brand ink — no black fills; ≥4.5:1 on white rows */
|
|
269
|
-
--avatar-initials-bg: color-mix(in oklch, var(--brand-color) 14%, oklch(1 0 0));
|
|
270
|
-
--avatar-initials-fg: var(--brand-color-dark);
|
|
271
|
-
|
|
272
|
-
/* ── KPI insight severity (KeyMetrics) ────────────────────────── */
|
|
273
|
-
--insight-severity-warning-bg: color-mix(in oklch, var(--chart-4) 15%, transparent);
|
|
274
|
-
--insight-severity-warning-fg: color-mix(in oklch, var(--chart-4) 75%, var(--foreground));
|
|
275
|
-
--insight-severity-info-bg: color-mix(in oklch, var(--chart-1) 14%, transparent);
|
|
276
|
-
--insight-severity-info-fg: color-mix(in oklch, var(--chart-1) 75%, var(--foreground));
|
|
277
|
-
|
|
278
|
-
/* ── Conditional formatting rule backgrounds (table drawer) ─── */
|
|
279
|
-
--conditional-rule-green: color-mix(in oklch, var(--chart-2) 22%, transparent);
|
|
280
|
-
--conditional-rule-yellow: color-mix(in oklch, var(--chart-4) 30%, transparent);
|
|
281
|
-
--conditional-rule-blue: color-mix(in oklch, var(--chart-1) 22%, transparent);
|
|
282
|
-
--conditional-rule-red: color-mix(in oklch, var(--destructive) 22%, transparent);
|
|
283
|
-
--conditional-rule-purple: color-mix(in oklch, var(--brand-color) 22%, transparent);
|
|
284
|
-
--conditional-rule-orange: color-mix(in oklch, var(--chart-5) 25%, transparent);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/* ==========================================================================
|
|
288
|
-
Dark Mode — Exxat One base (neutral surfaces; brand uses --brand-*)
|
|
289
|
-
========================================================================== */
|
|
290
|
-
.dark {
|
|
291
|
-
--sticky-edge-fade: oklch(0 0 0 / 0.32);
|
|
292
|
-
|
|
293
|
-
/* Canvas — charcoal grey (not near-black) */
|
|
294
|
-
--background: oklch(0.20 0.008 270);
|
|
295
|
-
--foreground: oklch(0.985 0 0);
|
|
296
|
-
--card: oklch(0.255 0.008 270);
|
|
297
|
-
--card-foreground: oklch(0.985 0 0);
|
|
298
|
-
--popover: oklch(0.255 0.008 270);
|
|
299
|
-
--popover-foreground: oklch(0.985 0 0);
|
|
300
|
-
--primary: oklch(0.985 0 0);
|
|
301
|
-
--primary-foreground: oklch(0.3457 0.0052 286.13);
|
|
302
|
-
--secondary: oklch(0.31 0.04 270);
|
|
303
|
-
--secondary-foreground: oklch(0.985 0 0);
|
|
304
|
-
--muted: oklch(0.31 0.04 270);
|
|
305
|
-
--muted-foreground: oklch(0.72 0.012 270); /* ≥ 4.5:1 on dark bg ✓ */
|
|
306
|
-
--accent: oklch(0.33 0.06 270);
|
|
307
|
-
--accent-foreground: oklch(0.985 0 0);
|
|
308
|
-
--destructive: oklch(0.65 0.20 25); /* brighter for dark bg */
|
|
309
|
-
--destructive-foreground: oklch(0.10 0 0);
|
|
310
|
-
|
|
311
|
-
/* Borders — visible but not washed out on dark surfaces */
|
|
312
|
-
--border: oklch(0.38 0.008 270);
|
|
313
|
-
--border-control: oklch(0.72 0.012 270);
|
|
314
|
-
--border-control-3: oklch(0.78 0.012 270);
|
|
315
|
-
--border-control-35: oklch(0.75 0.012 270);
|
|
316
|
-
--control-border: var(--border-control-3);
|
|
317
|
-
|
|
318
|
-
--input: oklch(0.72 0.012 270);
|
|
319
|
-
--input-background: oklch(0.255 0 0);
|
|
320
|
-
|
|
321
|
-
/* Focus ring — 3:1+ on dark bg */
|
|
322
|
-
--ring: oklch(0.85 0 0);
|
|
323
|
-
|
|
324
|
-
/* Charts — higher lightness for dark surfaces */
|
|
325
|
-
--chart-1: oklch(0.70 0.22 264.376);
|
|
326
|
-
--chart-2: oklch(0.75 0.15 162.48);
|
|
327
|
-
--chart-3: oklch(0.78 0.12 227.392);
|
|
328
|
-
--chart-4: oklch(0.80 0.18 84.429);
|
|
329
|
-
--chart-5: oklch(0.75 0.18 70.08);
|
|
330
|
-
|
|
331
|
-
/* Chips — AA-compliant on dark bg (L raised) */
|
|
332
|
-
--chip-1: oklch(0.72 0.18 264);
|
|
333
|
-
--chip-2: oklch(0.72 0.14 184);
|
|
334
|
-
--chip-3: oklch(0.78 0.12 227);
|
|
335
|
-
--chip-4: oklch(0.78 0.14 84);
|
|
336
|
-
--chip-5: oklch(0.78 0.16 70);
|
|
337
|
-
--chip-destructive: oklch(0.72 0.18 25);
|
|
338
|
-
|
|
339
|
-
/* Sidebar */
|
|
340
|
-
--sidebar: oklch(0.245 0.015 270);
|
|
341
|
-
--sidebar-foreground: oklch(0.985 0 0);
|
|
342
|
-
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
343
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
344
|
-
--sidebar-accent: oklch(0.30 0.012 270);
|
|
345
|
-
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
346
|
-
--sidebar-border: oklch(0.38 0.010 270);
|
|
347
|
-
--sidebar-ring: oklch(0.85 0 0);
|
|
348
|
-
--sidebar-section-label-foreground: color-mix(in oklch, var(--sidebar-foreground) 48%, var(--sidebar));
|
|
349
|
-
/* Nested secondary rail — dark: neutral step between page canvas and sidebar (no per-product brand wash). */
|
|
350
|
-
--secondary-panel-bg: color-mix(in oklch, var(--background) 58%, var(--sidebar) 42%);
|
|
351
|
-
--theme-color-chrome: #2f2d36;
|
|
352
|
-
|
|
353
|
-
/* Lifted scrim on dark — white-tinted veil, not heavy black */
|
|
354
|
-
--overlay: color-mix(in oklch, var(--foreground) 10%, transparent);
|
|
355
|
-
|
|
356
|
-
/* Mid lavender chip + light text — no black circle; contrasts on dark table rows */
|
|
357
|
-
--avatar-initials-bg: color-mix(in oklch, var(--brand-color) 38%, oklch(0.40 0.05 286.1));
|
|
358
|
-
--avatar-initials-fg: oklch(0.99 0.01 286.1);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/* ==========================================================================
|
|
362
|
-
Theme: Exxat One · Lavender (explicit — also applied as :root default)
|
|
363
|
-
Usage: <html class="theme-one"> or <html class="theme-lavender">
|
|
364
|
-
========================================================================== */
|
|
365
|
-
.theme-one,
|
|
366
|
-
.theme-lavender {
|
|
367
|
-
--brand-tint: oklch(0.9676 0.016 286.1);
|
|
368
|
-
--brand-tint-light: oklch(0.993 0.007 286.1);
|
|
369
|
-
--brand-tint-subtle: oklch(0.935 0.024 286.1);
|
|
370
|
-
--brand-color: oklch(0.50 0.14 286.1);
|
|
371
|
-
--brand-color-light: oklch(0.78 0.09 286.1);
|
|
372
|
-
--brand-color-dark: oklch(0.38 0.11 286.1);
|
|
373
|
-
--brand-color-deep: oklch(0.28 0.085 286.1);
|
|
374
|
-
--ring: var(--brand-color-dark);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/* Light surfaces only — must NOT override .dark (otherwise muted/accent stay “paper white” in dark mode) */
|
|
378
|
-
.theme-one:not(.dark),
|
|
379
|
-
.theme-lavender:not(.dark) {
|
|
380
|
-
--sidebar: var(--brand-tint);
|
|
381
|
-
--sidebar-accent: oklch(0.945 0.025 286.1);
|
|
382
|
-
--sidebar-border: oklch(0.92 0.025 286.1);
|
|
383
|
-
--secondary: oklch(0.95 0.012 286.1);
|
|
384
|
-
--accent: oklch(0.925 0.015 286.1);
|
|
385
|
-
--muted: oklch(0.945 0.008 286.1);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
.theme-one.dark,
|
|
389
|
-
.dark.theme-one,
|
|
390
|
-
.theme-lavender.dark,
|
|
391
|
-
.dark.theme-lavender {
|
|
392
|
-
--ring: var(--brand-color);
|
|
393
|
-
--background: oklch(0.20 0.008 286.1);
|
|
394
|
-
--sidebar: oklch(0.245 0.015 286.1);
|
|
395
|
-
--sidebar-accent: oklch(0.30 0.012 286.1);
|
|
396
|
-
--sidebar-border: oklch(0.38 0.010 286.1);
|
|
397
|
-
/* Restore dark surfaces (base .dark is overridden by :not(.dark) rules above when both classes apply) */
|
|
398
|
-
--secondary: oklch(0.31 0.04 286.1);
|
|
399
|
-
--muted: oklch(0.31 0.04 286.1);
|
|
400
|
-
--accent: oklch(0.33 0.06 286.1);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
/* ==========================================================================
|
|
404
|
-
Theme: Exxat Prism · Rose · hue 343
|
|
405
|
-
Usage: <html class="theme-prism"> or <html class="theme-rose">
|
|
406
|
-
========================================================================== */
|
|
407
|
-
.theme-prism,
|
|
408
|
-
.theme-rose {
|
|
409
|
-
--brand-color: oklch(0.57 0.24 342); /* Prism rose */
|
|
410
|
-
--brand-color-dark: oklch(0.42 0.24 342);
|
|
411
|
-
--ring: var(--brand-color-dark);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
.theme-prism:not(.dark),
|
|
415
|
-
.theme-rose:not(.dark) {
|
|
416
|
-
--sidebar: oklch(0.97 0.02 343);
|
|
417
|
-
--sidebar-accent: oklch(0.945 0.025 343);
|
|
418
|
-
--sidebar-border: oklch(0.92 0.025 343);
|
|
419
|
-
--secondary: oklch(0.95 0.012 343);
|
|
420
|
-
--accent: oklch(0.925 0.015 343);
|
|
421
|
-
--muted: oklch(0.945 0.008 343);
|
|
422
|
-
--banner-prism-bg: oklch(0.97 0.02 343);
|
|
423
|
-
--theme-color-chrome: #fff5f9;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
.theme-prism.dark,
|
|
427
|
-
.dark.theme-prism,
|
|
428
|
-
.theme-rose.dark,
|
|
429
|
-
.dark.theme-rose {
|
|
430
|
-
--ring: var(--brand-color);
|
|
431
|
-
--background: oklch(0.20 0.008 342);
|
|
432
|
-
--sidebar: oklch(0.245 0.015 342);
|
|
433
|
-
--sidebar-accent: oklch(0.30 0.012 342);
|
|
434
|
-
--sidebar-border: oklch(0.38 0.010 342);
|
|
435
|
-
--secondary: oklch(0.31 0.04 342);
|
|
436
|
-
--muted: oklch(0.31 0.04 342);
|
|
437
|
-
--accent: oklch(0.33 0.06 342);
|
|
438
|
-
--theme-color-chrome: #2a2428;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/* ==========================================================================
|
|
442
|
-
HIGH CONTRAST MODE
|
|
443
|
-
──────────────────────────────────────────────────────────────────────────
|
|
444
|
-
Three guiding principles (WCAG 1.4.6 Enhanced + research-backed UX):
|
|
445
|
-
|
|
446
|
-
1. FORCED-COLORS APPROACH
|
|
447
|
-
Strip gradients, shadows, and background images.
|
|
448
|
-
Use borders to define shape and elevation — not fills.
|
|
449
|
-
Limited palette: 4–6 named tokens, nothing decorative.
|
|
450
|
-
|
|
451
|
-
2. DON'T RELY ON COLOR ALONE
|
|
452
|
-
Error/warning states get a thick border AND an icon differentiator.
|
|
453
|
-
Charts and badges use luminance separation (grayscale steps), not hue.
|
|
454
|
-
Interactive state (hover / focus / active) is communicated via outline,
|
|
455
|
-
not background-color change alone.
|
|
456
|
-
|
|
457
|
-
3. DARK MODE ≠ HIGH CONTRAST
|
|
458
|
-
Dark mode = aesthetic; uses subtle greys.
|
|
459
|
-
High-contrast dark = functional; uses near-black grey canvas, pure white
|
|
460
|
-
(#FFF), and a "hot" neon-yellow focus accent for maximum visibility.
|
|
461
|
-
========================================================================== */
|
|
462
|
-
|
|
463
|
-
/* ── Light High Contrast ─────────────────────────────────────────────────── */
|
|
464
|
-
html[data-contrast="high"]:not(.dark) {
|
|
465
|
-
/* Pure palette — 4 roles */
|
|
466
|
-
--background: oklch(1 0 0); /* pure white canvas */
|
|
467
|
-
--foreground: oklch(0.06 0 0); /* near-black — 19:1 on white ✓ */
|
|
468
|
-
--card: oklch(1 0 0);
|
|
469
|
-
--card-foreground: oklch(0.06 0 0);
|
|
470
|
-
--popover: oklch(1 0 0);
|
|
471
|
-
--popover-foreground: oklch(0.06 0 0);
|
|
472
|
-
|
|
473
|
-
/* Primary CTA — solid black button, white label */
|
|
474
|
-
--primary: oklch(0.06 0 0);
|
|
475
|
-
--primary-foreground: oklch(1 0 0);
|
|
476
|
-
|
|
477
|
-
/* Secondary — white bg, black border (no fill) */
|
|
478
|
-
--secondary: oklch(1 0 0);
|
|
479
|
-
--secondary-foreground: oklch(0.06 0 0);
|
|
480
|
-
|
|
481
|
-
/* Muted — very light grey container only; text is NEVER "muted" in HC */
|
|
482
|
-
--muted: oklch(0.94 0 0);
|
|
483
|
-
--muted-foreground: oklch(0.10 0 0); /* still very dark — 15:1 ✓ */
|
|
484
|
-
|
|
485
|
-
/* Accent — near-white surface, near-black text */
|
|
486
|
-
--accent: oklch(0.94 0 0);
|
|
487
|
-
--accent-foreground: oklch(0.06 0 0);
|
|
488
|
-
|
|
489
|
-
/* Destructive — saturated red (stronger than AA default) + enforced icon */
|
|
490
|
-
--destructive: oklch(0.42 0.26 25); /* ≈ 5.8:1 on white ✓ */
|
|
491
|
-
--destructive-foreground: oklch(1 0 0);
|
|
492
|
-
|
|
493
|
-
/* Borders — all thick and dark; no decorative-only borders */
|
|
494
|
-
--border: oklch(0.10 0 0); /* 2px enforced via structural CSS */
|
|
495
|
-
--border-control: oklch(0.06 0 0);
|
|
496
|
-
--border-control-3: oklch(0.06 0 0);
|
|
497
|
-
--border-control-35: oklch(0.06 0 0);
|
|
498
|
-
--control-border: oklch(0.06 0 0);
|
|
499
|
-
|
|
500
|
-
/* Input */
|
|
501
|
-
--input: oklch(0.06 0 0);
|
|
502
|
-
--input-background: oklch(1 0 0);
|
|
503
|
-
|
|
504
|
-
/* Focus ring — 3 px solid near-black (WCAG 2.4.11 + 2.4.12) */
|
|
505
|
-
--ring: oklch(0.06 0 0);
|
|
506
|
-
|
|
507
|
-
/* Sidebar — white bg, defined by left border */
|
|
508
|
-
--sidebar: oklch(1 0 0);
|
|
509
|
-
--sidebar-foreground: oklch(0.06 0 0);
|
|
510
|
-
--sidebar-primary: oklch(0.06 0 0);
|
|
511
|
-
--sidebar-primary-foreground: oklch(1 0 0);
|
|
512
|
-
--sidebar-accent: oklch(0.94 0 0);
|
|
513
|
-
--sidebar-accent-foreground: oklch(0.06 0 0);
|
|
514
|
-
--sidebar-border: oklch(0.06 0 0);
|
|
515
|
-
--sidebar-ring: oklch(0.06 0 0);
|
|
516
|
-
--sidebar-section-label-foreground: color-mix(in oklch, var(--sidebar-foreground) 50%, var(--sidebar));
|
|
517
|
-
|
|
518
|
-
/* Charts — grayscale luminance steps (readable in B&W printout / grayscale test) */
|
|
519
|
-
--chart-1: oklch(0.10 0 0); /* near-black */
|
|
520
|
-
--chart-2: oklch(0.30 0 0); /* dark grey */
|
|
521
|
-
--chart-3: oklch(0.50 0 0); /* mid grey */
|
|
522
|
-
--chart-4: oklch(0.65 0 0); /* light grey */
|
|
523
|
-
--chart-5: oklch(0.80 0 0); /* very light */
|
|
524
|
-
|
|
525
|
-
/* Chips — high-contrast on white; all ≥ 7:1 (WCAG AAA) */
|
|
526
|
-
--chip-1: oklch(0.14 0 0);
|
|
527
|
-
--chip-2: oklch(0.14 0 0);
|
|
528
|
-
--chip-3: oklch(0.14 0 0);
|
|
529
|
-
--chip-4: oklch(0.14 0 0);
|
|
530
|
-
--chip-5: oklch(0.14 0 0);
|
|
531
|
-
--chip-destructive: oklch(0.42 0.26 25);
|
|
532
|
-
|
|
533
|
-
--overlay: color-mix(in oklch, var(--foreground) 18%, transparent);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
/* ── Dark High Contrast ──────────────────────────────────────────────────── */
|
|
537
|
-
/* Rule: dark grey canvas + pure white + ONE "hot" neon-yellow accent. */
|
|
538
|
-
/* Neon yellow = oklch(0.97 0.19 110) — highest luminance, max visibility */
|
|
539
|
-
/* on dark surfaces; does NOT cause hue-vibration against grey/white. */
|
|
540
|
-
html[data-contrast="high"].dark {
|
|
541
|
-
--hc-hot: oklch(0.97 0.19 110); /* neon yellow — focus / accent anchor */
|
|
542
|
-
|
|
543
|
-
--background: oklch(0.15 0 0); /* dark grey canvas (not #000) */
|
|
544
|
-
--foreground: oklch(1 0 0); /* pure white — strong contrast ✓ */
|
|
545
|
-
--card: oklch(0.15 0 0);
|
|
546
|
-
--card-foreground: oklch(1 0 0);
|
|
547
|
-
--popover: oklch(0.22 0 0); /* slightly lifted for layering */
|
|
548
|
-
--popover-foreground: oklch(1 0 0);
|
|
549
|
-
|
|
550
|
-
/* Primary CTA — pure white button, near-black label */
|
|
551
|
-
--primary: oklch(1 0 0);
|
|
552
|
-
--primary-foreground: oklch(0.08 0 0);
|
|
553
|
-
|
|
554
|
-
/* Secondary — canvas bg, white border */
|
|
555
|
-
--secondary: oklch(0.15 0 0);
|
|
556
|
-
--secondary-foreground: oklch(1 0 0);
|
|
557
|
-
|
|
558
|
-
/* Muted — lifted grey surface; text stays near-white */
|
|
559
|
-
--muted: oklch(0.22 0 0);
|
|
560
|
-
--muted-foreground: oklch(0.88 0 0); /* strong on dark grey ✓ */
|
|
561
|
-
|
|
562
|
-
/* Accent — slightly lifted dark surface */
|
|
563
|
-
--accent: oklch(0.22 0 0);
|
|
564
|
-
--accent-foreground: oklch(1 0 0);
|
|
565
|
-
|
|
566
|
-
/* Destructive — bright orange-red, visible on dark grey */
|
|
567
|
-
--destructive: oklch(0.68 0.26 25);
|
|
568
|
-
--destructive-foreground: oklch(0.08 0 0);
|
|
569
|
-
|
|
570
|
-
/* Borders — pure white; enforced 2px via structural CSS */
|
|
571
|
-
--border: oklch(1 0 0);
|
|
572
|
-
--border-control: oklch(0.88 0 0);
|
|
573
|
-
--border-control-3: oklch(0.88 0 0);
|
|
574
|
-
--border-control-35: oklch(1 0 0);
|
|
575
|
-
--control-border: oklch(0.88 0 0);
|
|
576
|
-
|
|
577
|
-
/* Input */
|
|
578
|
-
--input: oklch(0.88 0 0);
|
|
579
|
-
--input-background: oklch(0.12 0 0);
|
|
580
|
-
|
|
581
|
-
/* Focus ring — neon yellow; 3 px; unmissable on dark or white surfaces */
|
|
582
|
-
--ring: var(--hc-hot);
|
|
583
|
-
|
|
584
|
-
/* Sidebar */
|
|
585
|
-
--sidebar: oklch(0.15 0 0);
|
|
586
|
-
--sidebar-foreground: oklch(1 0 0);
|
|
587
|
-
--sidebar-primary: oklch(1 0 0);
|
|
588
|
-
--sidebar-primary-foreground: oklch(0.08 0 0);
|
|
589
|
-
--sidebar-accent: oklch(0.22 0 0);
|
|
590
|
-
--sidebar-accent-foreground: oklch(1 0 0);
|
|
591
|
-
--sidebar-border: oklch(1 0 0);
|
|
592
|
-
--sidebar-ring: var(--hc-hot);
|
|
593
|
-
--sidebar-section-label-foreground: color-mix(in oklch, var(--sidebar-foreground) 45%, var(--sidebar));
|
|
594
|
-
|
|
595
|
-
/* Charts — white → grey steps + neon yellow for emphasis series */
|
|
596
|
-
--chart-1: oklch(1 0 0); /* white */
|
|
597
|
-
--chart-2: oklch(0.82 0 0); /* light grey */
|
|
598
|
-
--chart-3: oklch(0.65 0 0); /* mid grey */
|
|
599
|
-
--chart-4: oklch(0.50 0 0); /* dark grey */
|
|
600
|
-
--chart-5: oklch(0.97 0.19 110); /* neon yellow */
|
|
601
|
-
|
|
602
|
-
/* Chips — high-contrast on dark grey */
|
|
603
|
-
--chip-1: oklch(0.90 0 0);
|
|
604
|
-
--chip-2: oklch(0.90 0 0);
|
|
605
|
-
--chip-3: oklch(0.90 0 0);
|
|
606
|
-
--chip-4: oklch(0.90 0 0);
|
|
607
|
-
--chip-5: oklch(0.90 0 0);
|
|
608
|
-
--chip-destructive: oklch(0.68 0.26 25);
|
|
609
|
-
|
|
610
|
-
--overlay: color-mix(in oklch, var(--foreground) 22%, transparent);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
/* ── Structural rules shared by BOTH light and dark HC ──────────────────── */
|
|
615
|
-
/* Pattern 1: "Borders over Fills" — strip decorative chrome */
|
|
616
|
-
html:is([data-contrast="high"], [data-contrast="windows"]) {
|
|
617
|
-
|
|
618
|
-
/* 1a. Remove ALL box-shadows (elevation via outline, not shadow) */
|
|
619
|
-
& * {
|
|
620
|
-
box-shadow: none !important;
|
|
621
|
-
text-shadow: none !important;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
/* 1b. Strip gradient fills from cards; use border to define shape */
|
|
625
|
-
& [data-slot="card"] {
|
|
626
|
-
background-image: none !important;
|
|
627
|
-
border: 2px solid var(--foreground) !important;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
/* 1c. Thicker borders on all form controls (WCAG 1.4.11: 3:1 UI contrast) */
|
|
631
|
-
& [data-slot="input"],
|
|
632
|
-
& [data-slot="select-trigger"],
|
|
633
|
-
& [data-slot="textarea"],
|
|
634
|
-
& [data-slot="checkbox"],
|
|
635
|
-
& [data-slot="switch"] {
|
|
636
|
-
border-width: 2px !important;
|
|
637
|
-
border-color: var(--foreground) !important;
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
/* 1d. Sidebar defined by its edge border, not background colour */
|
|
641
|
-
& [data-slot="sidebar"] {
|
|
642
|
-
border-inline-end: 2px solid var(--sidebar-border) !important;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
/* 1e. Tab list — border not fill */
|
|
646
|
-
& [data-slot="tabs-list"] {
|
|
647
|
-
background-color: transparent !important;
|
|
648
|
-
border: 2px solid var(--foreground) !important;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
/* 1f. Badge / chip — border-based; fill only for destructive */
|
|
652
|
-
& [data-slot="badge"] {
|
|
653
|
-
border-width: 2px !important;
|
|
654
|
-
border-color: currentColor !important;
|
|
655
|
-
background-color: transparent !important;
|
|
656
|
-
font-weight: 700 !important;
|
|
657
|
-
box-shadow: none !important;
|
|
658
|
-
outline: none !important;
|
|
659
|
-
color: var(--foreground) !important;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
& [data-slot="badge"] * {
|
|
663
|
-
color: var(--foreground) !important;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/* Sidebar wrapper is positioning-only when it wraps Badge — avoid double border */
|
|
667
|
-
& [data-slot="sidebar-menu-badge"]:has([data-slot="badge"]) {
|
|
668
|
-
border-width: 0 !important;
|
|
669
|
-
background-color: transparent !important;
|
|
670
|
-
box-shadow: none !important;
|
|
671
|
-
outline: none !important;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
& [data-slot="view-toolbar-count"] {
|
|
675
|
-
border-width: 2px !important;
|
|
676
|
-
border-color: currentColor !important;
|
|
677
|
-
background-color: transparent !important;
|
|
678
|
-
font-weight: 700 !important;
|
|
679
|
-
color: var(--foreground) !important;
|
|
680
|
-
box-shadow: none !important;
|
|
681
|
-
outline: none !important;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
& [data-slot="sidebar-menu-badge"]:not(:has([data-slot="badge"])) {
|
|
685
|
-
border-width: 2px !important;
|
|
686
|
-
border-color: currentColor !important;
|
|
687
|
-
background-color: transparent !important;
|
|
688
|
-
font-weight: 700 !important;
|
|
689
|
-
color: var(--foreground) !important;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
/* Segmented tab buttons: global HC button border + count pill border = double ring */
|
|
693
|
-
& button[data-slot="view-segmented-item"] {
|
|
694
|
-
border-width: 0 !important;
|
|
695
|
-
border-color: transparent !important;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/* Radix Tabs — active state must not rely on muted fill / pseudo underline only */
|
|
699
|
-
& [data-slot="tabs-trigger"] {
|
|
700
|
-
color: var(--foreground) !important;
|
|
701
|
-
opacity: 1 !important;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
& [data-slot="tabs-trigger"][data-state="active"] {
|
|
705
|
-
outline: 2px solid currentColor !important;
|
|
706
|
-
outline-offset: -2px !important;
|
|
707
|
-
font-weight: 600 !important;
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
/* KPI strip + chart card mini-metrics — force readable copy (muted grays fail in HC) */
|
|
711
|
-
& [data-slot="key-metrics"] :where(span, p, button, a, i, label, div) {
|
|
712
|
-
color: var(--foreground) !important;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
& [data-slot="key-metrics"] :where(svg) {
|
|
716
|
-
color: var(--foreground) !important;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
/* List / menu rows — highlighted row uses inverted pair */
|
|
720
|
-
& [data-slot="dropdown-menu-item"][data-highlighted],
|
|
721
|
-
& [data-slot="dropdown-menu-checkbox-item"][data-highlighted],
|
|
722
|
-
& [data-slot="dropdown-menu-radio-item"][data-highlighted],
|
|
723
|
-
& [data-slot="dropdown-menu-sub-trigger"][data-highlighted],
|
|
724
|
-
& [data-slot="select-item"][data-highlighted],
|
|
725
|
-
& [data-slot="command-item"][data-selected],
|
|
726
|
-
& [data-slot="command-item"][aria-selected="true"] {
|
|
727
|
-
background-color: var(--foreground) !important;
|
|
728
|
-
color: var(--background) !important;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
& [data-slot="dropdown-menu-item"][data-highlighted] *,
|
|
732
|
-
& [data-slot="dropdown-menu-checkbox-item"][data-highlighted] *,
|
|
733
|
-
& [data-slot="dropdown-menu-radio-item"][data-highlighted] *,
|
|
734
|
-
& [data-slot="dropdown-menu-sub-trigger"][data-highlighted] *,
|
|
735
|
-
& [data-slot="select-item"][data-highlighted] *,
|
|
736
|
-
& [data-slot="command-item"][data-selected] *,
|
|
737
|
-
& [data-slot="command-item"][aria-selected="true"] * {
|
|
738
|
-
color: var(--background) !important;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
/* List hub views toolbar + segmented radiogroup (not Radix Tabs / tabs-list) */
|
|
742
|
-
& [data-slot="view-segmented-toolbar"] {
|
|
743
|
-
background-color: transparent !important;
|
|
744
|
-
border: 2px solid var(--foreground) !important;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
& [data-slot="view-segmented-item"][aria-pressed="true"],
|
|
748
|
-
& [data-slot="view-segmented-item"][aria-checked="true"] {
|
|
749
|
-
outline: 2px solid currentColor !important;
|
|
750
|
-
outline-offset: -2px !important;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
/* Floating surfaces — portal menus, popovers, sheets, dialogs */
|
|
754
|
-
& [data-slot="dropdown-menu-content"],
|
|
755
|
-
& [data-slot="dropdown-menu-sub-content"],
|
|
756
|
-
& [data-slot="popover-content"],
|
|
757
|
-
& [data-slot="select-content"],
|
|
758
|
-
& [data-slot="dialog-content"],
|
|
759
|
-
& [data-slot="sheet-content"],
|
|
760
|
-
& [data-slot="drawer-content"] {
|
|
761
|
-
border: 2px solid var(--foreground) !important;
|
|
762
|
-
background-color: var(--background) !important;
|
|
763
|
-
color: var(--foreground) !important;
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
& [data-slot="command"] {
|
|
767
|
-
background-color: var(--background) !important;
|
|
768
|
-
color: var(--foreground) !important;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
/* ── Pattern 2: Stronger focus ring (WCAG 2.4.11 / 2.4.12) ── */
|
|
772
|
-
& :focus-visible {
|
|
773
|
-
outline: 3px solid var(--ring) !important;
|
|
774
|
-
outline-offset: 3px !important;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
/* ── Pattern 2: Error state — thick border + no colour-only signal ── */
|
|
778
|
-
& [aria-invalid="true"],
|
|
779
|
-
& [data-invalid] {
|
|
780
|
-
border-width: 3px !important;
|
|
781
|
-
border-color: var(--destructive) !important;
|
|
782
|
-
/* Downstream: pair with a ⚠ icon in the component for non-colour signal */
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
/* ── Pattern 3: Active nav / toggle items use outline not bg-fill ── */
|
|
786
|
-
& [data-slot="sidebar-menu-button"][data-active="true"],
|
|
787
|
-
& [data-slot="toggle-group-item"][data-state="on"] {
|
|
788
|
-
outline: 2px solid currentColor !important;
|
|
789
|
-
outline-offset: -2px !important;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
/* ── OS-level "Increase Contrast" — macOS / iOS / Android ───────────────── */
|
|
794
|
-
@media (prefers-contrast: more) {
|
|
795
|
-
:root:not([data-contrast="off"]):not([data-contrast="windows"]) {
|
|
796
|
-
--foreground: oklch(0.06 0 0);
|
|
797
|
-
--muted-foreground: oklch(0.10 0 0);
|
|
798
|
-
--border: oklch(0.10 0 0);
|
|
799
|
-
--border-control: oklch(0.06 0 0);
|
|
800
|
-
--border-control-35: oklch(0.06 0 0);
|
|
801
|
-
--border-control-3: oklch(0.06 0 0);
|
|
802
|
-
--ring: oklch(0.06 0 0);
|
|
803
|
-
}
|
|
804
|
-
.dark:not([data-contrast="off"]):not([data-contrast="windows"]) {
|
|
805
|
-
--foreground: oklch(1 0 0);
|
|
806
|
-
--muted-foreground: oklch(0.88 0 0);
|
|
807
|
-
--border: oklch(1 0 0);
|
|
808
|
-
--border-control: oklch(0.88 0 0);
|
|
809
|
-
--border-control-35: oklch(0.88 0 0);
|
|
810
|
-
--border-control-3: oklch(0.88 0 0);
|
|
811
|
-
--ring: oklch(0.97 0.19 110); /* neon yellow on dark */
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
/* ── Windows Forced Colors (High Contrast Mode in OS) ───────────────────── */
|
|
816
|
-
/* When `forced-colors: active`, Windows REPLACES our colours entirely. */
|
|
817
|
-
/* Our job: map semantic roles to system palette keywords so the OS */
|
|
818
|
-
/* colouring makes sense, and ensure borders/outlines remain visible. */
|
|
819
|
-
@media (forced-colors: active) {
|
|
820
|
-
:root {
|
|
821
|
-
--border: CanvasText;
|
|
822
|
-
--border-control: CanvasText;
|
|
823
|
-
--border-control-35: CanvasText;
|
|
824
|
-
--border-control-3: CanvasText;
|
|
825
|
-
--ring: Highlight;
|
|
826
|
-
--destructive: LinkText;
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
/* Always show a 2px outline so focus is forced-color-safe */
|
|
830
|
-
:focus-visible {
|
|
831
|
-
outline: 2px solid Highlight !important;
|
|
832
|
-
outline-offset: 2px !important;
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
body {
|
|
836
|
-
background-color: Canvas;
|
|
837
|
-
color: CanvasText;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
a { color: LinkText; }
|
|
841
|
-
button { border: 1px solid ButtonText !important; }
|
|
842
|
-
|
|
843
|
-
/* Cards: OS strips bg fills — add explicit border so shape is preserved */
|
|
844
|
-
[data-slot="card"],
|
|
845
|
-
.card {
|
|
846
|
-
border: 2px solid CanvasText !important;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
/* Sidebar edge */
|
|
850
|
-
[data-slot="sidebar"] {
|
|
851
|
-
border-inline-end: 2px solid CanvasText !important;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
/* Wrapper-only sidebar badge (no inner Badge) — rare; keep single ring */
|
|
855
|
-
[data-slot="sidebar-menu-badge"]:not(:has([data-slot="badge"])) {
|
|
856
|
-
border: 2px solid CanvasText !important;
|
|
857
|
-
background-color: Canvas !important;
|
|
858
|
-
color: CanvasText !important;
|
|
859
|
-
outline: none !important;
|
|
860
|
-
box-shadow: none !important;
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
/* Positioning shell around <Badge/> — do not draw a second ring */
|
|
864
|
-
[data-slot="sidebar-menu-badge"]:has([data-slot="badge"]) {
|
|
865
|
-
border: none !important;
|
|
866
|
-
background: transparent !important;
|
|
867
|
-
outline: none !important;
|
|
868
|
-
box-shadow: none !important;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
[data-slot="badge"],
|
|
872
|
-
[data-slot="view-toolbar-count"] {
|
|
873
|
-
border: 2px solid CanvasText !important;
|
|
874
|
-
background-color: Canvas !important;
|
|
875
|
-
color: CanvasText !important;
|
|
876
|
-
outline: none !important;
|
|
877
|
-
box-shadow: none !important;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
[data-slot="badge"] *,
|
|
881
|
-
[data-slot="view-toolbar-count"] * {
|
|
882
|
-
color: CanvasText !important;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
/* Avoid button chrome + inner count pill = double border */
|
|
886
|
-
button[data-slot="view-segmented-item"] {
|
|
887
|
-
border: none !important;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
/* Radix Tabs (ChartCard, library, etc.) — gray inactive + invisible active in OS HC */
|
|
891
|
-
[data-slot="tabs-list"] {
|
|
892
|
-
border: 2px solid CanvasText !important;
|
|
893
|
-
background-color: transparent !important;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
[data-slot="tabs-trigger"] {
|
|
897
|
-
color: CanvasText !important;
|
|
898
|
-
background-color: transparent !important;
|
|
899
|
-
opacity: 1 !important;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
[data-slot="tabs-trigger"][data-state="active"] {
|
|
903
|
-
outline: 2px solid Highlight !important;
|
|
904
|
-
outline-offset: -2px !important;
|
|
905
|
-
font-weight: 600 !important;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
/* Key metrics strip — labels/values/trend icons must not stay muted */
|
|
909
|
-
[data-slot="key-metrics"] :where(span, p, button, a, i, label, div) {
|
|
910
|
-
color: CanvasText !important;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
/* Menu / select / command palette — accent fill is often low-contrast in forced mode */
|
|
914
|
-
[data-slot="dropdown-menu-item"][data-highlighted]:not([data-disabled]),
|
|
915
|
-
[data-slot="dropdown-menu-checkbox-item"][data-highlighted]:not([data-disabled]),
|
|
916
|
-
[data-slot="dropdown-menu-radio-item"][data-highlighted]:not([data-disabled]),
|
|
917
|
-
[data-slot="dropdown-menu-sub-trigger"][data-highlighted]:not([data-disabled]),
|
|
918
|
-
[data-slot="select-item"][data-highlighted]:not([data-disabled]),
|
|
919
|
-
[data-slot="command-item"][data-selected]:not([data-disabled]),
|
|
920
|
-
[data-slot="command-item"][aria-selected="true"]:not([data-disabled]) {
|
|
921
|
-
background-color: Highlight !important;
|
|
922
|
-
color: HighlightText !important;
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
[data-slot="dropdown-menu-item"][data-highlighted] *,
|
|
926
|
-
[data-slot="dropdown-menu-checkbox-item"][data-highlighted] *,
|
|
927
|
-
[data-slot="dropdown-menu-radio-item"][data-highlighted] *,
|
|
928
|
-
[data-slot="dropdown-menu-sub-trigger"][data-highlighted] *,
|
|
929
|
-
[data-slot="select-item"][data-highlighted] *,
|
|
930
|
-
[data-slot="command-item"][data-selected] *,
|
|
931
|
-
[data-slot="command-item"][aria-selected="true"] * {
|
|
932
|
-
color: HighlightText !important;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
/* Views toolbar (list hubs) + segmented radiogroup — selection via outline, not fill */
|
|
936
|
-
[data-slot="view-segmented-toolbar"] {
|
|
937
|
-
border: 2px solid CanvasText !important;
|
|
938
|
-
background: transparent !important;
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
[data-slot="view-segmented-item"] {
|
|
942
|
-
color: CanvasText !important;
|
|
943
|
-
background: transparent !important;
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
[data-slot="view-segmented-item"][aria-pressed="true"],
|
|
947
|
-
[data-slot="view-segmented-item"][aria-checked="true"] {
|
|
948
|
-
outline: 2px solid Highlight !important;
|
|
949
|
-
outline-offset: -2px !important;
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
/* Portal overlays — explicit Canvas pairing (OS often strips popover fills) */
|
|
953
|
-
[data-slot="dropdown-menu-content"],
|
|
954
|
-
[data-slot="dropdown-menu-sub-content"],
|
|
955
|
-
[data-slot="popover-content"],
|
|
956
|
-
[data-slot="select-content"],
|
|
957
|
-
[data-slot="dialog-content"],
|
|
958
|
-
[data-slot="sheet-content"],
|
|
959
|
-
[data-slot="drawer-content"] {
|
|
960
|
-
border: 2px solid CanvasText !important;
|
|
961
|
-
background-color: Canvas !important;
|
|
962
|
-
color: CanvasText !important;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
[data-slot="command"] {
|
|
966
|
-
background-color: Canvas !important;
|
|
967
|
-
color: CanvasText !important;
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
/* Charts — forced-colors strips SVG fills; add stroke as fallback */
|
|
971
|
-
svg path, svg rect, svg circle {
|
|
972
|
-
forced-color-adjust: none;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
/* ==========================================================================
|
|
977
|
-
Base layer — global element defaults with AA focus management
|
|
978
|
-
========================================================================== */
|
|
979
|
-
@layer base {
|
|
980
|
-
/* Reset + border-color default */
|
|
981
|
-
*, *::before, *::after {
|
|
982
|
-
@apply border-border;
|
|
983
|
-
box-sizing: border-box;
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
/* Body — outer canvas matches sidebar; main column uses bg-background (white in light) */
|
|
987
|
-
body {
|
|
988
|
-
@apply bg-sidebar text-foreground antialiased;
|
|
989
|
-
font-family: var(--font-sans);
|
|
990
|
-
/* 14px baseline on body. 1rem stays = 16px so all shadcn rem-based
|
|
991
|
-
layout (spacing, heights, radii) is unaffected at any zoom level. */
|
|
992
|
-
font-size: 0.875rem;
|
|
993
|
-
line-height: 1.5;
|
|
994
|
-
/* Font smoothing: see apps/web/app/globals.css body block (macOS-only effect; Windows ClearType unchanged). */
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
html {
|
|
998
|
-
@apply font-sans;
|
|
999
|
-
color-scheme: light;
|
|
1000
|
-
/* Match sidebar canvas (body) on overscroll */
|
|
1001
|
-
background-color: var(--sidebar);
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
html.dark {
|
|
1005
|
-
color-scheme: dark;
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
/* ── Focus management (WCAG 2.4.11 / 2.4.7) ──────────────────── */
|
|
1009
|
-
/* Visible on keyboard navigation only; hidden for mouse users */
|
|
1010
|
-
:focus-visible {
|
|
1011
|
-
outline: 2px solid var(--ring);
|
|
1012
|
-
outline-offset: 2px;
|
|
1013
|
-
border-radius: var(--radius-sm, 4px);
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
/* Suppress outline for mouse/touch (Safari workaround) */
|
|
1017
|
-
:focus:not(:focus-visible) {
|
|
1018
|
-
outline: none;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
/* ── Reduced motion (WCAG 2.3.3) ────────────────────────────── */
|
|
1022
|
-
@media (prefers-reduced-motion: reduce) {
|
|
1023
|
-
*, *::before, *::after {
|
|
1024
|
-
animation-duration: 0.01ms !important;
|
|
1025
|
-
animation-iteration-count: 1 !important;
|
|
1026
|
-
transition-duration: 0.01ms !important;
|
|
1027
|
-
scroll-behavior: auto !important;
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
/* ── Touch targets (WCAG 2.5.5) ─────────────────────────────── */
|
|
1032
|
-
/* Applied via utility: min-h-[var(--control-height-touch)] */
|
|
1033
|
-
/* Global touch-target baseline for interactive elements on mobile */
|
|
1034
|
-
@media (max-width: 767px) {
|
|
1035
|
-
button,
|
|
1036
|
-
[role="button"],
|
|
1037
|
-
[role="checkbox"],
|
|
1038
|
-
[role="radio"],
|
|
1039
|
-
[role="switch"],
|
|
1040
|
-
[role="menuitem"],
|
|
1041
|
-
[role="tab"],
|
|
1042
|
-
a {
|
|
1043
|
-
min-height: var(--control-height-touch, 44px);
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
/* ── Heading hierarchy ───────────────────────────────────────── */
|
|
1048
|
-
/* Ivy Presto (font-heading) applied ONLY via explicit class/inline style
|
|
1049
|
-
on PageHeader <h1>. All other headings use Inter (font-sans). */
|
|
1050
|
-
h1, h2, h3, h4, h5, h6 {
|
|
1051
|
-
font-family: var(--font-sans);
|
|
1052
|
-
font-weight: 600;
|
|
1053
|
-
line-height: 1.25;
|
|
1054
|
-
color: var(--foreground);
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
/* ── Skip to main content (WCAG 2.4.1) ─────────────────────── */
|
|
1058
|
-
.skip-to-content {
|
|
1059
|
-
position: absolute;
|
|
1060
|
-
left: -9999px;
|
|
1061
|
-
top: auto;
|
|
1062
|
-
width: 1px;
|
|
1063
|
-
height: 1px;
|
|
1064
|
-
overflow: hidden;
|
|
1065
|
-
z-index: 9999;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
.skip-to-content:focus {
|
|
1069
|
-
position: fixed;
|
|
1070
|
-
top: 0;
|
|
1071
|
-
left: 0;
|
|
1072
|
-
width: auto;
|
|
1073
|
-
height: auto;
|
|
1074
|
-
padding: 0.75rem 1.25rem;
|
|
1075
|
-
background: var(--background);
|
|
1076
|
-
color: var(--foreground);
|
|
1077
|
-
border: 2px solid var(--ring);
|
|
1078
|
-
border-radius: var(--radius-md);
|
|
1079
|
-
font-weight: 600;
|
|
1080
|
-
font-size: 1rem;
|
|
1081
|
-
z-index: 9999;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
/* ==========================================================================
|
|
1086
|
-
Utility layer — design-system helpers
|
|
1087
|
-
========================================================================== */
|
|
1088
|
-
@layer utilities {
|
|
1089
|
-
/* Visually hidden but accessible to screen readers (WCAG 1.3.1) */
|
|
1090
|
-
.sr-only {
|
|
1091
|
-
position: absolute;
|
|
1092
|
-
width: 1px;
|
|
1093
|
-
height: 1px;
|
|
1094
|
-
padding: 0;
|
|
1095
|
-
margin: -1px;
|
|
1096
|
-
overflow: hidden;
|
|
1097
|
-
clip: rect(0, 0, 0, 0);
|
|
1098
|
-
white-space: nowrap;
|
|
1099
|
-
border-width: 0;
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
/* AA-safe muted text — use instead of text-muted-foreground on light bg */
|
|
1103
|
-
.text-aa-muted {
|
|
1104
|
-
color: oklch(0.50 0.012 270); /* 5.5:1 on white ✓ */
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
/* Touch-target wrapper for icon buttons (mobile only) */
|
|
1108
|
-
.touch-target {
|
|
1109
|
-
min-height: var(--control-height-touch, 44px);
|
|
1110
|
-
min-width: var(--control-height-touch, 44px);
|
|
1111
|
-
display: inline-flex;
|
|
1112
|
-
align-items: center;
|
|
1113
|
-
justify-content: center;
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
@media (min-width: 768px) {
|
|
1117
|
-
.touch-target {
|
|
1118
|
-
min-height: unset;
|
|
1119
|
-
min-width: unset;
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
/* Form field border — use --control-border (3:1 minimum) */
|
|
1124
|
-
.field-border {
|
|
1125
|
-
border-color: var(--control-border);
|
|
1126
|
-
}
|
|
9
|
+
/* High-contrast variant — in-app High or Windows (JSON) contrast */
|
|
10
|
+
@custom-variant hc (&:is([data-contrast="high"] *, [data-contrast="windows"] *));
|
|
1127
11
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
12
|
+
@import "./tokens/tailwind-bridge.css";
|
|
13
|
+
@import "./tokens/base.css";
|
|
14
|
+
@import "./tokens/themes.css";
|
|
15
|
+
@import "./tokens/high-contrast.css";
|
|
16
|
+
@import "./tokens/layers.css";
|