@calliopelabs/ui 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +332 -2
- package/dist/appearance/AppearancePicker.d.ts +10 -0
- package/dist/appearance/AppearancePicker.js +75 -0
- package/dist/appearance/AppearanceProvider.d.ts +53 -0
- package/dist/appearance/AppearanceProvider.js +113 -0
- package/dist/appearance/AppearanceScript.d.ts +16 -0
- package/dist/appearance/AppearanceScript.js +14 -0
- package/dist/appearance/appearance.d.ts +90 -0
- package/dist/appearance/appearance.js +151 -0
- package/dist/appearance/boot.d.ts +30 -0
- package/dist/appearance/boot.js +91 -0
- package/dist/appearance/brands.d.ts +94 -0
- package/dist/appearance/brands.js +61 -0
- package/dist/appearance/product.d.ts +89 -0
- package/dist/appearance/product.js +96 -0
- package/dist/data/DataTable.d.ts +66 -0
- package/dist/data/DataTable.js +148 -0
- package/dist/data/List.d.ts +55 -0
- package/dist/data/List.js +41 -0
- package/dist/data/types.d.ts +92 -0
- package/dist/data/types.js +2 -0
- package/dist/data/use-row-selection.d.ts +17 -0
- package/dist/data/use-row-selection.js +51 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +61 -0
- package/dist/lib/cn.d.ts +3 -0
- package/dist/lib/cn.js +6 -0
- package/dist/lib/local-store.d.ts +12 -0
- package/dist/lib/local-store.js +54 -0
- package/dist/nav/Nav.d.ts +21 -0
- package/dist/nav/Nav.js +97 -0
- package/dist/nav/model.d.ts +77 -0
- package/dist/nav/model.js +72 -0
- package/dist/overlays/ConfirmDialog.d.ts +37 -0
- package/dist/overlays/ConfirmDialog.js +70 -0
- package/dist/overlays/Dialog.d.ts +18 -0
- package/dist/overlays/Dialog.js +39 -0
- package/dist/overlays/Drawer.d.ts +24 -0
- package/dist/overlays/Drawer.js +42 -0
- package/dist/page/Breadcrumbs.d.ts +20 -0
- package/dist/page/Breadcrumbs.js +25 -0
- package/dist/page/PageFrame.d.ts +17 -0
- package/dist/page/PageFrame.js +14 -0
- package/dist/page/PageHeader.d.ts +28 -0
- package/dist/page/PageHeader.js +13 -0
- package/dist/page/Section.d.ts +18 -0
- package/dist/page/Section.js +12 -0
- package/dist/page/TabNav.d.ts +19 -0
- package/dist/page/TabNav.js +16 -0
- package/dist/primitives/Button.d.ts +16 -0
- package/dist/primitives/Button.js +36 -0
- package/dist/primitives/Checkbox.d.ts +8 -0
- package/dist/primitives/Checkbox.js +13 -0
- package/dist/primitives/DropdownMenu.d.ts +11 -0
- package/dist/primitives/DropdownMenu.js +25 -0
- package/dist/primitives/Input.d.ts +2 -0
- package/dist/primitives/Input.js +5 -0
- package/dist/primitives/Popover.d.ts +5 -0
- package/dist/primitives/Popover.js +13 -0
- package/dist/primitives/Skeleton.d.ts +3 -0
- package/dist/primitives/Skeleton.js +6 -0
- package/dist/primitives/Tabs.d.ts +12 -0
- package/dist/primitives/Tabs.js +22 -0
- package/dist/provider/UIProvider.d.ts +29 -0
- package/dist/provider/UIProvider.js +21 -0
- package/dist/provider/messages.d.ts +106 -0
- package/dist/provider/messages.js +104 -0
- package/dist/shell/AppShell.d.ts +43 -0
- package/dist/shell/AppShell.js +62 -0
- package/dist/shell/BrandMark.d.ts +14 -0
- package/dist/shell/BrandMark.js +14 -0
- package/dist/shell/RailPanel.d.ts +39 -0
- package/dist/shell/RailPanel.js +62 -0
- package/dist/shell/RailTree.d.ts +35 -0
- package/dist/shell/RailTree.js +32 -0
- package/dist/shell/Sidebar.d.ts +26 -0
- package/dist/shell/Sidebar.js +21 -0
- package/dist/shell/UserMenu.d.ts +22 -0
- package/dist/shell/UserMenu.js +31 -0
- package/dist/shell/placement.d.ts +10 -0
- package/dist/shell/placement.js +6 -0
- package/dist/shell/rail-state.d.ts +13 -0
- package/dist/shell/rail-state.js +28 -0
- package/dist/states/EmptyState.d.ts +20 -0
- package/dist/states/EmptyState.js +15 -0
- package/dist/states/ErrorState.d.ts +23 -0
- package/dist/states/ErrorState.js +16 -0
- package/dist/states/LoadingState.d.ts +14 -0
- package/dist/states/LoadingState.js +14 -0
- package/dist/status/StatusBadge.d.ts +19 -0
- package/dist/status/StatusBadge.js +13 -0
- package/dist/status/StatusDot.d.ts +14 -0
- package/dist/status/StatusDot.js +7 -0
- package/dist/status/tones.d.ts +17 -0
- package/dist/status/tones.js +29 -0
- package/dist/styles/preset.css +175 -0
- package/dist/styles/theme.css +339 -0
- package/dist/theme/contrast.d.ts +8 -0
- package/dist/theme/contrast.js +18 -0
- package/dist/theme/css.d.ts +23 -0
- package/dist/theme/css.js +116 -0
- package/dist/theme/tokens.d.ts +195 -0
- package/dist/theme/tokens.js +294 -0
- package/package.json +112 -4
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @calliopelabs/ui theme tokens.
|
|
3
|
+
* Generated by `npm run theme` from src/theme/tokens.ts. Do not edit by hand.
|
|
4
|
+
*
|
|
5
|
+
* The appearance axes ride on attributes of <html>, set before first paint by
|
|
6
|
+
* AppearanceScript and kept in step by AppearanceProvider:
|
|
7
|
+
* data-mode dark | light (follows the ground)
|
|
8
|
+
* data-ground the surface ramp
|
|
9
|
+
* data-accent a family accent, the product's brand accent, or custom
|
|
10
|
+
* data-density compact | cards (any element may carry its own)
|
|
11
|
+
* --radius the corners setting, inline
|
|
12
|
+
* Semantic status tokens are defined per mode only: no ground and no accent
|
|
13
|
+
* may change what ok, warn, info or error look like.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/* Mode-independent tokens */
|
|
17
|
+
:root {
|
|
18
|
+
--radius: 2px;
|
|
19
|
+
--motion-fast: 120ms;
|
|
20
|
+
--motion-base: 200ms;
|
|
21
|
+
--motion-slow: 320ms;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Dark: black ground, green accent */
|
|
25
|
+
:root,
|
|
26
|
+
[data-mode="dark"] {
|
|
27
|
+
color-scheme: dark;
|
|
28
|
+
--background: #000000;
|
|
29
|
+
--card: #0a0a0a;
|
|
30
|
+
--popover: #141414;
|
|
31
|
+
--secondary: #161616;
|
|
32
|
+
--muted: #111111;
|
|
33
|
+
--accent: #1b1b1b;
|
|
34
|
+
--muted-foreground: #848b88;
|
|
35
|
+
--border: rgba(255, 255, 255, 0.1);
|
|
36
|
+
--input: rgba(255, 255, 255, 0.1);
|
|
37
|
+
--sidebar: var(--brand-sidebar, #0a0a0a);
|
|
38
|
+
--sidebar-accent: color-mix(in oklab, var(--primary) 16%, transparent);
|
|
39
|
+
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
40
|
+
--foreground: #f0f2f1;
|
|
41
|
+
--card-foreground: #f0f2f1;
|
|
42
|
+
--popover-foreground: #f0f2f1;
|
|
43
|
+
--secondary-foreground: #f0f2f1;
|
|
44
|
+
--accent-foreground: #f0f2f1;
|
|
45
|
+
--brand-primary: #2f9e52;
|
|
46
|
+
--brand-on-primary: #04241b;
|
|
47
|
+
--primary: var(--brand-primary);
|
|
48
|
+
--primary-foreground: var(--brand-on-primary);
|
|
49
|
+
--destructive: #ff6b6b;
|
|
50
|
+
--ring: var(--brand-primary);
|
|
51
|
+
--chart-1: var(--brand-primary);
|
|
52
|
+
--chart-2: #8fd82a;
|
|
53
|
+
--chart-3: #cbf39d;
|
|
54
|
+
--chart-4: #e0b34a;
|
|
55
|
+
--chart-5: #0b6644;
|
|
56
|
+
--sidebar-foreground: #f0f2f1;
|
|
57
|
+
--sidebar-primary: var(--brand-primary);
|
|
58
|
+
--sidebar-primary-foreground: var(--brand-on-primary);
|
|
59
|
+
--sidebar-accent-foreground: #f0f2f1;
|
|
60
|
+
--sidebar-ring: var(--brand-primary);
|
|
61
|
+
--surface-0: var(--background);
|
|
62
|
+
--surface-1: var(--card);
|
|
63
|
+
--surface-2: var(--popover);
|
|
64
|
+
--success: #8fd82a;
|
|
65
|
+
--success-fg: #a6e34d;
|
|
66
|
+
--success-bg: rgba(143, 216, 42, 0.1);
|
|
67
|
+
--success-border: rgba(143, 216, 42, 0.35);
|
|
68
|
+
--warning: #f0c34e;
|
|
69
|
+
--warning-fg: #f0c34e;
|
|
70
|
+
--warning-bg: rgba(240, 195, 78, 0.1);
|
|
71
|
+
--warning-border: rgba(240, 195, 78, 0.35);
|
|
72
|
+
--info: #2f9e52;
|
|
73
|
+
--info-fg: #5cbb79;
|
|
74
|
+
--info-bg: rgba(47, 158, 82, 0.14);
|
|
75
|
+
--info-border: rgba(47, 158, 82, 0.4);
|
|
76
|
+
--danger: #ff6b6b;
|
|
77
|
+
--danger-fg: #ff8a8a;
|
|
78
|
+
--danger-bg: rgba(255, 107, 107, 0.1);
|
|
79
|
+
--danger-border: rgba(255, 107, 107, 0.35);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Light: neutral ground, green accent */
|
|
83
|
+
[data-mode="light"] {
|
|
84
|
+
color-scheme: light;
|
|
85
|
+
--background: #fafaf9;
|
|
86
|
+
--card: #ffffff;
|
|
87
|
+
--popover: #ffffff;
|
|
88
|
+
--secondary: #f1f1f0;
|
|
89
|
+
--muted: #f1f1f0;
|
|
90
|
+
--accent: #f3ebe5;
|
|
91
|
+
--muted-foreground: #5c5b58;
|
|
92
|
+
--border: #dfdedc;
|
|
93
|
+
--input: #dfdedc;
|
|
94
|
+
--sidebar: #f4f3f1;
|
|
95
|
+
--sidebar-accent: #f3ebe5;
|
|
96
|
+
--sidebar-border: #dfdedc;
|
|
97
|
+
--foreground: #1e1d1b;
|
|
98
|
+
--card-foreground: #1e1d1b;
|
|
99
|
+
--popover-foreground: #1e1d1b;
|
|
100
|
+
--secondary-foreground: #1e1d1b;
|
|
101
|
+
--accent-foreground: #1e1d1b;
|
|
102
|
+
--brand-primary: #26803f;
|
|
103
|
+
--brand-on-primary: #f3fbf7;
|
|
104
|
+
--primary: var(--brand-primary);
|
|
105
|
+
--primary-foreground: var(--brand-on-primary);
|
|
106
|
+
--destructive: #d40c1a;
|
|
107
|
+
--ring: var(--brand-primary);
|
|
108
|
+
--chart-1: var(--brand-primary);
|
|
109
|
+
--chart-2: #5c9c1a;
|
|
110
|
+
--chart-3: #8a7a5c;
|
|
111
|
+
--chart-4: #b8860b;
|
|
112
|
+
--chart-5: #26803f;
|
|
113
|
+
--sidebar-foreground: #1e1d1b;
|
|
114
|
+
--sidebar-primary: var(--brand-primary);
|
|
115
|
+
--sidebar-primary-foreground: var(--brand-on-primary);
|
|
116
|
+
--sidebar-accent-foreground: #1e1d1b;
|
|
117
|
+
--sidebar-ring: var(--brand-primary);
|
|
118
|
+
--surface-0: var(--background);
|
|
119
|
+
--surface-1: var(--card);
|
|
120
|
+
--surface-2: var(--popover);
|
|
121
|
+
--success: #508d23;
|
|
122
|
+
--success-fg: #305a12;
|
|
123
|
+
--success-bg: #e3f5da;
|
|
124
|
+
--success-border: #b4d7a2;
|
|
125
|
+
--warning: #cb7a00;
|
|
126
|
+
--warning-fg: #824c12;
|
|
127
|
+
--warning-bg: #fef0d4;
|
|
128
|
+
--warning-border: #e8c184;
|
|
129
|
+
--info: #2f8543;
|
|
130
|
+
--info-fg: #1b622d;
|
|
131
|
+
--info-bg: #e2f4e3;
|
|
132
|
+
--info-border: #acd8b1;
|
|
133
|
+
--danger: #df2225;
|
|
134
|
+
--danger-fg: #bb0916;
|
|
135
|
+
--danger-bg: #ffeae9;
|
|
136
|
+
--danger-border: #ffb1aa;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Ground: Black (dark) */
|
|
140
|
+
[data-mode="dark"][data-ground="black"] {
|
|
141
|
+
--background: #000000;
|
|
142
|
+
--card: #0a0a0a;
|
|
143
|
+
--popover: #141414;
|
|
144
|
+
--secondary: #161616;
|
|
145
|
+
--muted: #111111;
|
|
146
|
+
--accent: #1b1b1b;
|
|
147
|
+
--muted-foreground: #848b88;
|
|
148
|
+
--border: rgba(255, 255, 255, 0.1);
|
|
149
|
+
--input: rgba(255, 255, 255, 0.1);
|
|
150
|
+
--sidebar: var(--brand-sidebar, #0a0a0a);
|
|
151
|
+
--sidebar-accent: color-mix(in oklab, var(--primary) 16%, transparent);
|
|
152
|
+
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* Ground: Charcoal (dark) */
|
|
156
|
+
[data-mode="dark"][data-ground="charcoal"] {
|
|
157
|
+
--background: #0e0f10;
|
|
158
|
+
--card: #161819;
|
|
159
|
+
--popover: #1e2022;
|
|
160
|
+
--secondary: #202325;
|
|
161
|
+
--muted: #191b1c;
|
|
162
|
+
--accent: #232628;
|
|
163
|
+
--muted-foreground: #8b9194;
|
|
164
|
+
--border: rgba(255, 255, 255, 0.1);
|
|
165
|
+
--input: rgba(255, 255, 255, 0.1);
|
|
166
|
+
--sidebar: var(--brand-sidebar, #161819);
|
|
167
|
+
--sidebar-accent: color-mix(in oklab, var(--primary) 16%, transparent);
|
|
168
|
+
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* Ground: Graphite (dark) */
|
|
172
|
+
[data-mode="dark"][data-ground="graphite"] {
|
|
173
|
+
--background: #111214;
|
|
174
|
+
--card: #18191b;
|
|
175
|
+
--popover: #202124;
|
|
176
|
+
--secondary: #222326;
|
|
177
|
+
--muted: #1b1c1e;
|
|
178
|
+
--accent: #26272a;
|
|
179
|
+
--muted-foreground: #8e9296;
|
|
180
|
+
--border: rgba(255, 255, 255, 0.09);
|
|
181
|
+
--input: rgba(255, 255, 255, 0.1);
|
|
182
|
+
--sidebar: #161719;
|
|
183
|
+
--sidebar-accent: color-mix(in oklab, var(--primary) 14%, transparent);
|
|
184
|
+
--sidebar-border: rgba(255, 255, 255, 0.07);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Ground: Emerald (dark) */
|
|
188
|
+
[data-mode="dark"][data-ground="emerald"] {
|
|
189
|
+
--background: #050b08;
|
|
190
|
+
--card: #0a1310;
|
|
191
|
+
--popover: #0d1814;
|
|
192
|
+
--secondary: #121d18;
|
|
193
|
+
--muted: #101a15;
|
|
194
|
+
--accent: #16261f;
|
|
195
|
+
--muted-foreground: #8ba199;
|
|
196
|
+
--border: rgba(255, 255, 255, 0.1);
|
|
197
|
+
--input: rgba(255, 255, 255, 0.1);
|
|
198
|
+
--sidebar: var(--brand-sidebar, #0a1310);
|
|
199
|
+
--sidebar-accent: color-mix(in oklab, var(--primary) 16%, transparent);
|
|
200
|
+
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Ground: Paper (light) */
|
|
204
|
+
[data-mode="light"][data-ground="paper"] {
|
|
205
|
+
--background: #faf6ee;
|
|
206
|
+
--card: #fffdf9;
|
|
207
|
+
--popover: #fffdf9;
|
|
208
|
+
--secondary: #f3eee4;
|
|
209
|
+
--muted: #f3eee4;
|
|
210
|
+
--accent: #f0e6da;
|
|
211
|
+
--muted-foreground: #5c5b58;
|
|
212
|
+
--border: #dedad2;
|
|
213
|
+
--input: #dedad2;
|
|
214
|
+
--sidebar: #f5f0e4;
|
|
215
|
+
--sidebar-accent: #f0e6da;
|
|
216
|
+
--sidebar-border: #dedad2;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Ground: Mist (light) */
|
|
220
|
+
[data-mode="light"][data-ground="mist"] {
|
|
221
|
+
--background: #f2f7f9;
|
|
222
|
+
--card: #ffffff;
|
|
223
|
+
--popover: #ffffff;
|
|
224
|
+
--secondary: #e9eff2;
|
|
225
|
+
--muted: #e9eff2;
|
|
226
|
+
--accent: #dfeaf0;
|
|
227
|
+
--muted-foreground: #5c5b58;
|
|
228
|
+
--border: #d6dcdf;
|
|
229
|
+
--input: #d6dcdf;
|
|
230
|
+
--sidebar: #e9f1f5;
|
|
231
|
+
--sidebar-accent: #dfeaf0;
|
|
232
|
+
--sidebar-border: #d6dcdf;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* Accent: Green (dark) */
|
|
236
|
+
[data-mode="dark"][data-accent="green"] {
|
|
237
|
+
--brand-primary: #2f9e52;
|
|
238
|
+
--brand-on-primary: #04241b;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* Accent: Green (light) */
|
|
242
|
+
[data-mode="light"][data-accent="green"] {
|
|
243
|
+
--brand-primary: #26803f;
|
|
244
|
+
--brand-on-primary: #f3fbf7;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* Accent: Copper (dark) */
|
|
248
|
+
[data-mode="dark"][data-accent="copper"] {
|
|
249
|
+
--brand-primary: #c97b4a;
|
|
250
|
+
--brand-on-primary: #140a04;
|
|
251
|
+
--chart-3: #d9c7a3;
|
|
252
|
+
--chart-5: #2f9e52;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Accent: Copper (light) */
|
|
256
|
+
[data-mode="light"][data-accent="copper"] {
|
|
257
|
+
--brand-primary: #9c5730;
|
|
258
|
+
--brand-on-primary: #f3fbf7;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* Accent: Ice (dark) */
|
|
262
|
+
[data-mode="dark"][data-accent="ice"] {
|
|
263
|
+
--brand-primary: #6e9fc4;
|
|
264
|
+
--brand-on-primary: #06131c;
|
|
265
|
+
--chart-3: #b9d4e4;
|
|
266
|
+
--chart-5: #2f9e52;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* Accent: Ice (light) */
|
|
270
|
+
[data-mode="light"][data-accent="ice"] {
|
|
271
|
+
--brand-primary: #3d6f95;
|
|
272
|
+
--brand-on-primary: #f3fbf7;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Accent: Periwinkle (dark) */
|
|
276
|
+
[data-mode="dark"][data-accent="periwinkle"] {
|
|
277
|
+
--brand-primary: #8b8fd9;
|
|
278
|
+
--brand-on-primary: #0b0c1a;
|
|
279
|
+
--chart-3: #c3c5ec;
|
|
280
|
+
--chart-5: #2f9e52;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* Accent: Periwinkle (light) */
|
|
284
|
+
[data-mode="light"][data-accent="periwinkle"] {
|
|
285
|
+
--brand-primary: #5a5fae;
|
|
286
|
+
--brand-on-primary: #f3fbf7;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* Accent: Amber (dark) */
|
|
290
|
+
[data-mode="dark"][data-accent="amber"] {
|
|
291
|
+
--brand-primary: #e0b34a;
|
|
292
|
+
--brand-on-primary: #1a1204;
|
|
293
|
+
--chart-4: #c97b4a;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Accent: Amber (light) */
|
|
297
|
+
[data-mode="light"][data-accent="amber"] {
|
|
298
|
+
--brand-primary: #8a6410;
|
|
299
|
+
--brand-on-primary: #f3fbf7;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* Density: Compact, dense rows with hairline dividers */
|
|
303
|
+
:root,
|
|
304
|
+
[data-density="compact"] {
|
|
305
|
+
--row-pad-y: 0.5rem;
|
|
306
|
+
--row-pad-x: 0.75rem;
|
|
307
|
+
--stack-gap: 0.75rem;
|
|
308
|
+
--panel-pad: 1rem;
|
|
309
|
+
--list-columns: minmax(0, 1fr);
|
|
310
|
+
--list-gap: 0px;
|
|
311
|
+
--list-frame-border: 1px;
|
|
312
|
+
--list-frame-radius: var(--radius);
|
|
313
|
+
--list-item-border: 0px;
|
|
314
|
+
--list-item-radius: 0px;
|
|
315
|
+
--list-item-bg: transparent;
|
|
316
|
+
--list-item-pad: var(--row-pad-y) var(--row-pad-x);
|
|
317
|
+
--list-item-direction: row;
|
|
318
|
+
--list-item-align: center;
|
|
319
|
+
--list-item-trail: auto;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* Density: Cards, boxed items that flow into columns */
|
|
323
|
+
[data-density="cards"] {
|
|
324
|
+
--row-pad-y: 0.9rem;
|
|
325
|
+
--row-pad-x: 1rem;
|
|
326
|
+
--stack-gap: 1rem;
|
|
327
|
+
--panel-pad: 1.25rem;
|
|
328
|
+
--list-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
|
|
329
|
+
--list-gap: var(--stack-gap);
|
|
330
|
+
--list-frame-border: 0px;
|
|
331
|
+
--list-frame-radius: 0px;
|
|
332
|
+
--list-item-border: 1px;
|
|
333
|
+
--list-item-radius: var(--radius);
|
|
334
|
+
--list-item-bg: var(--card);
|
|
335
|
+
--list-item-pad: var(--panel-pad);
|
|
336
|
+
--list-item-direction: column;
|
|
337
|
+
--list-item-align: stretch;
|
|
338
|
+
--list-item-trail: 0px;
|
|
339
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Relative luminance of a `#rrggbb` colour (WCAG 2.2). */
|
|
2
|
+
export declare function luminance(hex: string): number;
|
|
3
|
+
/** The WCAG contrast ratio between two `#rrggbb` colours, 1 to 21. */
|
|
4
|
+
export declare function contrastRatio(a: string, b: string): number;
|
|
5
|
+
/** WCAG 2.2 §1.4.11: a control or a status mark stands 3:1 off what it sits on. */
|
|
6
|
+
export declare const MIN_NON_TEXT_CONTRAST = 3;
|
|
7
|
+
/** WCAG 2.2 §1.4.3: body text stands 4.5:1 off its background. */
|
|
8
|
+
export declare const MIN_TEXT_CONTRAST = 4.5;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Relative luminance of a `#rrggbb` colour (WCAG 2.2). */
|
|
2
|
+
export function luminance(hex) {
|
|
3
|
+
const n = parseInt(hex.slice(1), 16);
|
|
4
|
+
const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255].map((c) => {
|
|
5
|
+
const s = c / 255;
|
|
6
|
+
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
7
|
+
});
|
|
8
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
9
|
+
}
|
|
10
|
+
/** The WCAG contrast ratio between two `#rrggbb` colours, 1 to 21. */
|
|
11
|
+
export function contrastRatio(a, b) {
|
|
12
|
+
const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
|
|
13
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
14
|
+
}
|
|
15
|
+
/** WCAG 2.2 §1.4.11: a control or a status mark stands 3:1 off what it sits on. */
|
|
16
|
+
export const MIN_NON_TEXT_CONTRAST = 3;
|
|
17
|
+
/** WCAG 2.2 §1.4.3: body text stands 4.5:1 off its background. */
|
|
18
|
+
export const MIN_TEXT_CONTRAST = 4.5;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Mode } from "./tokens.js";
|
|
2
|
+
type Declarations = readonly (readonly [property: string, value: string])[];
|
|
3
|
+
/** The accent tokens one accent paints in one mode. */
|
|
4
|
+
export declare function accentDeclarations(mode: Mode, accent: {
|
|
5
|
+
dark: string;
|
|
6
|
+
onDark: string;
|
|
7
|
+
light: string;
|
|
8
|
+
onLight: string;
|
|
9
|
+
}, chart?: Partial<Record<string, string>>): Declarations;
|
|
10
|
+
/** The whole of theme.css. */
|
|
11
|
+
export declare function renderThemeCss(): string;
|
|
12
|
+
/** The brand tokens a product adds on top of theme.css: its sidebar, and its own accent when it has one. */
|
|
13
|
+
export declare function renderProductCss(product: {
|
|
14
|
+
sidebar: string;
|
|
15
|
+
brandAccent?: {
|
|
16
|
+
key: string;
|
|
17
|
+
dark: string;
|
|
18
|
+
onDark: string;
|
|
19
|
+
light: string;
|
|
20
|
+
onLight: string;
|
|
21
|
+
} | null;
|
|
22
|
+
}): string;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { ACCENTS, ACCENT_KEYS, BASE_SURFACES, DEFAULT_ACCENT, DENSITIES, GROUNDS, GROUND_KEYS, MODE_TOKENS, STATIC_TOKENS, SURFACES, } from "./tokens.js";
|
|
2
|
+
const STATUS_NAMES = ["success", "warning", "info", "danger"];
|
|
3
|
+
function block(selector, declarations, comment) {
|
|
4
|
+
const head = comment ? `/* ${comment} */\n` : "";
|
|
5
|
+
const body = declarations.map(([property, value]) => ` ${property}: ${value};`).join("\n");
|
|
6
|
+
return `${head}${selector} {\n${body}\n}`;
|
|
7
|
+
}
|
|
8
|
+
function surfaceDeclarations(s) {
|
|
9
|
+
return [
|
|
10
|
+
["--background", s.background],
|
|
11
|
+
["--card", s.card],
|
|
12
|
+
["--popover", s.popover],
|
|
13
|
+
["--secondary", s.secondary],
|
|
14
|
+
["--muted", s.muted],
|
|
15
|
+
["--accent", s.accent],
|
|
16
|
+
["--muted-foreground", s.mutedForeground],
|
|
17
|
+
["--border", s.border],
|
|
18
|
+
["--input", s.input],
|
|
19
|
+
["--sidebar", s.sidebar === "brand" ? `var(--brand-sidebar, ${s.card})` : s.sidebar],
|
|
20
|
+
["--sidebar-accent", s.sidebarAccent],
|
|
21
|
+
["--sidebar-border", s.sidebarBorder],
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
function modeDeclarations(mode) {
|
|
25
|
+
const m = MODE_TOKENS[mode];
|
|
26
|
+
const accent = ACCENTS[DEFAULT_ACCENT];
|
|
27
|
+
return [
|
|
28
|
+
["color-scheme", mode],
|
|
29
|
+
...surfaceDeclarations(BASE_SURFACES[mode]),
|
|
30
|
+
["--foreground", m.foreground],
|
|
31
|
+
["--card-foreground", m.foreground],
|
|
32
|
+
["--popover-foreground", m.foreground],
|
|
33
|
+
["--secondary-foreground", m.foreground],
|
|
34
|
+
["--accent-foreground", m.foreground],
|
|
35
|
+
["--brand-primary", mode === "dark" ? accent.dark : accent.light],
|
|
36
|
+
["--brand-on-primary", mode === "dark" ? accent.onDark : accent.onLight],
|
|
37
|
+
["--primary", "var(--brand-primary)"],
|
|
38
|
+
["--primary-foreground", "var(--brand-on-primary)"],
|
|
39
|
+
["--destructive", m.destructive],
|
|
40
|
+
["--ring", "var(--brand-primary)"],
|
|
41
|
+
["--chart-1", "var(--brand-primary)"],
|
|
42
|
+
...Object.entries(m.chart).map(([key, value]) => [`--${key}`, value]),
|
|
43
|
+
["--sidebar-foreground", m.sidebarForeground],
|
|
44
|
+
["--sidebar-primary", "var(--brand-primary)"],
|
|
45
|
+
["--sidebar-primary-foreground", "var(--brand-on-primary)"],
|
|
46
|
+
["--sidebar-accent-foreground", m.foreground],
|
|
47
|
+
["--sidebar-ring", "var(--brand-primary)"],
|
|
48
|
+
["--surface-0", "var(--background)"],
|
|
49
|
+
["--surface-1", "var(--card)"],
|
|
50
|
+
["--surface-2", "var(--popover)"],
|
|
51
|
+
...STATUS_NAMES.flatMap((name) => {
|
|
52
|
+
const s = m.status[name];
|
|
53
|
+
return [
|
|
54
|
+
[`--${name}`, s.base],
|
|
55
|
+
[`--${name}-fg`, s.fg],
|
|
56
|
+
[`--${name}-bg`, s.bg],
|
|
57
|
+
[`--${name}-border`, s.border],
|
|
58
|
+
];
|
|
59
|
+
}),
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
/** The accent tokens one accent paints in one mode. */
|
|
63
|
+
export function accentDeclarations(mode, accent, chart) {
|
|
64
|
+
return [
|
|
65
|
+
["--brand-primary", mode === "dark" ? accent.dark : accent.light],
|
|
66
|
+
["--brand-on-primary", mode === "dark" ? accent.onDark : accent.onLight],
|
|
67
|
+
...Object.entries(chart ?? {}).map(([key, value]) => [`--${key}`, value ?? ""]),
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
const HEADER = `/*
|
|
71
|
+
* @calliopelabs/ui theme tokens.
|
|
72
|
+
* Generated by \`npm run theme\` from src/theme/tokens.ts. Do not edit by hand.
|
|
73
|
+
*
|
|
74
|
+
* The appearance axes ride on attributes of <html>, set before first paint by
|
|
75
|
+
* AppearanceScript and kept in step by AppearanceProvider:
|
|
76
|
+
* data-mode dark | light (follows the ground)
|
|
77
|
+
* data-ground the surface ramp
|
|
78
|
+
* data-accent a family accent, the product's brand accent, or custom
|
|
79
|
+
* data-density compact | cards (any element may carry its own)
|
|
80
|
+
* --radius the corners setting, inline
|
|
81
|
+
* Semantic status tokens are defined per mode only: no ground and no accent
|
|
82
|
+
* may change what ok, warn, info or error look like.
|
|
83
|
+
*/`;
|
|
84
|
+
/** The whole of theme.css. */
|
|
85
|
+
export function renderThemeCss() {
|
|
86
|
+
const blocks = [HEADER];
|
|
87
|
+
blocks.push(block(":root", Object.entries(STATIC_TOKENS).map(([key, value]) => [`--${key}`, value]), "Mode-independent tokens"));
|
|
88
|
+
blocks.push(block(':root,\n[data-mode="dark"]', modeDeclarations("dark"), "Dark: black ground, green accent"));
|
|
89
|
+
blocks.push(block('[data-mode="light"]', modeDeclarations("light"), "Light: neutral ground, green accent"));
|
|
90
|
+
for (const ground of GROUND_KEYS) {
|
|
91
|
+
const { mode, label } = GROUNDS[ground];
|
|
92
|
+
blocks.push(block(`[data-mode="${mode}"][data-ground="${ground}"]`, surfaceDeclarations(SURFACES[ground]), `Ground: ${label} (${mode})`));
|
|
93
|
+
}
|
|
94
|
+
for (const key of ACCENT_KEYS) {
|
|
95
|
+
const accent = ACCENTS[key];
|
|
96
|
+
const chart = "chart" in accent ? accent.chart : {};
|
|
97
|
+
for (const mode of ["dark", "light"]) {
|
|
98
|
+
blocks.push(block(`[data-mode="${mode}"][data-accent="${key}"]`, accentDeclarations(mode, accent, chart[mode]), `Accent: ${accent.label} (${mode})`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
blocks.push(block(':root,\n[data-density="compact"]', Object.entries(DENSITIES.compact.tokens).map(([key, value]) => [`--${key}`, value]), `Density: ${DENSITIES.compact.label}, ${DENSITIES.compact.hint.toLowerCase()}`));
|
|
102
|
+
blocks.push(block('[data-density="cards"]', Object.entries(DENSITIES.cards.tokens).map(([key, value]) => [`--${key}`, value]), `Density: ${DENSITIES.cards.label}, ${DENSITIES.cards.hint.toLowerCase()}`));
|
|
103
|
+
return `${blocks.join("\n\n")}\n`;
|
|
104
|
+
}
|
|
105
|
+
/** The brand tokens a product adds on top of theme.css: its sidebar, and its own accent when it has one. */
|
|
106
|
+
export function renderProductCss(product) {
|
|
107
|
+
const inline = (selector, declarations) => `${selector}{${declarations.map(([property, value]) => `${property}:${value}`).join(";")}}`;
|
|
108
|
+
const rules = [inline(":root", [["--brand-sidebar", product.sidebar]])];
|
|
109
|
+
const accent = product.brandAccent;
|
|
110
|
+
if (accent) {
|
|
111
|
+
for (const mode of ["dark", "light"]) {
|
|
112
|
+
rules.push(inline(`[data-mode="${mode}"][data-accent="${accent.key}"]`, accentDeclarations(mode, accent)));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return rules.join("");
|
|
116
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Calliope AI operator palette: one table, and the source of
|
|
3
|
+
* `src/styles/theme.css` (`npm run theme`). Every value here is either a
|
|
4
|
+
* ground (the surface ramp), a family accent (action colour and the lead
|
|
5
|
+
* chart series), a semantic status, or a density step.
|
|
6
|
+
*
|
|
7
|
+
* Values come from the Astrolift frontend (`app/globals.css`, origin/main
|
|
8
|
+
* 8edb40d) and the operator-UI amendment of 2026-08-21: grounds are black
|
|
9
|
+
* and neutral, the sidebar is the one brand surface, corners are 2px, a
|
|
10
|
+
* second hue is allowed as accent, and semantic status never moves with the
|
|
11
|
+
* accent or the ground's tint. Astrolift's light values were oklch; they are
|
|
12
|
+
* stored here as the sRGB they render to, so the contrast tests can read
|
|
13
|
+
* them. One value changed on the way: light `warning` went from
|
|
14
|
+
* oklch(0.66 0.16 70) to oklch(0.65 0.16 70) so a warning dot clears 3:1 on
|
|
15
|
+
* paper and mist.
|
|
16
|
+
*/
|
|
17
|
+
export type Mode = "dark" | "light";
|
|
18
|
+
export declare const GROUNDS: {
|
|
19
|
+
readonly black: {
|
|
20
|
+
readonly label: "Black";
|
|
21
|
+
readonly mode: "dark";
|
|
22
|
+
};
|
|
23
|
+
readonly charcoal: {
|
|
24
|
+
readonly label: "Charcoal";
|
|
25
|
+
readonly mode: "dark";
|
|
26
|
+
};
|
|
27
|
+
readonly graphite: {
|
|
28
|
+
readonly label: "Graphite";
|
|
29
|
+
readonly mode: "dark";
|
|
30
|
+
};
|
|
31
|
+
readonly emerald: {
|
|
32
|
+
readonly label: "Emerald";
|
|
33
|
+
readonly mode: "dark";
|
|
34
|
+
};
|
|
35
|
+
readonly paper: {
|
|
36
|
+
readonly label: "Paper";
|
|
37
|
+
readonly mode: "light";
|
|
38
|
+
};
|
|
39
|
+
readonly mist: {
|
|
40
|
+
readonly label: "Mist";
|
|
41
|
+
readonly mode: "light";
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export type Ground = keyof typeof GROUNDS;
|
|
45
|
+
export declare const GROUND_KEYS: Ground[];
|
|
46
|
+
/**
|
|
47
|
+
* The surfaces a ground paints. `sidebar: "brand"` means the product's brand
|
|
48
|
+
* surface (`--brand-sidebar`, set by the product), falling back to the
|
|
49
|
+
* ground's own card colour when no product is set. Graphite and the light
|
|
50
|
+
* grounds keep a neutral sidebar whatever the product, so the brand colour
|
|
51
|
+
* is spent on the accent there (Astrolift #2286).
|
|
52
|
+
*/
|
|
53
|
+
export interface Surfaces {
|
|
54
|
+
background: string;
|
|
55
|
+
card: string;
|
|
56
|
+
popover: string;
|
|
57
|
+
secondary: string;
|
|
58
|
+
muted: string;
|
|
59
|
+
/** shadcn's name for the hover and selected surface, not the brand accent. */
|
|
60
|
+
accent: string;
|
|
61
|
+
mutedForeground: string;
|
|
62
|
+
border: string;
|
|
63
|
+
input: string;
|
|
64
|
+
sidebar: "brand" | string;
|
|
65
|
+
sidebarAccent: string;
|
|
66
|
+
sidebarBorder: string;
|
|
67
|
+
}
|
|
68
|
+
export declare const SURFACES: Record<Ground, Surfaces>;
|
|
69
|
+
/**
|
|
70
|
+
* What a mode paints before a ground refines it: dark starts on black, light
|
|
71
|
+
* on a neutral white. Every shipped ground overrides these surfaces, so the
|
|
72
|
+
* light set only shows when no ground is set.
|
|
73
|
+
*/
|
|
74
|
+
export declare const BASE_SURFACES: Record<Mode, Surfaces>;
|
|
75
|
+
export type StatusName = "success" | "warning" | "info" | "danger";
|
|
76
|
+
/**
|
|
77
|
+
* A status paints four tokens: the solid base (dots, icons, chart series),
|
|
78
|
+
* `fg` (text and icons on the status tint), `bg` (a subtle callout fill) and
|
|
79
|
+
* `border` (the tinted hairline). Defined per mode only: no ground and no
|
|
80
|
+
* accent may redefine them. Info is green; the family permits no blue.
|
|
81
|
+
*/
|
|
82
|
+
export interface StatusTokens {
|
|
83
|
+
base: string;
|
|
84
|
+
fg: string;
|
|
85
|
+
bg: string;
|
|
86
|
+
border: string;
|
|
87
|
+
}
|
|
88
|
+
export interface ModeTokens {
|
|
89
|
+
foreground: string;
|
|
90
|
+
destructive: string;
|
|
91
|
+
/** chart-1 is always the accent; these are series two to five. */
|
|
92
|
+
chart: {
|
|
93
|
+
"chart-2": string;
|
|
94
|
+
"chart-3": string;
|
|
95
|
+
"chart-4": string;
|
|
96
|
+
"chart-5": string;
|
|
97
|
+
};
|
|
98
|
+
sidebarForeground: string;
|
|
99
|
+
status: Record<StatusName, StatusTokens>;
|
|
100
|
+
}
|
|
101
|
+
export declare const MODE_TOKENS: Record<Mode, ModeTokens>;
|
|
102
|
+
/**
|
|
103
|
+
* The family accents: the action colour, focus ring and lead chart series.
|
|
104
|
+
* `dark` and `light` are the accent on each mode's grounds; `onDark` and
|
|
105
|
+
* `onLight` are the ink on a filled accent. A product may add its own brand
|
|
106
|
+
* accent beside these (Zentinelle teal); none of them reaches status.
|
|
107
|
+
*/
|
|
108
|
+
export interface AccentSpec {
|
|
109
|
+
label: string;
|
|
110
|
+
dark: string;
|
|
111
|
+
onDark: string;
|
|
112
|
+
light: string;
|
|
113
|
+
onLight: string;
|
|
114
|
+
/** Chart series moved so they do not collide with this accent. */
|
|
115
|
+
chart?: Partial<Record<Mode, Partial<ModeTokens["chart"]>>>;
|
|
116
|
+
}
|
|
117
|
+
export declare const ACCENTS: {
|
|
118
|
+
readonly green: {
|
|
119
|
+
readonly label: "Green";
|
|
120
|
+
readonly dark: "#2f9e52";
|
|
121
|
+
readonly onDark: "#04241b";
|
|
122
|
+
readonly light: "#26803f";
|
|
123
|
+
readonly onLight: "#f3fbf7";
|
|
124
|
+
};
|
|
125
|
+
readonly copper: {
|
|
126
|
+
readonly label: "Copper";
|
|
127
|
+
readonly dark: "#c97b4a";
|
|
128
|
+
readonly onDark: "#140a04";
|
|
129
|
+
readonly light: "#9c5730";
|
|
130
|
+
readonly onLight: "#f3fbf7";
|
|
131
|
+
readonly chart: {
|
|
132
|
+
readonly dark: {
|
|
133
|
+
readonly "chart-3": "#d9c7a3";
|
|
134
|
+
readonly "chart-5": "#2f9e52";
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
readonly ice: {
|
|
139
|
+
readonly label: "Ice";
|
|
140
|
+
readonly dark: "#6e9fc4";
|
|
141
|
+
readonly onDark: "#06131c";
|
|
142
|
+
readonly light: "#3d6f95";
|
|
143
|
+
readonly onLight: "#f3fbf7";
|
|
144
|
+
readonly chart: {
|
|
145
|
+
readonly dark: {
|
|
146
|
+
readonly "chart-3": "#b9d4e4";
|
|
147
|
+
readonly "chart-5": "#2f9e52";
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
readonly periwinkle: {
|
|
152
|
+
readonly label: "Periwinkle";
|
|
153
|
+
readonly dark: "#8b8fd9";
|
|
154
|
+
readonly onDark: "#0b0c1a";
|
|
155
|
+
readonly light: "#5a5fae";
|
|
156
|
+
readonly onLight: "#f3fbf7";
|
|
157
|
+
readonly chart: {
|
|
158
|
+
readonly dark: {
|
|
159
|
+
readonly "chart-3": "#c3c5ec";
|
|
160
|
+
readonly "chart-5": "#2f9e52";
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
readonly amber: {
|
|
165
|
+
readonly label: "Amber";
|
|
166
|
+
readonly dark: "#e0b34a";
|
|
167
|
+
readonly onDark: "#1a1204";
|
|
168
|
+
readonly light: "#8a6410";
|
|
169
|
+
readonly onLight: "#f3fbf7";
|
|
170
|
+
readonly chart: {
|
|
171
|
+
readonly dark: {
|
|
172
|
+
readonly "chart-4": "#c97b4a";
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
export type FamilyAccent = keyof typeof ACCENTS;
|
|
178
|
+
export declare const ACCENT_KEYS: FamilyAccent[];
|
|
179
|
+
/** The accent a mode paints when nothing chooses one. */
|
|
180
|
+
export declare const DEFAULT_ACCENT: FamilyAccent;
|
|
181
|
+
export type Density = "compact" | "cards";
|
|
182
|
+
/**
|
|
183
|
+
* Density is a layout axis, not padding (operator-UI amendment): `compact`
|
|
184
|
+
* is one panel of hairline rows, `cards` boxes items and flows them into
|
|
185
|
+
* responsive columns. Components read these variables, and a subtree can
|
|
186
|
+
* carry its own `data-density`, so the nearest one wins.
|
|
187
|
+
*/
|
|
188
|
+
export declare const DENSITIES: Record<Density, {
|
|
189
|
+
label: string;
|
|
190
|
+
hint: string;
|
|
191
|
+
tokens: Record<string, string>;
|
|
192
|
+
}>;
|
|
193
|
+
export declare const DENSITY_KEYS: Density[];
|
|
194
|
+
/** Mode-independent tokens. `--radius` is the corners setting (2px shipped). */
|
|
195
|
+
export declare const STATIC_TOKENS: Record<string, string>;
|