@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
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
Base layer — global element defaults with AA focus management
|
|
3
|
+
========================================================================== */
|
|
4
|
+
@layer base {
|
|
5
|
+
/* Reset + border-color default */
|
|
6
|
+
*, *::before, *::after {
|
|
7
|
+
@apply border-border;
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Body — outer canvas matches sidebar; main column uses bg-background (white in light) */
|
|
12
|
+
body {
|
|
13
|
+
@apply bg-sidebar text-foreground antialiased;
|
|
14
|
+
font-family: var(--font-sans);
|
|
15
|
+
/* 14px baseline on body. 1rem stays = 16px so all shadcn rem-based
|
|
16
|
+
layout (spacing, heights, radii) is unaffected at any zoom level. */
|
|
17
|
+
font-size: 0.875rem;
|
|
18
|
+
line-height: 1.5;
|
|
19
|
+
/* Font smoothing (Tailwind `antialiased` = -webkit-font-smoothing + -moz-osx):
|
|
20
|
+
Industry practice per Josh Comeau’s CSS reset and 2024 cross-browser testing
|
|
21
|
+
(e.g. David Bushell): the -webkit property affects macOS WebKit browsers only —
|
|
22
|
+
it tones down browser-only subpixel AA (Mojave+ system UI already moved away).
|
|
23
|
+
Windows/Linux/iOS/Android: no practical effect; OS font rasterizer (ClearType, etc.)
|
|
24
|
+
owns rendering. Safe to apply on body for Mac parity without changing Windows output. */
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
html {
|
|
28
|
+
@apply font-sans;
|
|
29
|
+
color-scheme: light;
|
|
30
|
+
/* Match sidebar canvas (body) on overscroll */
|
|
31
|
+
background-color: var(--sidebar);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
html.dark {
|
|
35
|
+
color-scheme: dark;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* ── Focus management (WCAG 2.4.11 / 2.4.7) ──────────────────── */
|
|
39
|
+
/* Visible on keyboard navigation only; hidden for mouse users */
|
|
40
|
+
:focus-visible {
|
|
41
|
+
outline: 2px solid var(--ring);
|
|
42
|
+
outline-offset: 2px;
|
|
43
|
+
border-radius: var(--radius-sm, 4px);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Suppress outline for mouse/touch (Safari workaround) */
|
|
47
|
+
:focus:not(:focus-visible) {
|
|
48
|
+
outline: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* ── Reduced motion (WCAG 2.3.3) ────────────────────────────── */
|
|
52
|
+
@media (prefers-reduced-motion: reduce) {
|
|
53
|
+
*, *::before, *::after {
|
|
54
|
+
animation-duration: 0.01ms !important;
|
|
55
|
+
animation-iteration-count: 1 !important;
|
|
56
|
+
transition-duration: 0.01ms !important;
|
|
57
|
+
scroll-behavior: auto !important;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* ── Touch targets (WCAG 2.5.5) ─────────────────────────────── */
|
|
62
|
+
/* Applied via utility: min-h-[var(--control-height-touch)] */
|
|
63
|
+
/* Coarse pointer only: browser zoom (200–400%) shrinks the *layout* viewport
|
|
64
|
+
in CSS px, so max-width: 767px becomes true on desktop — without pointer: coarse,
|
|
65
|
+
44px min-height hits every button and [role=checkbox], stretching 16px-wide
|
|
66
|
+
checkboxes into tall pills and blowing up table/toolbar controls. Real phones
|
|
67
|
+
and tablets report pointer: coarse. */
|
|
68
|
+
@media (max-width: 767px) and (pointer: coarse) {
|
|
69
|
+
button,
|
|
70
|
+
[role="button"],
|
|
71
|
+
[role="radio"],
|
|
72
|
+
[role="switch"],
|
|
73
|
+
[role="menuitem"],
|
|
74
|
+
[role="tab"],
|
|
75
|
+
a {
|
|
76
|
+
min-height: var(--control-height-touch, 44px);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/* Checkboxes use compact box + pseudo-element hit slop (see ui/checkbox). */
|
|
80
|
+
|
|
81
|
+
/* ── Heading hierarchy ───────────────────────────────────────── */
|
|
82
|
+
/* Ivy Presto (font-heading) applied ONLY via explicit class/inline style
|
|
83
|
+
on PageHeader <h1>. All other headings use Inter (font-sans). */
|
|
84
|
+
h1, h2, h3, h4, h5, h6 {
|
|
85
|
+
font-family: var(--font-sans);
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
line-height: 1.25;
|
|
88
|
+
color: var(--foreground);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* ── Skip to main content (WCAG 2.4.1) ─────────────────────── */
|
|
92
|
+
.skip-to-content {
|
|
93
|
+
position: absolute;
|
|
94
|
+
left: -9999px;
|
|
95
|
+
top: auto;
|
|
96
|
+
width: 1px;
|
|
97
|
+
height: 1px;
|
|
98
|
+
overflow: hidden;
|
|
99
|
+
z-index: 9999;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.skip-to-content:focus {
|
|
103
|
+
position: fixed;
|
|
104
|
+
top: 0;
|
|
105
|
+
left: 0;
|
|
106
|
+
width: auto;
|
|
107
|
+
height: auto;
|
|
108
|
+
padding: 0.75rem 1.25rem;
|
|
109
|
+
background: var(--background);
|
|
110
|
+
color: var(--foreground);
|
|
111
|
+
border: 2px solid var(--ring);
|
|
112
|
+
border-radius: var(--radius-md);
|
|
113
|
+
font-weight: 600;
|
|
114
|
+
font-size: 1rem;
|
|
115
|
+
z-index: 9999;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ==========================================================================
|
|
120
|
+
Utility layer — design-system helpers
|
|
121
|
+
========================================================================== */
|
|
122
|
+
@layer utilities {
|
|
123
|
+
/* Visually hidden but accessible to screen readers (WCAG 1.3.1) */
|
|
124
|
+
.sr-only {
|
|
125
|
+
position: absolute;
|
|
126
|
+
width: 1px;
|
|
127
|
+
height: 1px;
|
|
128
|
+
padding: 0;
|
|
129
|
+
margin: -1px;
|
|
130
|
+
overflow: hidden;
|
|
131
|
+
clip: rect(0, 0, 0, 0);
|
|
132
|
+
white-space: nowrap;
|
|
133
|
+
border-width: 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* AA-safe muted text — use instead of text-muted-foreground on light bg */
|
|
137
|
+
.text-aa-muted {
|
|
138
|
+
color: oklch(0.50 0.012 270); /* 5.5:1 on white ✓ */
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* Touch-target wrapper for icon buttons (mobile only) */
|
|
142
|
+
.touch-target {
|
|
143
|
+
min-height: var(--control-height-touch, 44px);
|
|
144
|
+
min-width: var(--control-height-touch, 44px);
|
|
145
|
+
display: inline-flex;
|
|
146
|
+
align-items: center;
|
|
147
|
+
justify-content: center;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@media (min-width: 768px) {
|
|
151
|
+
.touch-target {
|
|
152
|
+
min-height: unset;
|
|
153
|
+
min-width: unset;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Form field border — use --control-border (3:1 minimum) */
|
|
158
|
+
.field-border {
|
|
159
|
+
border-color: var(--control-border);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Exxat Prism highlight banner */
|
|
163
|
+
.banner-prism {
|
|
164
|
+
background-color: var(--banner-prism-bg);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* Sidebar nav — little pop when an item becomes active. Key swap on the
|
|
168
|
+
wrapper remounts the span, so the animation replays on selection. */
|
|
169
|
+
@keyframes sidebar-icon-pop {
|
|
170
|
+
0% { opacity: 0.4; transform: scale(0.7) rotate(-6deg); }
|
|
171
|
+
60% { opacity: 1; transform: scale(1.15) rotate(2deg); }
|
|
172
|
+
100% { opacity: 1; transform: scale(1) rotate(0); }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* Radix Collapsible / Accordion — slide the children open/closed using the
|
|
176
|
+
`--radix-collapsible-content-height` CSS var that Radix sets on the
|
|
177
|
+
content element. Mirrors the shadcn accordion pattern (Tailwind v3
|
|
178
|
+
"accordion-down" / "accordion-up"); we keep our own name so it can be
|
|
179
|
+
reused by both Collapsible and Accordion primitives. */
|
|
180
|
+
@keyframes collapsible-down {
|
|
181
|
+
from { height: 0; }
|
|
182
|
+
to { height: var(--radix-collapsible-content-height); }
|
|
183
|
+
}
|
|
184
|
+
@keyframes collapsible-up {
|
|
185
|
+
from { height: var(--radix-collapsible-content-height); }
|
|
186
|
+
to { height: 0; }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Ask Leo suggestion chips — staggered fade-in + lift on first render. */
|
|
190
|
+
@keyframes leo-chip-in {
|
|
191
|
+
0% { opacity: 0; transform: translateY(8px) scale(0.96); }
|
|
192
|
+
100% { opacity: 1; transform: translateY(0) scale(1); }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Leo typing indicator — 3 dots bouncing opacity/scale, staggered via delay. */
|
|
196
|
+
@keyframes leo-typing-dot {
|
|
197
|
+
0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
|
|
198
|
+
40% { opacity: 1; transform: scale(1); }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Whole-panel brand radial glow pulse — runs on the BG layer while Leo thinks. */
|
|
202
|
+
@keyframes leo-panel-pulse {
|
|
203
|
+
0%, 100% { opacity: 0.5; transform: scale(1); }
|
|
204
|
+
50% { opacity: 0.75; transform: scale(1.01); }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Chart “Leo insight” marker — soft breathing halo behind the chip. */
|
|
208
|
+
@keyframes leo-chart-insight-halo {
|
|
209
|
+
0%, 100% { opacity: 0.45; transform: scale(1); }
|
|
210
|
+
50% { opacity: 0.72; transform: scale(1.12); }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Accent dot on the insight marker — gentle pulse (respect motion-reduce in markup). */
|
|
214
|
+
@keyframes leo-chart-insight-dot {
|
|
215
|
+
0%, 100% { opacity: 0.9; transform: scale(1); }
|
|
216
|
+
50% { opacity: 1; transform: scale(1.35); }
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* After all @layer rules: win over Tailwind preflight / layer merge so real controls show a pointer. */
|
|
221
|
+
button:not(:disabled):not([disabled]),
|
|
222
|
+
[role="button"]:not([aria-disabled="true"]):not([data-disabled]) {
|
|
223
|
+
cursor: pointer;
|
|
224
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* --------------------------------------------------------------------------
|
|
2
|
+
Tailwind v4 theme bridge — maps CSS custom-props → utility classes
|
|
3
|
+
-------------------------------------------------------------------------- */
|
|
4
|
+
@theme inline {
|
|
5
|
+
/* Typography */
|
|
6
|
+
--font-heading: "ivypresto-text";
|
|
7
|
+
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
8
|
+
/* Minimum product text: 11px — use `text-xs` or larger; avoid arbitrary classes below 11px */
|
|
9
|
+
--text-xs: 0.6875rem; /* 11px at 16px root */
|
|
10
|
+
|
|
11
|
+
/* Semantic color map */
|
|
12
|
+
--color-background: var(--background);
|
|
13
|
+
--color-foreground: var(--foreground);
|
|
14
|
+
--color-card: var(--card);
|
|
15
|
+
--color-card-foreground: var(--card-foreground);
|
|
16
|
+
--color-popover: var(--popover);
|
|
17
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
18
|
+
--color-primary: var(--primary);
|
|
19
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
20
|
+
--color-secondary: var(--secondary);
|
|
21
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
22
|
+
--color-muted: var(--muted);
|
|
23
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
24
|
+
--color-accent: var(--accent);
|
|
25
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
26
|
+
--color-destructive: var(--destructive);
|
|
27
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
28
|
+
--color-border: var(--border);
|
|
29
|
+
--color-border-control: var(--border-control);
|
|
30
|
+
--color-input: var(--input);
|
|
31
|
+
--color-input-background: var(--input-background);
|
|
32
|
+
--color-ring: var(--ring);
|
|
33
|
+
/* Modal / sheet / drawer scrim */
|
|
34
|
+
--color-overlay: var(--overlay);
|
|
35
|
+
|
|
36
|
+
/* Chart tokens */
|
|
37
|
+
--color-chart-1: var(--chart-1);
|
|
38
|
+
--color-chart-2: var(--chart-2);
|
|
39
|
+
--color-chart-3: var(--chart-3);
|
|
40
|
+
--color-chart-4: var(--chart-4);
|
|
41
|
+
--color-chart-5: var(--chart-5);
|
|
42
|
+
|
|
43
|
+
/* Chip / badge tokens (AA-compliant on light bg: ≥ 4.5:1) */
|
|
44
|
+
--color-chip-1: var(--chip-1);
|
|
45
|
+
--color-chip-2: var(--chip-2);
|
|
46
|
+
--color-chip-3: var(--chip-3);
|
|
47
|
+
--color-chip-4: var(--chip-4);
|
|
48
|
+
--color-chip-5: var(--chip-5);
|
|
49
|
+
--color-chip-destructive: var(--chip-destructive);
|
|
50
|
+
|
|
51
|
+
/* Brand accent + Exxat One tint scale (see :root --brand-*) */
|
|
52
|
+
--color-brand: var(--brand-color);
|
|
53
|
+
--color-brand-dark: var(--brand-color-dark);
|
|
54
|
+
--color-brand-foreground: var(--brand-foreground);
|
|
55
|
+
--color-brand-tint: var(--brand-tint);
|
|
56
|
+
--color-brand-tint-light: var(--brand-tint-light);
|
|
57
|
+
--color-brand-tint-subtle: var(--brand-tint-subtle);
|
|
58
|
+
--color-brand-color-light: var(--brand-color-light);
|
|
59
|
+
--color-brand-deep: var(--brand-color-deep);
|
|
60
|
+
|
|
61
|
+
/* Prism banner highlight */
|
|
62
|
+
--color-banner-prism: var(--banner-prism-bg);
|
|
63
|
+
|
|
64
|
+
/* Sidebar */
|
|
65
|
+
--color-sidebar: var(--sidebar);
|
|
66
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
67
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
68
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
69
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
70
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
71
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
72
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
73
|
+
/* WCAG 1.4.3 — always mixed against real --sidebar (lavender / rose / dark) */
|
|
74
|
+
--color-sidebar-section-label: var(--sidebar-section-label-foreground);
|
|
75
|
+
--color-secondary-panel-bg: var(--secondary-panel-bg);
|
|
76
|
+
|
|
77
|
+
/* Interactive hover — single source for ghost controls, lists, table chrome */
|
|
78
|
+
--color-interactive-hover: var(--interactive-hover);
|
|
79
|
+
--color-interactive-hover-foreground: var(--interactive-hover-foreground);
|
|
80
|
+
--color-interactive-hover-subtle: var(--interactive-hover-subtle);
|
|
81
|
+
--color-interactive-hover-soft: var(--interactive-hover-soft);
|
|
82
|
+
--color-interactive-hover-medium: var(--interactive-hover-medium);
|
|
83
|
+
--color-interactive-hover-strong: var(--interactive-hover-strong);
|
|
84
|
+
--color-interactive-hover-row: var(--interactive-hover-row);
|
|
85
|
+
|
|
86
|
+
/* DataTable — opaque surfaces for pinned/sticky cells and row states */
|
|
87
|
+
--color-dt-row-bg: var(--dt-row-bg);
|
|
88
|
+
--color-dt-row-hover: var(--dt-row-hover);
|
|
89
|
+
--color-dt-row-selected: var(--dt-row-selected);
|
|
90
|
+
--color-dt-row-selected-fg: var(--dt-row-selected-fg);
|
|
91
|
+
--color-dt-header-bg: var(--dt-header-bg);
|
|
92
|
+
--color-dt-group-bg: var(--dt-group-bg);
|
|
93
|
+
--color-dt-new-row-bg: var(--dt-new-row-bg);
|
|
94
|
+
--color-dt-new-row-border: var(--dt-new-row-border);
|
|
95
|
+
|
|
96
|
+
/* Border-radius scale (4 px base → 8 px default) */
|
|
97
|
+
--radius-sm: 4px;
|
|
98
|
+
--radius-md: 8px;
|
|
99
|
+
--radius-lg: 12px;
|
|
100
|
+
--radius-xl: 16px;
|
|
101
|
+
--radius-2xl: 20px;
|
|
102
|
+
--radius-3xl: 24px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* --------------------------------------------------------------------------
|
|
106
|
+
Text size (Settings → Appearance)
|
|
107
|
+
Pattern: root `font-size` steps like iOS Larger Text / Android font scale /
|
|
108
|
+
browser zoom-at-root so rem-based components track together. We keep steps
|
|
109
|
+
modest; `compact` redefines `--text-xs` so minimum UI copy stays 11px (Exxat
|
|
110
|
+
accessibility rule) when the root is slightly below 16px.
|
|
111
|
+
-------------------------------------------------------------------------- */
|
|
112
|
+
html[data-text-size="compact"] {
|
|
113
|
+
font-size: 94%;
|
|
114
|
+
--text-xs: 0.7333rem; /* ~11px when 1rem ≈ 15px */
|
|
115
|
+
}
|
|
116
|
+
html[data-text-size="large"] {
|
|
117
|
+
font-size: 112.5%;
|
|
118
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
Theme: Exxat One · Lavender (explicit — also applied as :root default)
|
|
3
|
+
Usage: <html class="theme-one"> or <html class="theme-lavender">
|
|
4
|
+
========================================================================== */
|
|
5
|
+
.theme-one,
|
|
6
|
+
.theme-lavender {
|
|
7
|
+
--brand-tint: oklch(0.9676 0.016 286.1);
|
|
8
|
+
--brand-tint-light: oklch(0.993 0.007 286.1);
|
|
9
|
+
--brand-tint-subtle: oklch(0.935 0.024 286.1);
|
|
10
|
+
--brand-color: oklch(0.50 0.14 286.1);
|
|
11
|
+
--brand-color-light: oklch(0.78 0.09 286.1);
|
|
12
|
+
--brand-color-dark: oklch(0.38 0.11 286.1);
|
|
13
|
+
--brand-color-deep: oklch(0.28 0.085 286.1);
|
|
14
|
+
--ring: var(--brand-color-dark);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Light surfaces only — must NOT override .dark (otherwise muted/accent stay “paper white” in dark mode) */
|
|
18
|
+
.theme-one:not(.dark),
|
|
19
|
+
.theme-lavender:not(.dark) {
|
|
20
|
+
--sidebar: var(--brand-tint);
|
|
21
|
+
--sidebar-accent: oklch(0.945 0.025 286.1);
|
|
22
|
+
--sidebar-border: oklch(0.92 0.025 286.1);
|
|
23
|
+
--secondary: oklch(0.95 0.012 286.1);
|
|
24
|
+
--accent: oklch(0.925 0.015 286.1);
|
|
25
|
+
--muted: oklch(0.945 0.008 286.1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.theme-one.dark,
|
|
29
|
+
.dark.theme-one,
|
|
30
|
+
.theme-lavender.dark,
|
|
31
|
+
.dark.theme-lavender {
|
|
32
|
+
--ring: var(--brand-color);
|
|
33
|
+
--background: oklch(0.20 0.008 286.1);
|
|
34
|
+
--sidebar: oklch(0.245 0.015 286.1);
|
|
35
|
+
--sidebar-accent: oklch(0.30 0.012 286.1);
|
|
36
|
+
--sidebar-border: oklch(0.38 0.010 286.1);
|
|
37
|
+
/* Restore dark surfaces (base .dark is overridden by :not(.dark) rules above when both classes apply) */
|
|
38
|
+
--secondary: oklch(0.31 0.04 286.1);
|
|
39
|
+
--muted: oklch(0.31 0.04 286.1);
|
|
40
|
+
--accent: oklch(0.33 0.06 286.1);
|
|
41
|
+
--brand-tint: oklch(0.28 0.014 286.1);
|
|
42
|
+
--brand-tint-light: oklch(0.30 0.014 286.1);
|
|
43
|
+
--brand-tint-subtle: oklch(0.24 0.018 286.1);
|
|
44
|
+
--secondary-panel-bg: oklch(0.262 0.012 286.1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* ==========================================================================
|
|
48
|
+
Theme: Exxat Prism · Rose · hue 343
|
|
49
|
+
Usage: <html class="theme-prism"> or <html class="theme-rose">
|
|
50
|
+
========================================================================== */
|
|
51
|
+
.theme-prism,
|
|
52
|
+
.theme-rose {
|
|
53
|
+
--brand-tint: oklch(0.97 0.02 343);
|
|
54
|
+
--brand-tint-light: oklch(0.992 0.01 343);
|
|
55
|
+
--brand-tint-subtle: oklch(0.93 0.028 343);
|
|
56
|
+
--brand-color: oklch(0.57 0.24 342); /* Prism rose */
|
|
57
|
+
--brand-color-light: oklch(0.78 0.14 342);
|
|
58
|
+
--brand-color-dark: oklch(0.42 0.24 342);
|
|
59
|
+
--brand-color-deep: oklch(0.32 0.20 342);
|
|
60
|
+
--ring: var(--brand-color-dark);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.theme-prism:not(.dark),
|
|
64
|
+
.theme-rose:not(.dark) {
|
|
65
|
+
--sidebar: oklch(0.97 0.02 343);
|
|
66
|
+
--sidebar-accent: oklch(0.945 0.025 343);
|
|
67
|
+
--sidebar-border: oklch(0.92 0.025 343);
|
|
68
|
+
--secondary: oklch(0.95 0.012 343);
|
|
69
|
+
--accent: oklch(0.925 0.015 343);
|
|
70
|
+
--muted: oklch(0.945 0.008 343);
|
|
71
|
+
--banner-prism-bg: oklch(0.97 0.02 343);
|
|
72
|
+
--theme-color-chrome: #fff5f9;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.theme-prism.dark,
|
|
76
|
+
.dark.theme-prism,
|
|
77
|
+
.theme-rose.dark,
|
|
78
|
+
.dark.theme-rose {
|
|
79
|
+
--ring: var(--brand-color);
|
|
80
|
+
--background: oklch(0.20 0.008 342);
|
|
81
|
+
--sidebar: oklch(0.245 0.015 342);
|
|
82
|
+
--sidebar-accent: oklch(0.30 0.012 342);
|
|
83
|
+
--sidebar-border: oklch(0.38 0.010 342);
|
|
84
|
+
--secondary: oklch(0.31 0.04 342);
|
|
85
|
+
--muted: oklch(0.31 0.04 342);
|
|
86
|
+
--accent: oklch(0.33 0.06 342);
|
|
87
|
+
--theme-color-chrome: #2a2428;
|
|
88
|
+
--brand-tint: oklch(0.28 0.014 342);
|
|
89
|
+
--brand-tint-light: oklch(0.30 0.014 342);
|
|
90
|
+
--brand-tint-subtle: oklch(0.24 0.018 342);
|
|
91
|
+
--card: oklch(0.27 0.012 342);
|
|
92
|
+
--secondary-panel-bg: oklch(0.262 0.012 342);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ==========================================================================
|
|
96
|
+
Theme: Exxat Assessment · Green · hue 159.88
|
|
97
|
+
Usage: <html class="theme-assessment">
|
|
98
|
+
========================================================================== */
|
|
99
|
+
.theme-assessment {
|
|
100
|
+
--brand-tint: oklch(0.965 0.018 159.88);
|
|
101
|
+
--brand-tint-light: oklch(0.992 0.008 159.88);
|
|
102
|
+
--brand-tint-subtle: oklch(0.93 0.028 159.88);
|
|
103
|
+
--brand-color: oklch(0.7 0.0913 159.88);
|
|
104
|
+
--brand-color-light: oklch(0.82 0.072 159.88);
|
|
105
|
+
--brand-color-dark: oklch(0.48 0.0913 159.88);
|
|
106
|
+
--brand-color-deep: oklch(0.34 0.075 159.88);
|
|
107
|
+
--ring: var(--brand-color-dark);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.theme-assessment:not(.dark) {
|
|
111
|
+
--sidebar: var(--brand-tint);
|
|
112
|
+
--sidebar-accent: oklch(0.945 0.026 159.88);
|
|
113
|
+
--sidebar-border: oklch(0.90 0.026 159.88);
|
|
114
|
+
--secondary: oklch(0.95 0.012 159.88);
|
|
115
|
+
--accent: oklch(0.925 0.016 159.88);
|
|
116
|
+
--muted: oklch(0.945 0.008 159.88);
|
|
117
|
+
--theme-color-chrome: #f1faf5;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.theme-assessment.dark,
|
|
121
|
+
.dark.theme-assessment {
|
|
122
|
+
--ring: var(--brand-color);
|
|
123
|
+
--background: oklch(0.20 0.008 159.88);
|
|
124
|
+
--sidebar: oklch(0.245 0.015 159.88);
|
|
125
|
+
--sidebar-accent: oklch(0.30 0.012 159.88);
|
|
126
|
+
--sidebar-border: oklch(0.38 0.010 159.88);
|
|
127
|
+
--secondary: oklch(0.31 0.04 159.88);
|
|
128
|
+
--muted: oklch(0.31 0.04 159.88);
|
|
129
|
+
--accent: oklch(0.33 0.06 159.88);
|
|
130
|
+
--theme-color-chrome: #242a27;
|
|
131
|
+
--brand-tint: oklch(0.28 0.014 159.88);
|
|
132
|
+
--brand-tint-light: oklch(0.30 0.014 159.88);
|
|
133
|
+
--brand-tint-subtle: oklch(0.24 0.018 159.88);
|
|
134
|
+
--card: oklch(0.27 0.012 159.88);
|
|
135
|
+
--secondary-panel-bg: oklch(0.262 0.012 159.88);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* ==========================================================================
|
|
139
|
+
Theme: Custom product · user-selected hue
|
|
140
|
+
Usage: <html class="theme-custom" style="--custom-product-brand-color: …">
|
|
141
|
+
========================================================================== */
|
|
142
|
+
/*
|
|
143
|
+
* Tint chroma is **derived from the source's chroma** so neighbouring hues
|
|
144
|
+
* (e.g. Blue h=252 vs Indigo h=280 vs Purple h=286) read as distinct pale
|
|
145
|
+
* tints instead of collapsing to the same near-white. The earlier formula
|
|
146
|
+
* pinned chroma to a fixed `0.018`, which is below the hue-discrimination
|
|
147
|
+
* threshold at L≈0.96 for closely-spaced hues — that's why Blue / Indigo /
|
|
148
|
+
* Purple looked identical in the sidebar.
|
|
149
|
+
*
|
|
150
|
+
* `max(<floor>, calc(c * <fraction>))` rules:
|
|
151
|
+
* - vibrant brand colours (c ≈ 0.10–0.23) get a meaningfully tinted
|
|
152
|
+
* sidebar / accent / muted scale → product chrome visibly changes per
|
|
153
|
+
* pick
|
|
154
|
+
* - low-chroma custom-hex picks (greys) fall back to the floor so they
|
|
155
|
+
* don't render as pure white
|
|
156
|
+
* - lightness + hue stay pinned to the original mock-up values, so the
|
|
157
|
+
* overall "very pale background" feel is unchanged
|
|
158
|
+
*/
|
|
159
|
+
.theme-custom {
|
|
160
|
+
--brand-tint: oklch(from var(--custom-product-brand-color) 0.965 max(0.018, calc(c * 0.20)) h);
|
|
161
|
+
--brand-tint-light: oklch(from var(--custom-product-brand-color) 0.992 max(0.008, calc(c * 0.08)) h);
|
|
162
|
+
--brand-tint-subtle: oklch(from var(--custom-product-brand-color) 0.93 max(0.028, calc(c * 0.30)) h);
|
|
163
|
+
--brand-color: var(--custom-product-brand-color);
|
|
164
|
+
--brand-color-light: oklch(from var(--custom-product-brand-color) 0.82 c h);
|
|
165
|
+
--brand-color-dark: oklch(from var(--custom-product-brand-color) 0.48 c h);
|
|
166
|
+
--brand-color-deep: oklch(from var(--custom-product-brand-color) 0.34 c h);
|
|
167
|
+
--ring: var(--brand-color-dark);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.theme-custom:not(.dark) {
|
|
171
|
+
--sidebar: var(--brand-tint);
|
|
172
|
+
--sidebar-accent: oklch(from var(--custom-product-brand-color) 0.945 max(0.026, calc(c * 0.28)) h);
|
|
173
|
+
--sidebar-border: oklch(from var(--custom-product-brand-color) 0.90 max(0.026, calc(c * 0.28)) h);
|
|
174
|
+
--secondary: oklch(from var(--custom-product-brand-color) 0.95 max(0.012, calc(c * 0.14)) h);
|
|
175
|
+
--accent: oklch(from var(--custom-product-brand-color) 0.925 max(0.016, calc(c * 0.18)) h);
|
|
176
|
+
--muted: oklch(from var(--custom-product-brand-color) 0.945 max(0.008, calc(c * 0.10)) h);
|
|
177
|
+
--theme-color-chrome: color-mix(in oklch, var(--custom-product-brand-color) 10%, white);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.theme-custom.dark,
|
|
181
|
+
.dark.theme-custom {
|
|
182
|
+
--ring: var(--brand-color);
|
|
183
|
+
--background: oklch(from var(--custom-product-brand-color) 0.20 max(0.008, calc(c * 0.10)) h);
|
|
184
|
+
--sidebar: oklch(from var(--custom-product-brand-color) 0.245 max(0.015, calc(c * 0.18)) h);
|
|
185
|
+
--sidebar-accent: oklch(from var(--custom-product-brand-color) 0.30 max(0.012, calc(c * 0.14)) h);
|
|
186
|
+
--sidebar-border: oklch(from var(--custom-product-brand-color) 0.38 max(0.010, calc(c * 0.12)) h);
|
|
187
|
+
--secondary: oklch(from var(--custom-product-brand-color) 0.31 max(0.04, calc(c * 0.40)) h);
|
|
188
|
+
--muted: oklch(from var(--custom-product-brand-color) 0.31 max(0.04, calc(c * 0.40)) h);
|
|
189
|
+
--accent: oklch(from var(--custom-product-brand-color) 0.33 max(0.06, calc(c * 0.50)) h);
|
|
190
|
+
--theme-color-chrome: color-mix(in oklch, var(--custom-product-brand-color) 12%, black);
|
|
191
|
+
--brand-tint: oklch(from var(--custom-product-brand-color) 0.28 max(0.014, calc(c * 0.20)) h);
|
|
192
|
+
--brand-tint-light: oklch(from var(--custom-product-brand-color) 0.30 max(0.014, calc(c * 0.20)) h);
|
|
193
|
+
--brand-tint-subtle: oklch(from var(--custom-product-brand-color) 0.24 max(0.018, calc(c * 0.28)) h);
|
|
194
|
+
--card: oklch(from var(--custom-product-brand-color) 0.27 max(0.012, calc(c * 0.16)) h);
|
|
195
|
+
--secondary-panel-bg: oklch(from var(--custom-product-brand-color) 0.262 max(0.012, calc(c * 0.14)) h);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* Nested secondary panel — always paint from token (not Tailwind arbitrary var) */
|
|
199
|
+
[data-slot="secondary-panel"][data-state="open"] {
|
|
200
|
+
background-color: var(--secondary-panel-bg);
|
|
201
|
+
}
|