@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.
Files changed (140) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/consumer-extras/AGENTS.md +76 -0
  3. package/consumer-extras/README.md +5 -1
  4. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +14 -3
  5. package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +37 -0
  6. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +21 -6
  7. package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +57 -0
  8. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +4 -2
  9. package/consumer-extras/patterns/consumer-app-pattern.md +39 -0
  10. package/consumer-extras/patterns/consumer-upgrade-checklist.md +20 -0
  11. package/consumer-extras/patterns/data-views-pattern.md +40 -3
  12. package/consumer-extras/patterns/focused-workflow-page-pattern.md +84 -0
  13. package/consumer-extras/patterns/shell-surface-elevation-pattern.md +5 -3
  14. package/package.json +2 -1
  15. package/src/components/ui/button-group.tsx +81 -0
  16. package/src/components/ui/button.tsx +4 -4
  17. package/src/globals.css +7 -1858
  18. package/src/theme.css +10 -1126
  19. package/src/tokens/README.md +15 -0
  20. package/src/tokens/base.css +337 -0
  21. package/src/tokens/high-contrast.css +1195 -0
  22. package/src/tokens/layers.css +224 -0
  23. package/src/tokens/tailwind-bridge.css +118 -0
  24. package/src/tokens/themes.css +201 -0
  25. package/template/AGENTS.md +60 -22
  26. package/template/app/(app)/dashboard/loading.tsx +3 -15
  27. package/template/app/(app)/dashboard/page.tsx +2 -14
  28. package/template/app/(app)/data-list/layout.tsx +43 -0
  29. package/template/app/(app)/data-list/page.tsx +2 -2
  30. package/template/app/(app)/examples/focused-workflow/page.tsx +5 -0
  31. package/template/app/(app)/examples/page.tsx +1 -0
  32. package/template/app/(app)/loading.tsx +1 -18
  33. package/template/app/(app)/question-bank/find/page.tsx +2 -1
  34. package/template/app/(app)/question-bank/library/page.tsx +2 -1
  35. package/template/app/(app)/question-bank/list/page.tsx +2 -1
  36. package/template/app/(app)/question-bank/new/page.tsx +15 -23
  37. package/template/app/(app)/question-bank/page.tsx +2 -1
  38. package/template/app/(app)/settings/page.tsx +4 -5
  39. package/template/app/globals.css +7 -1964
  40. package/template/components/app-route-loading.tsx +14 -0
  41. package/template/components/app-sidebar.tsx +70 -55
  42. package/template/components/data-views/index.ts +37 -9
  43. package/template/components/data-views/list-page-calendar-view.tsx +593 -0
  44. package/template/components/data-views/list-page-connected-view-body.tsx +66 -0
  45. package/template/components/data-views/list-page-folder-columns-panel.tsx +345 -0
  46. package/template/components/data-views/list-page-split-hub-chrome.tsx +8 -0
  47. package/template/components/examples/focused-workflow-showcase.tsx +183 -0
  48. package/template/components/list-hub-board-view.tsx +68 -0
  49. package/template/components/list-hub-client.tsx +186 -0
  50. package/template/components/list-hub-list-view.tsx +36 -0
  51. package/template/components/list-hub-panel-activator.tsx +8 -0
  52. package/template/components/list-hub-secondary-nav.tsx +121 -0
  53. package/template/components/list-hub-table.tsx +336 -0
  54. package/template/components/new-question-composer.tsx +6 -24
  55. package/template/components/product-switcher.tsx +3 -2
  56. package/template/components/question-bank-client.tsx +4 -1
  57. package/template/components/question-bank-folder-columns-panel.tsx +104 -0
  58. package/template/components/question-bank-table.tsx +143 -485
  59. package/template/components/secondary-panel/nav-link-rows.tsx +83 -0
  60. package/template/components/secondary-panel.tsx +4 -44
  61. package/template/components/secondary-panels/list-hub-panel.tsx +39 -0
  62. package/template/components/secondary-panels/question-bank-panel.tsx +39 -0
  63. package/template/components/secondary-panels/registry.tsx +15 -0
  64. package/template/components/settings-appearance-card.tsx +3 -2
  65. package/template/components/settings-client.tsx +59 -15
  66. package/template/components/settings-form-row.tsx +9 -4
  67. package/template/components/table-properties/drawer-button.tsx +13 -0
  68. package/template/components/table-properties/drawer.tsx +65 -4
  69. package/template/components/templates/focused-workflow-layouts.tsx +448 -0
  70. package/template/components/templates/focused-workflow-page-template.tsx +69 -0
  71. package/template/components/templates/list-page.tsx +29 -5
  72. package/template/components/templates/nested-secondary-panel-shell.tsx +2 -1
  73. package/template/components/templates/page-loading-shell.tsx +262 -0
  74. package/template/components/ui/button-group.tsx +1 -0
  75. package/template/docs/consumer-app-pattern.md +39 -0
  76. package/template/docs/data-views-pattern.md +40 -3
  77. package/template/docs/drawer-vs-dialog-pattern.md +3 -1
  78. package/template/docs/focused-workflow-page-pattern.md +84 -0
  79. package/template/docs/shell-surface-elevation-pattern.md +5 -3
  80. package/template/lib/command-menu-search-data.ts +11 -27
  81. package/template/lib/data-list-display-options.ts +16 -2
  82. package/template/lib/data-list-view-registry.ts +104 -0
  83. package/template/lib/data-list-view-surface.ts +15 -1
  84. package/template/lib/data-list-view.ts +10 -1
  85. package/template/lib/data-view-dashboard-storage.ts +38 -35
  86. package/template/lib/hub-connected-view-renderers.ts +58 -0
  87. package/template/lib/list-hub-nav.ts +121 -0
  88. package/template/lib/list-hub-supported-views.ts +10 -0
  89. package/template/lib/list-page-table-properties.ts +3 -7
  90. package/template/lib/list-status-badges.ts +4 -97
  91. package/template/lib/mock/list-hub-directory.ts +27 -0
  92. package/template/lib/mock/list-hub-kpi.ts +27 -0
  93. package/template/lib/mock/navigation.tsx +1 -0
  94. package/template/lib/page-loading-variant.ts +40 -0
  95. package/template/lib/question-bank-supported-views.ts +13 -0
  96. package/template/lib/table-state-lifecycle.ts +2 -2
  97. package/template/app/(app)/data-list/[id]/page.tsx +0 -44
  98. package/template/app/(app)/data-list/new/page.tsx +0 -34
  99. package/template/components/compliance-board-view.tsx +0 -142
  100. package/template/components/compliance-client.tsx +0 -92
  101. package/template/components/compliance-list-view.tsx +0 -54
  102. package/template/components/compliance-page-header.tsx +0 -89
  103. package/template/components/compliance-table.tsx +0 -612
  104. package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
  105. package/template/components/data-view-dashboard-charts-team.tsx +0 -971
  106. package/template/components/data-view-dashboard-charts.tsx +0 -1503
  107. package/template/components/new-placement-back-btn.tsx +0 -28
  108. package/template/components/new-placement-form.tsx +0 -1068
  109. package/template/components/placement-board-card.tsx +0 -262
  110. package/template/components/placement-detail.tsx +0 -438
  111. package/template/components/placements-board-view.tsx +0 -404
  112. package/template/components/placements-client.tsx +0 -252
  113. package/template/components/placements-list-view.tsx +0 -171
  114. package/template/components/placements-page-header.tsx +0 -166
  115. package/template/components/placements-table-cells.test.tsx +0 -22
  116. package/template/components/placements-table-cells.tsx +0 -173
  117. package/template/components/placements-table-columns.tsx +0 -640
  118. package/template/components/placements-table.tsx +0 -1642
  119. package/template/components/rotations-empty-state.tsx +0 -50
  120. package/template/components/rotations-panel-activator.tsx +0 -8
  121. package/template/components/sites-all-client.tsx +0 -154
  122. package/template/components/sites-board-view.tsx +0 -67
  123. package/template/components/sites-list-view.tsx +0 -42
  124. package/template/components/sites-table.tsx +0 -382
  125. package/template/components/team-board-view.tsx +0 -122
  126. package/template/components/team-client.tsx +0 -100
  127. package/template/components/team-list-view.tsx +0 -59
  128. package/template/components/team-page-header.tsx +0 -92
  129. package/template/components/team-table.tsx +0 -693
  130. package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
  131. package/template/lib/mock/compliance-kpi.ts +0 -61
  132. package/template/lib/mock/compliance.ts +0 -146
  133. package/template/lib/mock/placements-kpi.ts +0 -134
  134. package/template/lib/mock/placements.ts +0 -183
  135. package/template/lib/mock/sites-directory.ts +0 -16
  136. package/template/lib/mock/sites-kpi.ts +0 -25
  137. package/template/lib/mock/team-kpi.ts +0 -60
  138. package/template/lib/mock/team.ts +0 -118
  139. package/template/lib/placement-board-card-layout.ts +0 -79
  140. 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
+ }