@siyahi/ui 0.1.9-next.35 → 0.1.9-next.37
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/DESIGN.md +80 -63
- package/README.md +10 -21
- package/THIRD_PARTY_NOTICES.md +15 -15
- package/agents/examples/chart-panels.tsx +0 -4
- package/agents/examples/metric-strip-headline.tsx +12 -0
- package/agents/examples/{section-cards-select.tsx → metric-strip-select.tsx} +5 -5
- package/agents/examples/metric-strip-trend.tsx +33 -0
- package/agents/examples/overview-dashboard.tsx +4 -4
- package/agents/examples/scatter-chart.tsx +0 -1
- package/agents/examples/stale-blocks.tsx +9 -9
- package/agents/examples/surfaces.tsx +1 -33
- package/agents/manifest.json +38 -53
- package/dist/components/registry/dashboardcn/kpi-row-card.d.ts +20 -6
- package/dist/components/registry/dashboardcn/kpi-row-card.js +52 -33
- package/dist/components/siyahi/callout.js +2 -1
- package/dist/components/siyahi/inline-code.d.ts +1 -1
- package/dist/components/siyahi/inline-code.js +1 -1
- package/dist/components/siyahi/page-header.js +1 -0
- package/dist/components/siyahi/panel-grid.d.ts +4 -6
- package/dist/components/siyahi/panel-grid.js +9 -12
- package/dist/components/siyahi/scatter-chart.d.ts +3 -3
- package/dist/components/siyahi/scatter-chart.js +6 -3
- package/dist/components/siyahi/siyahi-provider.d.ts +5 -15
- package/dist/components/siyahi/siyahi-provider.js +7 -17
- package/dist/index.d.ts +3 -4
- package/dist/index.js +3 -3
- package/dist/lib/chart-frame.d.ts +1 -1
- package/dist/lib/chart-frame.js +4 -2
- package/dist/lib/chart-series.d.ts +10 -2
- package/dist/lib/chart-series.js +10 -0
- package/dist/lib/fit-text.js +1 -1
- package/dist/lib/tone.d.ts +1 -1
- package/dist/lib/tone.js +1 -1
- package/package.json +2 -2
- package/registry.lock.json +0 -8
- package/styles/fallback-fonts.css +13 -13
- package/styles/fonts/m-plus-1-code-latin-ext-wght-normal.woff2 +0 -0
- package/styles/fonts/m-plus-1-code-latin-wght-normal.woff2 +0 -0
- package/styles/m-plus-1-code.css +21 -0
- package/styles/siyahi.css +166 -330
- package/agents/examples/section-cards-trend.tsx +0 -32
- package/agents/examples/section-cards.tsx +0 -48
- package/dist/components/registry/dashboard-01/section-cards.d.ts +0 -30
- package/dist/components/registry/dashboard-01/section-cards.js +0 -74
package/styles/siyahi.css
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Siyahi theme: the only file in the package that holds colour values.
|
|
3
|
-
* shadcn/ui `neutral` greys for both schemes,
|
|
4
|
-
* placed, not spread: links, the
|
|
5
|
-
*
|
|
6
|
-
* black and white read strongest. Plus three meaning
|
|
3
|
+
* A terminal: the mono face, shadcn/ui `neutral` greys for both schemes, and
|
|
4
|
+
* terminal amber placed, not spread: links, the eyebrow and panel numbers and
|
|
5
|
+
* the selected sidebar item's text. Buttons, badges, toggles and focus stay
|
|
6
|
+
* monochrome, where black and white read strongest. Plus three meaning
|
|
7
7
|
* colours and a five-hue chart palette. `packages/tools/contrast.mjs` checks every value
|
|
8
8
|
* here in the build; when a check fails, change the value, never the bar.
|
|
9
9
|
*/
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
@import "tw-animate-css";
|
|
12
12
|
@import "./shadcn.css";
|
|
13
13
|
@import "@fontsource-variable/geist";
|
|
14
|
-
@import "
|
|
14
|
+
@import "./m-plus-1-code.css";
|
|
15
15
|
@import "./fallback-fonts.css";
|
|
16
16
|
|
|
17
17
|
/* Tailwind does not scan node_modules, so name the package's own classes. */
|
|
@@ -41,8 +41,13 @@
|
|
|
41
41
|
|
|
42
42
|
@theme inline {
|
|
43
43
|
--font-sans: "Geist Variable", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
/*
|
|
45
|
+
* M PLUS 1 Code: a mono half a font size wide (6px a character at 12px, Geist Mono's 7), so a terminal page fits
|
|
46
|
+
* a table's every column and a row's words on one line. 20KB for every weight.
|
|
47
|
+
*/
|
|
48
|
+
--font-mono: "M PLUS 1 Code Variable", "M PLUS 1 Code Fallback", ui-monospace, monospace;
|
|
49
|
+
/* Titles are data labels on a terminal: the mono face, as the page itself is. */
|
|
50
|
+
--font-heading: var(--font-mono);
|
|
46
51
|
/*
|
|
47
52
|
* Tailwind's 65ch, measured in Geist, whose "0" is 0.663em wide. A ch
|
|
48
53
|
* follows whichever font is drawing, so a plain 65ch is narrower while the
|
|
@@ -109,7 +114,8 @@
|
|
|
109
114
|
--radius: 0rem;
|
|
110
115
|
--background: oklch(0.985 0 0);
|
|
111
116
|
--foreground: oklch(0.145 0 0);
|
|
112
|
-
|
|
117
|
+
/* The page colour: a card is a block on the page, not a box, so what lies under its marks is the page. */
|
|
118
|
+
--card: oklch(0.985 0 0);
|
|
113
119
|
--card-foreground: oklch(0.145 0 0);
|
|
114
120
|
--popover: oklch(1 0 0);
|
|
115
121
|
--popover-foreground: oklch(0.145 0 0);
|
|
@@ -130,12 +136,13 @@
|
|
|
130
136
|
--input: oklch(0.64 0 0);
|
|
131
137
|
/* Focus is the text colour, as black reads strongest: a field's edge thickens in it, any other control takes a halo. */
|
|
132
138
|
--ring: oklch(0.145 0 0);
|
|
133
|
-
/* Tailwind green-700
|
|
139
|
+
/* Tailwind green-700 and red-600, with their chroma lowered to the sRGB edge, and a mustard yellow. */
|
|
134
140
|
--positive: oklch(0.527 0.145 150.069);
|
|
135
141
|
--positive-foreground: oklch(0.985 0 0);
|
|
136
142
|
--negative: oklch(0.577 0.235 27.325);
|
|
137
143
|
--negative-foreground: oklch(0.985 0 0);
|
|
138
|
-
|
|
144
|
+
/* Yellow, not amber: amber is the accent, so caution takes its own hue. */
|
|
145
|
+
--warning: oklch(0.53 0.1 95);
|
|
139
146
|
--warning-foreground: oklch(0.985 0 0);
|
|
140
147
|
/*
|
|
141
148
|
* Heat steps: how much of --positive or --negative each of five steps mixes
|
|
@@ -161,7 +168,16 @@
|
|
|
161
168
|
* contrast check holds each bar colour's tint against the card and each slot's against the next slot's.
|
|
162
169
|
*/
|
|
163
170
|
--bar-tint: 50%;
|
|
164
|
-
|
|
171
|
+
/* Terminal amber, deepened to burnt orange so it reads as text on the light page. */
|
|
172
|
+
--link: oklch(0.55 0.13 58);
|
|
173
|
+
/*
|
|
174
|
+
* A headline MetricStrip's blocks (variant "blocks"): the orange, then the text colour, then a grey, each with the
|
|
175
|
+
* ink its figures take on it.
|
|
176
|
+
*/
|
|
177
|
+
--headline: oklch(0.68 0.19 40);
|
|
178
|
+
--headline-foreground: oklch(0.145 0 0);
|
|
179
|
+
--headline-muted: oklch(0.88 0 0);
|
|
180
|
+
--headline-muted-foreground: oklch(0.145 0 0);
|
|
165
181
|
/*
|
|
166
182
|
* Indigo, amber, plum, sky, teal: no green or red, so no series reads as a
|
|
167
183
|
* gain or a loss. Each slot reaches 3:1 against the canvas, the card and the
|
|
@@ -175,30 +191,30 @@
|
|
|
175
191
|
--chart-1: oklch(0.4 0.17 275);
|
|
176
192
|
--chart-2: oklch(0.66 0.13 70);
|
|
177
193
|
--chart-3: oklch(0.38 0.13 335);
|
|
178
|
-
--chart-4: oklch(0.
|
|
194
|
+
--chart-4: oklch(0.64 0.11 235);
|
|
179
195
|
--chart-5: oklch(0.36 0.06 205);
|
|
180
196
|
--sidebar: oklch(0.985 0 0);
|
|
181
197
|
--sidebar-foreground: oklch(0.145 0 0);
|
|
182
198
|
--sidebar-primary: oklch(0.205 0 0);
|
|
183
199
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
184
200
|
--sidebar-accent: oklch(0.97 0 0);
|
|
185
|
-
--sidebar-accent-foreground: oklch(0.
|
|
201
|
+
--sidebar-accent-foreground: oklch(0.55 0.13 58);
|
|
186
202
|
--sidebar-border: oklch(0.145 0 0 / 18%);
|
|
187
203
|
--sidebar-ring: oklch(0.145 0 0);
|
|
188
204
|
|
|
189
205
|
@variant dark {
|
|
190
206
|
/*
|
|
191
207
|
* The canvas lifts just off black and text sits a step below white:
|
|
192
|
-
* near-white on near-black (19:1) blurs for many readers, and
|
|
193
|
-
*
|
|
208
|
+
* near-white on near-black (19:1) blurs for many readers, and on an open
|
|
209
|
+
* page, where cards have no fill, the canvas is what text sits on.
|
|
194
210
|
* Claude Code (#181818, about #e8e8e8 text) and T3 Code (#0a0a0a, about
|
|
195
211
|
* #bebebe) both narrow that gap; this sits between them, at 14.9:1.
|
|
196
|
-
*
|
|
197
|
-
*
|
|
212
|
+
* A popover sits one step above the canvas, so it reads as lifted over
|
|
213
|
+
* the page; the sidebar is a shade darker.
|
|
198
214
|
*/
|
|
199
215
|
--background: oklch(0.14 0 0);
|
|
200
216
|
--foreground: oklch(0.922 0 0);
|
|
201
|
-
--card: oklch(0.
|
|
217
|
+
--card: oklch(0.14 0 0);
|
|
202
218
|
--card-foreground: oklch(0.922 0 0);
|
|
203
219
|
--popover: oklch(0.215 0 0);
|
|
204
220
|
--popover-foreground: oklch(0.922 0 0);
|
|
@@ -218,14 +234,14 @@
|
|
|
218
234
|
--ring: oklch(0.922 0 0);
|
|
219
235
|
/*
|
|
220
236
|
* Green toned down from Tailwind green-400, below red-400's lightness so a
|
|
221
|
-
* treemap's deepest green tile keeps its light label at 4.5:1;
|
|
222
|
-
*
|
|
237
|
+
* treemap's deepest green tile keeps its light label at 4.5:1; a yellow
|
|
238
|
+
* for caution, apart from the amber link. Red sits at the sRGB edge.
|
|
223
239
|
*/
|
|
224
240
|
--positive: oklch(0.69 0.16 151.711);
|
|
225
241
|
--positive-foreground: oklch(0.145 0 0);
|
|
226
242
|
--negative: oklch(0.704 0.187 22.216);
|
|
227
243
|
--negative-foreground: oklch(0.145 0 0);
|
|
228
|
-
--warning: oklch(0.
|
|
244
|
+
--warning: oklch(0.8 0.15 98);
|
|
229
245
|
--warning-foreground: oklch(0.145 0 0);
|
|
230
246
|
/* A 12% tint of a dark card reads as the card itself, so dark starts at 25%. */
|
|
231
247
|
--heat-1: 25%;
|
|
@@ -242,7 +258,11 @@
|
|
|
242
258
|
--wash-edge: 55%;
|
|
243
259
|
--field-wash: 12%;
|
|
244
260
|
--bar-tint: 50%;
|
|
245
|
-
--link: oklch(0.
|
|
261
|
+
--link: oklch(0.8 0.13 65);
|
|
262
|
+
--headline: oklch(0.7 0.19 40);
|
|
263
|
+
--headline-foreground: oklch(0.145 0 0);
|
|
264
|
+
--headline-muted: oklch(0.27 0 0);
|
|
265
|
+
--headline-muted-foreground: oklch(0.922 0 0);
|
|
246
266
|
--chart-1: oklch(0.58 0.2 275);
|
|
247
267
|
--chart-2: oklch(0.88 0.08 75);
|
|
248
268
|
--chart-3: oklch(0.58 0.16 335);
|
|
@@ -253,7 +273,7 @@
|
|
|
253
273
|
--sidebar-primary: oklch(0.922 0 0);
|
|
254
274
|
--sidebar-primary-foreground: oklch(0.205 0 0);
|
|
255
275
|
--sidebar-accent: oklch(0.269 0 0);
|
|
256
|
-
--sidebar-accent-foreground: oklch(0.8 0.
|
|
276
|
+
--sidebar-accent-foreground: oklch(0.8 0.13 65);
|
|
257
277
|
--sidebar-border: oklch(1 0 0 / 20%);
|
|
258
278
|
--sidebar-ring: oklch(0.922 0 0);
|
|
259
279
|
}
|
|
@@ -267,7 +287,16 @@
|
|
|
267
287
|
@apply bg-background text-foreground;
|
|
268
288
|
font-variant-numeric: tabular-nums;
|
|
269
289
|
}
|
|
290
|
+
/*
|
|
291
|
+
* A terminal: the page is set in the mono face, so figures, labels, controls and titles share one grid. Text that
|
|
292
|
+
* reads as sentences (a paragraph, a quote, a card's or a dialog's description) takes the sans, where
|
|
293
|
+
* mono tires the eye over a line. Not a list item: a sidebar row, a menu option or a tile is one, and its contents
|
|
294
|
+
* are labels. Zero specificity, so a component's own font class wins.
|
|
295
|
+
*/
|
|
270
296
|
html {
|
|
297
|
+
@apply font-mono;
|
|
298
|
+
}
|
|
299
|
+
:where(p, blockquote, dd, figcaption, [data-slot$="description"]) {
|
|
271
300
|
@apply font-sans;
|
|
272
301
|
}
|
|
273
302
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -283,12 +312,9 @@
|
|
|
283
312
|
}
|
|
284
313
|
|
|
285
314
|
/*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
* selects the same way: stock fills it with the canvas colour, 1.05:1 against
|
|
290
|
-
* its grey list, lifted on a drop shadow, which the fill makes needless. Unlayered, to outrank the stock
|
|
291
|
-
* aria-pressed:bg-muted.
|
|
315
|
+
* A selected toggle, a view switch or a filter chip alike, and a selected pill tab fill with the foreground, black on
|
|
316
|
+
* white and white on black. Stock shadcn marks them with a faint grey, 1.05:1 against the canvas, so the reader cannot
|
|
317
|
+
* see which one is on. Unlayered, to outrank the stock aria-pressed:bg-muted.
|
|
292
318
|
*/
|
|
293
319
|
:is([data-slot="toggle"], [data-slot="toggle-group-item"]):is([aria-pressed="true"], [data-pressed]),
|
|
294
320
|
[data-slot="tabs-list"][data-variant="default"] [data-slot="tabs-trigger"][data-active] {
|
|
@@ -298,34 +324,9 @@
|
|
|
298
324
|
--tw-shadow: 0 0 #0000;
|
|
299
325
|
}
|
|
300
326
|
|
|
301
|
-
/*
|
|
302
|
-
* A toggle group's items sit flush and share their borders, one control with
|
|
303
|
-
* its parts side by side; stock spaces them 8px apart. A group set to
|
|
304
|
-
* spacing={0} already joins its items by dropping their inner borders. The
|
|
305
|
-
* pressed item rises over its neighbours, so its own edge shows on both sides.
|
|
306
|
-
*/
|
|
307
|
-
[data-slot="toggle-group"]:not([data-spacing="0"]) {
|
|
308
|
-
gap: 0;
|
|
309
|
-
> [data-slot="toggle-group-item"] {
|
|
310
|
-
&[aria-pressed="true"],
|
|
311
|
-
&[data-pressed] {
|
|
312
|
-
z-index: 1;
|
|
313
|
-
}
|
|
314
|
-
&[data-variant="outline"] + [data-slot="toggle-group-item"] {
|
|
315
|
-
margin-inline-start: -1px;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
&[data-orientation="vertical"]
|
|
319
|
-
> [data-slot="toggle-group-item"][data-variant="outline"]
|
|
320
|
-
+ [data-slot="toggle-group-item"] {
|
|
321
|
-
margin-inline-start: 0;
|
|
322
|
-
margin-block-start: -1px;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
327
|
/*
|
|
327
328
|
* A component that lifts a part of itself with a z-index is a stacking context
|
|
328
|
-
* of its own:
|
|
329
|
+
* of its own: MetricStrip's picker buttons, an avatar's badge, a calendar's
|
|
329
330
|
* days, a focused toggle, a code block's buttons, a price chart's layers. The
|
|
330
331
|
* lifted part then ranks only against the rest of its component, and can never
|
|
331
332
|
* sit over, or take a tap from, a page's sticky header or section strip (also
|
|
@@ -352,43 +353,10 @@
|
|
|
352
353
|
}
|
|
353
354
|
|
|
354
355
|
/*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
* CodeBlockHeader, always shown, with the header's plain button.
|
|
360
|
-
*/
|
|
361
|
-
[data-slot="code-block"] > [data-slot="code-block-actions"] {
|
|
362
|
-
position: static;
|
|
363
|
-
align-self: flex-end;
|
|
364
|
-
margin: 0.5rem 0.5rem 0;
|
|
365
|
-
opacity: 1;
|
|
366
|
-
}
|
|
367
|
-
/*
|
|
368
|
-
* Core's floating fill and faint resting ring go. Its fill outranks the ghost
|
|
369
|
-
* button's own hover fill, so that is set again here, as a header's button
|
|
370
|
-
* shows it; the focus ring is left alone.
|
|
371
|
-
*/
|
|
372
|
-
[data-slot="code-block"] > [data-slot="code-block-actions"] > button {
|
|
373
|
-
background-color: transparent;
|
|
374
|
-
&:hover {
|
|
375
|
-
background-color: var(--muted);
|
|
376
|
-
@variant dark {
|
|
377
|
-
background-color: color-mix(in oklab, var(--muted) 50%, transparent);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
&:not(:focus-visible) {
|
|
381
|
-
--tw-ring-shadow: 0 0 #0000;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/*
|
|
386
|
-
* --input is the edge of a field, where the reader types or picks. A button,
|
|
387
|
-
* a toggle and a code block are framed by their label or their content, so
|
|
388
|
-
* their outline stays the faint divider colour; scoping the variable keeps
|
|
389
|
-
* the fill each derives from it faint too. A button is matched by its own
|
|
390
|
-
* group/button class: rendered as a popover or menu trigger, it takes that
|
|
391
|
-
* trigger's data-slot instead of "button".
|
|
356
|
+
* --input is the edge of a field, raised to 3:1 so the reader sees where to type. Stock buttons, toggles and code
|
|
357
|
+
* blocks also edge and fill themselves from it (dark:bg-input/30), which the raised value would make heavy, so for
|
|
358
|
+
* them it stays the faint divider colour, as stock draws them. A button is matched by its own group/button class:
|
|
359
|
+
* rendered as a popover or menu trigger, it takes that trigger's data-slot instead of "button".
|
|
392
360
|
*/
|
|
393
361
|
:is([class~="group/button"], [data-slot="toggle"], [data-slot="toggle-group-item"], [data-slot="code-block"]) {
|
|
394
362
|
--input: var(--border);
|
|
@@ -590,85 +558,6 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
590
558
|
background-color: color-mix(in oklab, var(--destructive) var(--field-wash), var(--card));
|
|
591
559
|
}
|
|
592
560
|
|
|
593
|
-
/*
|
|
594
|
-
* A field's label sits above it in small monospaced capitals, muted, as the
|
|
595
|
-
* labels over figures do: FieldLabel in a Field (directly or in its
|
|
596
|
-
* FieldContent), the home a page gets it from. A label for a checkbox, a
|
|
597
|
-
* switch or a radio reads as a sentence and keeps the sans face, whatever the
|
|
598
|
-
* Field's orientation.
|
|
599
|
-
*/
|
|
600
|
-
[data-slot="field"]:not(:has([role="checkbox"], [role="switch"], [role="radio"])) [data-slot="field-label"] {
|
|
601
|
-
@apply font-mono text-2xs tracking-wider text-muted-foreground uppercase;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
/*
|
|
605
|
-
* A group's label in a menu (dropdown, context, menubar), a select's or a combobox's list or the
|
|
606
|
-
* command palette is a label over its items, set as one.
|
|
607
|
-
*/
|
|
608
|
-
:is(
|
|
609
|
-
[data-slot$="menu-label"],
|
|
610
|
-
[data-slot="menubar-label"],
|
|
611
|
-
[data-slot="select-label"],
|
|
612
|
-
[data-slot="combobox-label"],
|
|
613
|
-
[data-slot="command-group"] [cmdk-group-heading]
|
|
614
|
-
) {
|
|
615
|
-
@apply font-mono text-2xs tracking-wider text-muted-foreground uppercase;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
/*
|
|
619
|
-
* A floating surface reads as a sheet of the page, not a bubble over it: no shadow (the theme has none) and the
|
|
620
|
-
* card's hairline for an edge. Stock shadcn edges a menu, a list, a popover, a dialog or a card with a fainter ring
|
|
621
|
-
* (ring-foreground/10, also behind a variant such as data-open:); it takes the hairline wherever it appears, on
|
|
622
|
-
* a component added later too. The
|
|
623
|
-
* chart's tooltip, which Recharts draws, is named.
|
|
624
|
-
*/
|
|
625
|
-
[class*="ring-foreground/10"] {
|
|
626
|
-
--tw-ring-color: var(--border);
|
|
627
|
-
}
|
|
628
|
-
[data-slot="chart"] .recharts-tooltip-wrapper > div {
|
|
629
|
-
border-color: var(--border);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
/*
|
|
633
|
-
* Square corners everywhere (DESIGN.md): every element and its ::before and ::after are square, whatever rounding
|
|
634
|
-
* a component asks for (rounded-full, rounded-[4px], hover:rounded-full, after:rounded-full), on a component added
|
|
635
|
-
* later too. Only what is round by nature stays round, by what it is rather than its classes: an avatar, a radio
|
|
636
|
-
* and a radio's dot (a questionnaire's radio choice too), a status dot and a spinner. A chart draws its round marks
|
|
637
|
-
* in SVG, which this does not touch.
|
|
638
|
-
*/
|
|
639
|
-
:not(
|
|
640
|
-
[data-slot^="avatar"],
|
|
641
|
-
[data-slot="message-avatar"],
|
|
642
|
-
[role="radio"],
|
|
643
|
-
[role="radio"] *,
|
|
644
|
-
[data-type="radio"] [data-slot$="indicator"],
|
|
645
|
-
[data-type="radio"] [data-slot$="indicator-dot"],
|
|
646
|
-
[data-slot="status-dot"],
|
|
647
|
-
.animate-spin
|
|
648
|
-
),
|
|
649
|
-
:not(
|
|
650
|
-
[data-slot^="avatar"],
|
|
651
|
-
[data-slot="message-avatar"],
|
|
652
|
-
[role="radio"],
|
|
653
|
-
[role="radio"] *,
|
|
654
|
-
[data-type="radio"] [data-slot$="indicator"],
|
|
655
|
-
[data-type="radio"] [data-slot$="indicator-dot"],
|
|
656
|
-
[data-slot="status-dot"],
|
|
657
|
-
.animate-spin
|
|
658
|
-
)::before,
|
|
659
|
-
:not(
|
|
660
|
-
[data-slot^="avatar"],
|
|
661
|
-
[data-slot="message-avatar"],
|
|
662
|
-
[role="radio"],
|
|
663
|
-
[role="radio"] *,
|
|
664
|
-
[data-type="radio"] [data-slot$="indicator"],
|
|
665
|
-
[data-type="radio"] [data-slot$="indicator-dot"],
|
|
666
|
-
[data-slot="status-dot"],
|
|
667
|
-
.animate-spin
|
|
668
|
-
)::after {
|
|
669
|
-
border-radius: 0;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
561
|
/*
|
|
673
562
|
* Muted text is already the faintest that reads at 4.5:1 (contrast.mjs). Stock shadcn fades it further on some
|
|
674
563
|
* parts (text-muted-foreground/70 on a trace's description), under that bar, so a faded muted class reads as the
|
|
@@ -705,65 +594,50 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
705
594
|
}
|
|
706
595
|
|
|
707
596
|
/*
|
|
708
|
-
*
|
|
709
|
-
* button and a data table's column title take the head's type rather than
|
|
710
|
-
*
|
|
711
|
-
* ledger's: one rule above it and a double rule under it in the text colour,
|
|
712
|
-
* with no band.
|
|
597
|
+
* A label over data reads as a terminal's column head: small capitals, muted. A table's column heads (a sort
|
|
598
|
+
* header's button and a data table's column title take the head's type rather than their own), a sidebar group's
|
|
599
|
+
* label, and a group's label in a menu, a select's or a combobox's list or the command palette.
|
|
713
600
|
*/
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
601
|
+
:is(
|
|
602
|
+
[data-slot="table-head"],
|
|
603
|
+
[data-slot="sidebar-group-label"],
|
|
604
|
+
[data-slot$="menu-label"],
|
|
605
|
+
[data-slot="menubar-label"],
|
|
606
|
+
[data-slot="select-label"],
|
|
607
|
+
[data-slot="combobox-label"],
|
|
608
|
+
[data-slot="command-group"] [cmdk-group-heading]
|
|
609
|
+
) {
|
|
610
|
+
@apply text-2xs tracking-wider text-muted-foreground uppercase;
|
|
611
|
+
}
|
|
612
|
+
[data-slot="table-head"] :is([class~="group/button"], [data-slot="column-title"]) {
|
|
613
|
+
font: inherit;
|
|
614
|
+
letter-spacing: inherit;
|
|
615
|
+
text-transform: inherit;
|
|
721
616
|
}
|
|
617
|
+
|
|
722
618
|
/*
|
|
723
|
-
* Tables are dense,
|
|
724
|
-
*
|
|
725
|
-
* weight, and figures in the mono face, a size down so a column of them takes
|
|
726
|
-
* no more room than the sans did. A figure is a cell marked data-numeric (the
|
|
727
|
-
* data table's number columns, or a plain TableCell the page marks), which
|
|
728
|
-
* also right-aligns it, and any Delta in a cell. Alignment alone says nothing:
|
|
729
|
-
* a right-aligned word stays in the sans. Row hairlines stay; no stripes, no
|
|
730
|
-
* column rules.
|
|
619
|
+
* Tables are dense, so a screen shows more rows and columns: 13px text, 24px column heads, cells 2px by 6px. Row
|
|
620
|
+
* hairlines stay; no stripes, no column rules.
|
|
731
621
|
*/
|
|
622
|
+
[data-slot="table"] {
|
|
623
|
+
font-size: 0.8125rem;
|
|
624
|
+
}
|
|
732
625
|
[data-slot="table-head"] {
|
|
733
|
-
height: 1.
|
|
626
|
+
height: 1.5rem;
|
|
627
|
+
padding-inline: 0.375rem;
|
|
734
628
|
}
|
|
735
629
|
[data-slot="table-cell"] {
|
|
736
|
-
padding
|
|
737
|
-
}
|
|
738
|
-
[data-slot="table-header"] [data-slot="table-row"] {
|
|
739
|
-
border-bottom-color: var(--foreground);
|
|
740
|
-
}
|
|
741
|
-
/* A data table's sticky header draws its rule as a line of its own, which stays when the rows scroll under it. */
|
|
742
|
-
[data-slot="table-header"]::after {
|
|
743
|
-
background-color: var(--foreground);
|
|
744
|
-
}
|
|
745
|
-
[data-slot="table-body"] [data-slot="table-cell"]:first-child {
|
|
746
|
-
font-weight: 500;
|
|
747
|
-
}
|
|
748
|
-
:is([data-slot="table-cell"][data-numeric], [data-slot="table-cell"] [data-slot="delta"]) {
|
|
749
|
-
@apply font-mono;
|
|
750
|
-
font-size: 0.8125rem;
|
|
751
|
-
}
|
|
752
|
-
[data-slot="table-footer"] {
|
|
753
|
-
background: none;
|
|
754
|
-
border-top: 1px solid var(--foreground);
|
|
755
|
-
border-bottom: 3px double var(--foreground);
|
|
756
|
-
font-weight: 600;
|
|
630
|
+
padding: 0.125rem 0.375rem;
|
|
757
631
|
}
|
|
758
632
|
|
|
759
633
|
/*
|
|
760
|
-
* An exception the page marks, a
|
|
761
|
-
*
|
|
762
|
-
*
|
|
763
|
-
*
|
|
764
|
-
*
|
|
634
|
+
* An exception the page marks, a MetricStrip figure (its item's tone) or a
|
|
635
|
+
* table row (<TableRow data-tone="negative">, a DataTableBody's getRowTone),
|
|
636
|
+
* takes a faint wash of its colour over the card, and a figure an outline of
|
|
637
|
+
* it, so the one figure needing attention stands out of a run of neutral
|
|
638
|
+
* ones. Unlayered, to outrank a row's hover band.
|
|
765
639
|
*/
|
|
766
|
-
:is([data-slot="
|
|
640
|
+
:is([data-slot="metric-strip-item"], [data-slot="table-row"]) {
|
|
767
641
|
&[data-tone="negative"] {
|
|
768
642
|
--exception: var(--negative);
|
|
769
643
|
}
|
|
@@ -771,24 +645,49 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
771
645
|
--exception: var(--warning);
|
|
772
646
|
}
|
|
773
647
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
648
|
+
/*
|
|
649
|
+
* A MetricStrip figure has no padding of its own, so its text lines up with its neighbours'; its wash and outline
|
|
650
|
+
* reach 8px past it, into the gap, as a picked figure's outline does. --figure-wash is the wash the whole box takes.
|
|
651
|
+
*/
|
|
652
|
+
[data-slot="metric-strip-item"] {
|
|
653
|
+
--figure-reach: calc(var(--spacing) * 2);
|
|
654
|
+
&:is([data-tone="negative"], [data-tone="warning"]) {
|
|
655
|
+
--figure-wash: color-mix(in oklab, var(--exception) var(--wash), var(--card));
|
|
656
|
+
background-color: var(--figure-wash);
|
|
657
|
+
box-shadow: 0 0 0 var(--figure-reach) var(--figure-wash);
|
|
658
|
+
outline: 1px solid color-mix(in oklab, var(--exception) var(--wash-edge), transparent);
|
|
659
|
+
outline-offset: var(--figure-reach);
|
|
660
|
+
}
|
|
661
|
+
&[data-picked] {
|
|
662
|
+
outline: 2px solid var(--primary);
|
|
663
|
+
outline-offset: var(--figure-reach);
|
|
664
|
+
}
|
|
779
665
|
}
|
|
780
666
|
/*
|
|
781
|
-
* A
|
|
782
|
-
*
|
|
667
|
+
* A headline MetricStrip sets each figure on a solid block, in turn the headline orange, the text colour and a grey,
|
|
668
|
+
* touching. Everything in a block takes the block's ink: its label, note and a change's trend icon would not read on
|
|
669
|
+
* the fill in their own colours, and the sign still says gain or loss. A picked block's outline sits inside it.
|
|
783
670
|
*/
|
|
784
|
-
[data-slot="metric-strip
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
671
|
+
[data-slot="metric-strip"][data-variant="blocks"] [data-slot="metric-strip-item"] {
|
|
672
|
+
background-color: var(--headline);
|
|
673
|
+
color: var(--headline-foreground);
|
|
674
|
+
&:nth-child(3n + 2) {
|
|
675
|
+
background-color: var(--foreground);
|
|
676
|
+
color: var(--background);
|
|
677
|
+
}
|
|
678
|
+
&:nth-child(3n) {
|
|
679
|
+
background-color: var(--headline-muted);
|
|
680
|
+
color: var(--headline-muted-foreground);
|
|
681
|
+
}
|
|
682
|
+
& > * {
|
|
683
|
+
--foreground: currentColor;
|
|
684
|
+
--muted-foreground: currentColor;
|
|
685
|
+
--positive: currentColor;
|
|
686
|
+
--negative: currentColor;
|
|
687
|
+
}
|
|
688
|
+
&[data-picked] {
|
|
689
|
+
outline-offset: -4px;
|
|
690
|
+
outline-color: currentColor;
|
|
792
691
|
}
|
|
793
692
|
}
|
|
794
693
|
/* A pinned cell paints the card under itself (niko-table's lib/styles), so it lays the row's wash over that. */
|
|
@@ -803,24 +702,17 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
803
702
|
/*
|
|
804
703
|
* Panels in a PanelGrid are numbered 01, 02 before their title, in the mono
|
|
805
704
|
* face and the link colour, so the text can point to one. A panel without a
|
|
806
|
-
* card title, such as a MetricStrip with none, takes no number
|
|
807
|
-
* wrapped in its own SiyahiProvider (which draws no box) still counts. The panel
|
|
705
|
+
* card title, such as a MetricStrip with none, takes no number. The panel
|
|
808
706
|
* counts, not the title: a card header is a size container, whose style
|
|
809
707
|
* containment would keep each title's count to itself.
|
|
810
708
|
*/
|
|
811
709
|
[data-slot="panel-grid"] {
|
|
812
710
|
counter-reset: panel;
|
|
813
711
|
}
|
|
814
|
-
[data-slot="panel"]:has(
|
|
815
|
-
> [data-slot="card"] > [data-slot="card-header"] > [data-slot="card-title"],
|
|
816
|
-
> [data-slot="siyahi-provider"] > [data-slot="card"] > [data-slot="card-header"] > [data-slot="card-title"]
|
|
817
|
-
) {
|
|
712
|
+
[data-slot="panel"]:has(> [data-slot="card"] > [data-slot="card-header"] > [data-slot="card-title"]) {
|
|
818
713
|
counter-increment: panel;
|
|
819
714
|
}
|
|
820
|
-
|
|
821
|
-
> [data-slot="card"]
|
|
822
|
-
> [data-slot="card-header"]
|
|
823
|
-
> [data-slot="card-title"]::before {
|
|
715
|
+
[data-slot="panel"] > [data-slot="card"] > [data-slot="card-header"] > [data-slot="card-title"]::before {
|
|
824
716
|
content: counter(panel, decimal-leading-zero);
|
|
825
717
|
@apply font-mono text-2xs font-medium text-link;
|
|
826
718
|
letter-spacing: 0.04em;
|
|
@@ -830,22 +722,11 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
830
722
|
}
|
|
831
723
|
|
|
832
724
|
/*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
* it keeps its border.
|
|
836
|
-
*/
|
|
837
|
-
@scope (:root, [data-surface="ruled"], [data-surface="card"]) to ([data-surface="open"]) {
|
|
838
|
-
[data-slot="card"] [data-slot="table-container"] {
|
|
839
|
-
border-color: transparent;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
/*
|
|
843
|
-
* The opaque fill a data table's sticky header and pinned cells paint, so rows
|
|
844
|
-
* scrolling under them stay hidden: the colour under the table, the card it
|
|
845
|
-
* sits in or, on an open surface, whose cards have no fill, the page.
|
|
725
|
+
* The opaque fill a data table's sticky header and pinned cells paint, so rows scrolling under them stay hidden: the
|
|
726
|
+
* page, which a card does not cover.
|
|
846
727
|
*/
|
|
847
728
|
[data-slot="table-container"] {
|
|
848
|
-
--table-fill: var(--
|
|
729
|
+
--table-fill: var(--background);
|
|
849
730
|
}
|
|
850
731
|
|
|
851
732
|
/*
|
|
@@ -888,45 +769,40 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
888
769
|
}
|
|
889
770
|
|
|
890
771
|
/*
|
|
891
|
-
* A block the page marks stale (stale on a chart card or a
|
|
892
|
-
* loses its colour: its chart, sparkline and badge turn grey, and a
|
|
893
|
-
*
|
|
772
|
+
* A block the page marks stale (stale on a chart card or a MetricStrip figure)
|
|
773
|
+
* loses its colour: its chart, sparkline and badge turn grey, and a figure
|
|
774
|
+
* turns muted. The chart and sparkline also draw at 65%, so a
|
|
894
775
|
* dark slot's line does not read heavier than a fresh one: a stale block is
|
|
895
776
|
* meant to be harder to read, so its marks are exempt from the 3:1 mark bar.
|
|
896
777
|
* The small "Stale · 3 Nov" note under the title and the figure keep their
|
|
897
778
|
* text contrast.
|
|
898
779
|
*/
|
|
899
|
-
|
|
780
|
+
:is([data-slot="card"], [data-slot="metric-strip-item"])[data-stale]
|
|
781
|
+
:is([data-slot="chart"], [data-slot="sparkline"], [data-slot="badge"], [data-slot="delta"]) {
|
|
900
782
|
filter: grayscale(1);
|
|
901
783
|
}
|
|
902
|
-
[data-slot="card"][data-stale] :is([data-slot="chart"], [data-slot="sparkline"]) {
|
|
784
|
+
:is([data-slot="card"], [data-slot="metric-strip-item"])[data-stale] :is([data-slot="chart"], [data-slot="sparkline"]) {
|
|
903
785
|
opacity: 0.65;
|
|
904
786
|
}
|
|
905
|
-
[data-slot="
|
|
787
|
+
[data-slot="metric-strip-item"][data-stale] [data-slot="metric-strip-value"] {
|
|
906
788
|
color: var(--muted-foreground);
|
|
907
789
|
}
|
|
908
790
|
|
|
909
791
|
/*
|
|
910
|
-
* Chart gridlines take the full border colour
|
|
911
|
-
*
|
|
792
|
+
* Chart gridlines take the full border colour: stock shadcn draws them at half of it, which all but vanishes on the
|
|
793
|
+
* dark page (10% white).
|
|
912
794
|
*/
|
|
913
795
|
[data-slot="chart"] .recharts-cartesian-grid line {
|
|
914
796
|
stroke: var(--border);
|
|
915
797
|
}
|
|
916
798
|
|
|
917
799
|
/*
|
|
918
|
-
* The sidebar
|
|
919
|
-
*
|
|
920
|
-
* is, and its rows are compact. Fill, icons and the current-page mark stay as
|
|
921
|
-
* shadcn draws them.
|
|
800
|
+
* The sidebar's rows are compact, as a table's are. shadcn edges a collapsible sidebar on its page side already; a
|
|
801
|
+
* non-collapsible one, set in the page's flow, has none, so it takes the hairline here.
|
|
922
802
|
*/
|
|
923
|
-
/* shadcn edges a collapsible sidebar on its page side already; a non-collapsible one, set in the page's flow, has none. */
|
|
924
803
|
div[data-slot="sidebar"]:not([data-side], [data-mobile]) {
|
|
925
804
|
border-inline-end: 1px solid var(--sidebar-border);
|
|
926
805
|
}
|
|
927
|
-
[data-slot="sidebar-group-label"] {
|
|
928
|
-
@apply font-mono text-2xs tracking-wider uppercase;
|
|
929
|
-
}
|
|
930
806
|
[data-slot="sidebar-menu"] {
|
|
931
807
|
gap: 0;
|
|
932
808
|
}
|
|
@@ -942,19 +818,6 @@ div[data-slot="sidebar"]:not([data-side], [data-mobile]) {
|
|
|
942
818
|
height: 1.5rem;
|
|
943
819
|
}
|
|
944
820
|
|
|
945
|
-
/*
|
|
946
|
-
* The selected sub-page marks its stretch of the sidebar's rail in the accent,
|
|
947
|
-
* so the current place reads at a glance in a long list, as a docs index does.
|
|
948
|
-
*/
|
|
949
|
-
[data-slot="sidebar-menu-sub-item"]:has(> [data-active])::before {
|
|
950
|
-
content: "";
|
|
951
|
-
position: absolute;
|
|
952
|
-
inset-block: 2px;
|
|
953
|
-
left: -11px;
|
|
954
|
-
width: 2px;
|
|
955
|
-
background: var(--sidebar-accent-foreground);
|
|
956
|
-
}
|
|
957
|
-
|
|
958
821
|
/* Touch targets follow input capability, including touch laptops. */
|
|
959
822
|
@media (pointer: coarse) {
|
|
960
823
|
:is(
|
|
@@ -1002,51 +865,24 @@ div[data-slot="sidebar"]:not([data-side], [data-mobile]) {
|
|
|
1002
865
|
}
|
|
1003
866
|
|
|
1004
867
|
/*
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1009
|
-
* that stands alone, such as a MetricStrip or a table's card, takes those
|
|
1010
|
-
* rules above and below itself. Card is a filled square card with a hairline
|
|
1011
|
-
* ring and no shadow, 8px from the next, for a page of separate blocks. Open
|
|
1012
|
-
* takes the box off every core Card: no fill, no ring, no tinted footer band,
|
|
1013
|
-
* and no padding, so a block's content sits level with the page's text. Each
|
|
1014
|
-
* scope stops at a provider that says otherwise. Unlayered, like the rules
|
|
1015
|
-
* above, to outrank the core components' utility classes; a picked
|
|
1016
|
-
* SectionCards tile keeps its ring, which is important, and an exception
|
|
1017
|
-
* keeps its outline, which is more specific.
|
|
868
|
+
* A Card is a block on the page, not a box: no fill, no ring, no tinted footer band and no padding, so its content
|
|
869
|
+
* sits level with the page's text, and space, not a box or a rule, parts it from the next. --card is the page colour
|
|
870
|
+
* (above), so what a block mixes into, a forecast's halo, a bar's tint or an exception's wash, is what lies under it.
|
|
871
|
+
* Unlayered, like the rules above, to outrank the core component's utility classes.
|
|
1018
872
|
*/
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
*/
|
|
1027
|
-
[data-slot="card"]:not(
|
|
1028
|
-
:scope :is([data-slot="panel"], [data-slot="section-card"], [data-slot="card"]) [data-slot="card"]
|
|
1029
|
-
) {
|
|
1030
|
-
border-block: 1px solid var(--border);
|
|
1031
|
-
}
|
|
873
|
+
[data-slot="card"] {
|
|
874
|
+
/* No box, so no edge to clip at: a figure's wash or picker reaching past its text into the gap stays whole. */
|
|
875
|
+
overflow: visible;
|
|
876
|
+
background: none;
|
|
877
|
+
--tw-ring-color: transparent;
|
|
878
|
+
--card-spacing: 0px;
|
|
879
|
+
gap: calc(var(--spacing) * 3);
|
|
1032
880
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
[data-slot="card"] {
|
|
1039
|
-
background: none;
|
|
1040
|
-
--tw-ring-color: transparent;
|
|
1041
|
-
--card-spacing: 0px;
|
|
1042
|
-
gap: calc(var(--spacing) * 4);
|
|
1043
|
-
}
|
|
1044
|
-
[data-slot="card-footer"] {
|
|
1045
|
-
background: none;
|
|
1046
|
-
border-top-width: 0;
|
|
1047
|
-
/* The band's own top padding stacked on the card's gap, and with no band it read as a hole under the figure. */
|
|
1048
|
-
padding-top: 0;
|
|
1049
|
-
}
|
|
881
|
+
[data-slot="card-footer"] {
|
|
882
|
+
background: none;
|
|
883
|
+
border-top-width: 0;
|
|
884
|
+
/* The band's own top padding stacked on the card's gap, and with no band it read as a hole under the figure. */
|
|
885
|
+
padding-top: 0;
|
|
1050
886
|
}
|
|
1051
887
|
|
|
1052
888
|
/*
|