@siyahi/ui 0.1.9-next.34 → 0.1.9-next.36
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 +43 -26
- package/README.md +12 -14
- package/THIRD_PARTY_NOTICES.md +10 -11
- package/agents/examples/app-sidebar.tsx +0 -2
- package/agents/examples/chart-panels.tsx +0 -4
- 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 +2 -3
- package/agents/manifest.json +27 -49
- package/dist/components/registry/dashboardcn/kpi-row-card.d.ts +14 -6
- package/dist/components/registry/dashboardcn/kpi-row-card.js +49 -32
- package/dist/components/registry/niko-table/core/data-table-error-boundary.js +1 -1
- package/dist/components/registry/niko-table/filters/table-faceted-filter.js +2 -2
- package/dist/components/siyahi/panel-grid.d.ts +5 -6
- package/dist/components/siyahi/panel-grid.js +10 -9
- 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 -6
- package/dist/components/siyahi/siyahi-provider.js +1 -1
- package/dist/components/siyahi/status-badge.js +1 -0
- package/dist/components/ui/alert-dialog.d.ts +20 -0
- package/dist/components/ui/alert-dialog.js +94 -0
- package/dist/components/ui/aspect-ratio.d.ts +6 -0
- package/dist/components/ui/aspect-ratio.js +13 -0
- package/dist/components/ui/attachment.d.ts +25 -0
- package/dist/components/ui/attachment.js +104 -0
- package/dist/components/ui/bubble.d.ts +18 -0
- package/dist/components/ui/bubble.js +73 -0
- package/dist/components/ui/button-group.d.ts +12 -0
- package/dist/components/ui/button-group.js +41 -0
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/carousel.d.ts +30 -0
- package/dist/components/ui/carousel.js +131 -0
- package/dist/components/ui/checkbox.d.ts +5 -0
- package/dist/components/ui/checkbox.js +20 -0
- package/dist/components/ui/combobox.d.ts +26 -0
- package/dist/components/ui/combobox.js +162 -0
- package/dist/components/ui/command.js +1 -1
- package/dist/components/ui/direction.d.ts +2 -0
- package/dist/components/ui/direction.js +1 -0
- package/dist/components/ui/drawer.d.ts +18 -0
- package/dist/components/ui/drawer.js +123 -0
- package/dist/components/ui/empty.d.ts +13 -0
- package/dist/components/ui/empty.js +59 -0
- package/dist/components/ui/field.js +1 -1
- package/dist/components/ui/hover-card.d.ts +7 -0
- package/dist/components/ui/hover-card.js +36 -0
- package/dist/components/ui/input-otp.d.ts +13 -0
- package/dist/components/ui/input-otp.js +46 -0
- package/dist/components/ui/item.d.ts +24 -0
- package/dist/components/ui/item.js +121 -0
- package/dist/components/ui/kbd.d.ts +5 -0
- package/dist/components/ui/kbd.js +19 -0
- package/dist/components/ui/marker.d.ts +12 -0
- package/dist/components/ui/marker.js +43 -0
- package/dist/components/ui/menubar.d.ts +31 -0
- package/dist/components/ui/menubar.js +136 -0
- package/dist/components/ui/message-scroller.d.ts +12 -0
- package/dist/components/ui/message-scroller.js +60 -0
- package/dist/components/ui/message.d.ts +12 -0
- package/dist/components/ui/message.js +49 -0
- package/dist/components/ui/native-select.d.ts +10 -0
- package/dist/components/ui/native-select.js +38 -0
- package/dist/components/ui/navigation-menu.d.ts +13 -0
- package/dist/components/ui/navigation-menu.js +82 -0
- package/dist/components/ui/pagination.d.ts +19 -0
- package/dist/components/ui/pagination.js +80 -0
- package/dist/components/ui/questionnaire.d.ts +21 -0
- package/dist/components/ui/questionnaire.js +170 -0
- package/dist/components/ui/radio-group.d.ts +7 -0
- package/dist/components/ui/radio-group.js +26 -0
- package/dist/components/ui/resizable.d.ts +9 -0
- package/dist/components/ui/resizable.js +28 -0
- package/dist/components/ui/sidebar.js +1 -1
- package/dist/components/ui/spinner.d.ts +4 -0
- package/dist/components/ui/spinner.js +15 -0
- package/dist/components/ui/switch.d.ts +7 -0
- package/dist/components/ui/switch.js +19 -0
- package/dist/components/ui/toast.d.ts +18 -0
- package/dist/components/ui/toast.js +123 -0
- package/dist/components/ui/trace.d.ts +20 -0
- package/dist/components/ui/trace.js +75 -0
- package/dist/index.d.ts +32 -3
- package/dist/index.js +35 -5
- package/dist/lib/chart-frame.d.ts +1 -1
- package/dist/lib/chart-frame.js +3 -1
- package/dist/lib/chart-series.d.ts +10 -2
- package/dist/lib/chart-series.js +10 -0
- package/package.json +5 -3
- package/registry.lock.json +0 -8
- package/styles/siyahi.css +257 -109
- package/agents/examples/section-cards-trend.tsx +0 -32
- package/agents/examples/section-cards.tsx +0 -48
- package/agents/report-rules.json +0 -7
- package/dist/components/registry/dashboard-01/section-cards.d.ts +0 -30
- package/dist/components/registry/dashboard-01/section-cards.js +0 -74
- package/report.css +0 -146
package/styles/siyahi.css
CHANGED
|
@@ -24,6 +24,21 @@
|
|
|
24
24
|
*/
|
|
25
25
|
@custom-variant dark (@media (prefers-color-scheme: dark));
|
|
26
26
|
|
|
27
|
+
/*
|
|
28
|
+
* Nothing casts a drop shadow (DESIGN.md, "Floating surfaces lie flat"): every shadow size is transparent, so
|
|
29
|
+
* shadow-md and the rest draw nothing, on a component added later as on the ones here. The sizes stay defined, so
|
|
30
|
+
* a stock shadow class is still a known utility to Tailwind and the lint.
|
|
31
|
+
*/
|
|
32
|
+
@theme {
|
|
33
|
+
--shadow-2xs: 0 0 #0000;
|
|
34
|
+
--shadow-xs: 0 0 #0000;
|
|
35
|
+
--shadow-sm: 0 0 #0000;
|
|
36
|
+
--shadow-md: 0 0 #0000;
|
|
37
|
+
--shadow-lg: 0 0 #0000;
|
|
38
|
+
--shadow-xl: 0 0 #0000;
|
|
39
|
+
--shadow-2xl: 0 0 #0000;
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
@theme inline {
|
|
28
43
|
--font-sans: "Geist Variable", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;
|
|
29
44
|
--font-mono: "Geist Mono Variable", "Geist Mono Fallback", ui-monospace, monospace;
|
|
@@ -160,7 +175,7 @@
|
|
|
160
175
|
--chart-1: oklch(0.4 0.17 275);
|
|
161
176
|
--chart-2: oklch(0.66 0.13 70);
|
|
162
177
|
--chart-3: oklch(0.38 0.13 335);
|
|
163
|
-
--chart-4: oklch(0.
|
|
178
|
+
--chart-4: oklch(0.64 0.11 235);
|
|
164
179
|
--chart-5: oklch(0.36 0.06 205);
|
|
165
180
|
--sidebar: oklch(0.985 0 0);
|
|
166
181
|
--sidebar-foreground: oklch(0.145 0 0);
|
|
@@ -174,8 +189,8 @@
|
|
|
174
189
|
@variant dark {
|
|
175
190
|
/*
|
|
176
191
|
* The canvas lifts just off black and text sits a step below white:
|
|
177
|
-
* near-white on near-black (19:1) blurs for many readers, and
|
|
178
|
-
*
|
|
192
|
+
* near-white on near-black (19:1) blurs for many readers, and on an open
|
|
193
|
+
* page, where cards have no fill, the canvas is what text sits on.
|
|
179
194
|
* Claude Code (#181818, about #e8e8e8 text) and T3 Code (#0a0a0a, about
|
|
180
195
|
* #bebebe) both narrow that gap; this sits between them, at 14.9:1.
|
|
181
196
|
* Cards sit one step above the canvas, so data reads as raised, as the
|
|
@@ -310,7 +325,7 @@
|
|
|
310
325
|
|
|
311
326
|
/*
|
|
312
327
|
* A component that lifts a part of itself with a z-index is a stacking context
|
|
313
|
-
* of its own:
|
|
328
|
+
* of its own: MetricStrip's picker buttons, an avatar's badge, a calendar's
|
|
314
329
|
* days, a focused toggle, a code block's buttons, a price chart's layers. The
|
|
315
330
|
* lifted part then ranks only against the rest of its component, and can never
|
|
316
331
|
* sit over, or take a tap from, a page's sticky header or section strip (also
|
|
@@ -325,7 +340,13 @@
|
|
|
325
340
|
[data-slot="calendar"],
|
|
326
341
|
[data-slot="toggle-group"],
|
|
327
342
|
[data-slot="code-block"],
|
|
328
|
-
[data-slot="price-chart"]
|
|
343
|
+
[data-slot="price-chart"],
|
|
344
|
+
[data-slot="button-group"],
|
|
345
|
+
[data-slot="input-otp-group"],
|
|
346
|
+
[data-slot="resizable-panel-group"],
|
|
347
|
+
[data-slot="attachment"],
|
|
348
|
+
[data-slot="bubble"],
|
|
349
|
+
[data-slot="questionnaire"]
|
|
329
350
|
) {
|
|
330
351
|
isolation: isolate;
|
|
331
352
|
}
|
|
@@ -409,11 +430,20 @@
|
|
|
409
430
|
[role="alertdialog"],
|
|
410
431
|
[role="menu"],
|
|
411
432
|
[role="listbox"],
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
433
|
+
input:not(
|
|
434
|
+
[type="checkbox"],
|
|
435
|
+
[type="radio"],
|
|
436
|
+
[type="range"],
|
|
437
|
+
[type="hidden"],
|
|
438
|
+
[type="file"],
|
|
439
|
+
[type="button"],
|
|
440
|
+
[type="submit"],
|
|
441
|
+
[type="reset"],
|
|
442
|
+
[type="color"]
|
|
443
|
+
),
|
|
444
|
+
textarea,
|
|
445
|
+
select,
|
|
446
|
+
[data-slot="select-trigger"]
|
|
417
447
|
),
|
|
418
448
|
[data-slot="slider-thumb"]:has(input:focus-visible) {
|
|
419
449
|
outline: none;
|
|
@@ -421,14 +451,74 @@
|
|
|
421
451
|
inset 0 0 0 2px var(--ring),
|
|
422
452
|
inset 0 0 0 4px var(--background);
|
|
423
453
|
}
|
|
424
|
-
|
|
425
|
-
|
|
454
|
+
/*
|
|
455
|
+
* A field is what HTML makes one, a text input, a textarea or a select, plus Base UI's select trigger, so a field in
|
|
456
|
+
* a component added later is covered without naming it. A field inside a box that holds it (an input group, a
|
|
457
|
+
* combobox's chips) shows its state on that box.
|
|
458
|
+
*/
|
|
459
|
+
:is(
|
|
460
|
+
input:not(
|
|
461
|
+
[type="checkbox"],
|
|
462
|
+
[type="radio"],
|
|
463
|
+
[type="range"],
|
|
464
|
+
[type="hidden"],
|
|
465
|
+
[type="file"],
|
|
466
|
+
[type="button"],
|
|
467
|
+
[type="submit"],
|
|
468
|
+
[type="reset"],
|
|
469
|
+
[type="color"]
|
|
470
|
+
),
|
|
471
|
+
textarea,
|
|
472
|
+
select,
|
|
473
|
+
[data-slot="select-trigger"]
|
|
474
|
+
):not(:is([data-slot="input-group"], [data-slot="combobox-chips"]) *):focus-visible,
|
|
475
|
+
:is([data-slot="input-group"], [data-slot="combobox-chips"]):has(
|
|
476
|
+
:is(
|
|
477
|
+
input:not(
|
|
478
|
+
[type="checkbox"],
|
|
479
|
+
[type="radio"],
|
|
480
|
+
[type="range"],
|
|
481
|
+
[type="hidden"],
|
|
482
|
+
[type="file"],
|
|
483
|
+
[type="button"],
|
|
484
|
+
[type="submit"],
|
|
485
|
+
[type="reset"],
|
|
486
|
+
[type="color"]
|
|
487
|
+
),
|
|
488
|
+
textarea,
|
|
489
|
+
select,
|
|
490
|
+
[data-slot="select-trigger"]
|
|
491
|
+
):focus-visible
|
|
492
|
+
) {
|
|
426
493
|
outline: none;
|
|
427
494
|
border-color: var(--ring);
|
|
428
495
|
box-shadow: inset 0 0 0 1px var(--ring);
|
|
429
496
|
}
|
|
430
|
-
:is(
|
|
431
|
-
|
|
497
|
+
:is(
|
|
498
|
+
input:not(
|
|
499
|
+
[type="checkbox"],
|
|
500
|
+
[type="radio"],
|
|
501
|
+
[type="range"],
|
|
502
|
+
[type="hidden"],
|
|
503
|
+
[type="file"],
|
|
504
|
+
[type="button"],
|
|
505
|
+
[type="submit"],
|
|
506
|
+
[type="reset"],
|
|
507
|
+
[type="color"]
|
|
508
|
+
),
|
|
509
|
+
textarea,
|
|
510
|
+
select,
|
|
511
|
+
[data-slot="select-trigger"]
|
|
512
|
+
):not(:is([data-slot="input-group"], [data-slot="combobox-chips"]) *)[aria-invalid="true"]:focus-visible,
|
|
513
|
+
/* Names the field as the focus rule above does, so this rule outranks it rather than losing on specificity. */
|
|
514
|
+
:is([data-slot="input-group"], [data-slot="combobox-chips"]):has(
|
|
515
|
+
:is(
|
|
516
|
+
input:not([type="checkbox"], [type="radio"], [type="range"], [type="hidden"], [type="file"], [type="button"], [type="submit"], [type="reset"], [type="color"]),
|
|
517
|
+
textarea,
|
|
518
|
+
select,
|
|
519
|
+
[data-slot="select-trigger"]
|
|
520
|
+
)[aria-invalid="true"]:focus-visible
|
|
521
|
+
) {
|
|
432
522
|
border-color: var(--destructive);
|
|
433
523
|
box-shadow: inset 0 0 0 1px var(--destructive);
|
|
434
524
|
}
|
|
@@ -460,17 +550,43 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
460
550
|
[class~="group/button"][class~="text-destructive"]:focus-visible,
|
|
461
551
|
[data-slot="code-block"][data-variant="destructive"]:has(> [data-slot="code-block-content"]:focus-visible),
|
|
462
552
|
:is(
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
[
|
|
553
|
+
input:not(
|
|
554
|
+
[type="checkbox"],
|
|
555
|
+
[type="radio"],
|
|
556
|
+
[type="range"],
|
|
557
|
+
[type="hidden"],
|
|
558
|
+
[type="file"],
|
|
559
|
+
[type="button"],
|
|
560
|
+
[type="submit"],
|
|
561
|
+
[type="reset"],
|
|
562
|
+
[type="color"]
|
|
563
|
+
),
|
|
564
|
+
textarea,
|
|
565
|
+
select,
|
|
566
|
+
[data-slot="select-trigger"]
|
|
567
|
+
):not(:is([data-slot="input-group"], [data-slot="combobox-chips"]) *)[aria-invalid="true"],
|
|
568
|
+
[class~="group/button"][aria-invalid="true"],
|
|
569
|
+
:is([data-slot="input-group"], [data-slot="combobox-chips"]):has([aria-invalid="true"]) {
|
|
469
570
|
border-color: var(--destructive);
|
|
470
571
|
}
|
|
471
572
|
/* An invalid field also takes a faint wash of the red, so it reads as the one to fix before its message is read. */
|
|
472
|
-
:is(
|
|
473
|
-
|
|
573
|
+
:is(
|
|
574
|
+
input:not(
|
|
575
|
+
[type="checkbox"],
|
|
576
|
+
[type="radio"],
|
|
577
|
+
[type="range"],
|
|
578
|
+
[type="hidden"],
|
|
579
|
+
[type="file"],
|
|
580
|
+
[type="button"],
|
|
581
|
+
[type="submit"],
|
|
582
|
+
[type="reset"],
|
|
583
|
+
[type="color"]
|
|
584
|
+
),
|
|
585
|
+
textarea,
|
|
586
|
+
select,
|
|
587
|
+
[data-slot="select-trigger"]
|
|
588
|
+
):not(:is([data-slot="input-group"], [data-slot="combobox-chips"]) *)[aria-invalid="true"],
|
|
589
|
+
:is([data-slot="input-group"], [data-slot="combobox-chips"]):has([aria-invalid="true"]) {
|
|
474
590
|
background-color: color-mix(in oklab, var(--destructive) var(--field-wash), var(--card));
|
|
475
591
|
}
|
|
476
592
|
|
|
@@ -485,48 +601,86 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
485
601
|
@apply font-mono text-2xs tracking-wider text-muted-foreground uppercase;
|
|
486
602
|
}
|
|
487
603
|
|
|
488
|
-
/*
|
|
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
|
+
*/
|
|
489
608
|
:is(
|
|
490
|
-
[data-slot
|
|
491
|
-
[data-slot="
|
|
609
|
+
[data-slot$="menu-label"],
|
|
610
|
+
[data-slot="menubar-label"],
|
|
492
611
|
[data-slot="select-label"],
|
|
612
|
+
[data-slot="combobox-label"],
|
|
493
613
|
[data-slot="command-group"] [cmdk-group-heading]
|
|
494
614
|
) {
|
|
495
615
|
@apply font-mono text-2xs tracking-wider text-muted-foreground uppercase;
|
|
496
616
|
}
|
|
497
617
|
|
|
498
618
|
/*
|
|
499
|
-
* A
|
|
500
|
-
*
|
|
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.
|
|
501
624
|
*/
|
|
502
|
-
|
|
503
|
-
[data-slot="dropdown-menu-content"],
|
|
504
|
-
[data-slot="dropdown-menu-sub-content"],
|
|
505
|
-
[data-slot="context-menu-content"],
|
|
506
|
-
[data-slot="context-menu-sub-content"],
|
|
507
|
-
[data-slot="select-content"],
|
|
508
|
-
[data-slot="popover-content"],
|
|
509
|
-
[data-slot="dialog-content"],
|
|
510
|
-
[data-slot="sheet-content"],
|
|
511
|
-
[data-slot="chart"] .recharts-tooltip-wrapper > div
|
|
512
|
-
) {
|
|
513
|
-
--tw-shadow: 0 0 #0000;
|
|
625
|
+
[class*="ring-foreground/10"] {
|
|
514
626
|
--tw-ring-color: var(--border);
|
|
627
|
+
}
|
|
628
|
+
[data-slot="chart"] .recharts-tooltip-wrapper > div {
|
|
515
629
|
border-color: var(--border);
|
|
516
630
|
}
|
|
517
631
|
|
|
518
632
|
/*
|
|
519
|
-
* Square
|
|
520
|
-
*
|
|
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
|
+
/*
|
|
673
|
+
* Muted text is already the faintest that reads at 4.5:1 (contrast.mjs). Stock shadcn fades it further on some
|
|
674
|
+
* parts (text-muted-foreground/70 on a trace's description), under that bar, so a faded muted class reads as the
|
|
675
|
+
* muted colour itself.
|
|
521
676
|
*/
|
|
522
677
|
:is(
|
|
523
|
-
[
|
|
524
|
-
[
|
|
525
|
-
[
|
|
526
|
-
[
|
|
527
|
-
[data-slot="chart"] :is(.recharts-tooltip-wrapper, .recharts-legend-wrapper) [class~="rounded-[2px]"]
|
|
678
|
+
[class~="text-muted-foreground/50"],
|
|
679
|
+
[class~="text-muted-foreground/60"],
|
|
680
|
+
[class~="text-muted-foreground/70"],
|
|
681
|
+
[class~="text-muted-foreground/80"]
|
|
528
682
|
) {
|
|
529
|
-
|
|
683
|
+
color: var(--muted-foreground);
|
|
530
684
|
}
|
|
531
685
|
|
|
532
686
|
/*
|
|
@@ -603,13 +757,13 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
603
757
|
}
|
|
604
758
|
|
|
605
759
|
/*
|
|
606
|
-
* An exception the page marks, a
|
|
607
|
-
*
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
760
|
+
* An exception the page marks, a MetricStrip figure (its item's tone) or a
|
|
761
|
+
* table row (<TableRow data-tone="negative">, a DataTableBody's getRowTone),
|
|
762
|
+
* takes a faint wash of its colour over the card, and a figure an outline of
|
|
763
|
+
* it, so the one figure needing attention stands out of a run of neutral
|
|
764
|
+
* ones. Unlayered, to outrank a row's hover band.
|
|
611
765
|
*/
|
|
612
|
-
:is([data-slot="
|
|
766
|
+
:is([data-slot="metric-strip-item"], [data-slot="table-row"]) {
|
|
613
767
|
&[data-tone="negative"] {
|
|
614
768
|
--exception: var(--negative);
|
|
615
769
|
}
|
|
@@ -617,24 +771,22 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
617
771
|
--exception: var(--warning);
|
|
618
772
|
}
|
|
619
773
|
}
|
|
620
|
-
:is([data-slot="card"], [data-slot="metric-strip-item"]):is([data-tone="negative"], [data-tone="warning"]) {
|
|
621
|
-
background-color: color-mix(in oklab, var(--exception) var(--wash), var(--card));
|
|
622
|
-
}
|
|
623
|
-
[data-slot="card"]:is([data-tone="negative"], [data-tone="warning"]) {
|
|
624
|
-
--tw-ring-color: color-mix(in oklab, var(--exception) var(--wash-edge), transparent);
|
|
625
|
-
}
|
|
626
774
|
/*
|
|
627
|
-
* A figure
|
|
628
|
-
*
|
|
775
|
+
* A MetricStrip figure has no padding of its own, so its text lines up with its neighbours'; its wash and outline
|
|
776
|
+
* reach 8px past it, into the gap, as a picked figure's outline does. --figure-wash is the wash the whole box takes.
|
|
629
777
|
*/
|
|
630
|
-
[data-slot="metric-strip-item"]
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
778
|
+
[data-slot="metric-strip-item"] {
|
|
779
|
+
--figure-reach: calc(var(--spacing) * 2);
|
|
780
|
+
&:is([data-tone="negative"], [data-tone="warning"]) {
|
|
781
|
+
--figure-wash: color-mix(in oklab, var(--exception) var(--wash), var(--card));
|
|
782
|
+
background-color: var(--figure-wash);
|
|
783
|
+
box-shadow: 0 0 0 var(--figure-reach) var(--figure-wash);
|
|
784
|
+
outline: 1px solid color-mix(in oklab, var(--exception) var(--wash-edge), transparent);
|
|
785
|
+
outline-offset: var(--figure-reach);
|
|
786
|
+
}
|
|
787
|
+
&[data-picked] {
|
|
788
|
+
outline: 2px solid var(--primary);
|
|
789
|
+
outline-offset: var(--figure-reach);
|
|
638
790
|
}
|
|
639
791
|
}
|
|
640
792
|
/* A pinned cell paints the card under itself (niko-table's lib/styles), so it lays the row's wash over that. */
|
|
@@ -676,11 +828,10 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
676
828
|
}
|
|
677
829
|
|
|
678
830
|
/*
|
|
679
|
-
* A data table inside a card drops its own border: the card's ring
|
|
680
|
-
*
|
|
681
|
-
* it keeps its border.
|
|
831
|
+
* A data table inside a boxed card drops its own border: the card's ring
|
|
832
|
+
* already frames it. On an open page, with no frame, it keeps its border.
|
|
682
833
|
*/
|
|
683
|
-
@scope (
|
|
834
|
+
@scope ([data-surface="card"]) to ([data-surface="open"]) {
|
|
684
835
|
[data-slot="card"] [data-slot="table-container"] {
|
|
685
836
|
border-color: transparent;
|
|
686
837
|
}
|
|
@@ -734,21 +885,22 @@ svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
|
734
885
|
}
|
|
735
886
|
|
|
736
887
|
/*
|
|
737
|
-
* A block the page marks stale (stale on a chart card or a
|
|
738
|
-
* loses its colour: its chart, sparkline and badge turn grey, and a
|
|
739
|
-
*
|
|
888
|
+
* A block the page marks stale (stale on a chart card or a MetricStrip figure)
|
|
889
|
+
* loses its colour: its chart, sparkline and badge turn grey, and a figure
|
|
890
|
+
* turns muted. The chart and sparkline also draw at 65%, so a
|
|
740
891
|
* dark slot's line does not read heavier than a fresh one: a stale block is
|
|
741
892
|
* meant to be harder to read, so its marks are exempt from the 3:1 mark bar.
|
|
742
893
|
* The small "Stale · 3 Nov" note under the title and the figure keep their
|
|
743
894
|
* text contrast.
|
|
744
895
|
*/
|
|
745
|
-
|
|
896
|
+
:is([data-slot="card"], [data-slot="metric-strip-item"])[data-stale]
|
|
897
|
+
:is([data-slot="chart"], [data-slot="sparkline"], [data-slot="badge"], [data-slot="delta"]) {
|
|
746
898
|
filter: grayscale(1);
|
|
747
899
|
}
|
|
748
|
-
[data-slot="card"][data-stale] :is([data-slot="chart"], [data-slot="sparkline"]) {
|
|
900
|
+
:is([data-slot="card"], [data-slot="metric-strip-item"])[data-stale] :is([data-slot="chart"], [data-slot="sparkline"]) {
|
|
749
901
|
opacity: 0.65;
|
|
750
902
|
}
|
|
751
|
-
[data-slot="
|
|
903
|
+
[data-slot="metric-strip-item"][data-stale] [data-slot="metric-strip-value"] {
|
|
752
904
|
color: var(--muted-foreground);
|
|
753
905
|
}
|
|
754
906
|
|
|
@@ -808,6 +960,7 @@ div[data-slot="sidebar"]:not([data-side], [data-mobile]) {
|
|
|
808
960
|
[data-slot="toggle"],
|
|
809
961
|
[data-slot="toggle-group-item"],
|
|
810
962
|
[data-slot="select-trigger"],
|
|
963
|
+
[data-slot="native-select"],
|
|
811
964
|
[data-slot="tabs-trigger"],
|
|
812
965
|
[data-slot="input-group-button"]
|
|
813
966
|
) {
|
|
@@ -847,44 +1000,39 @@ div[data-slot="sidebar"]:not([data-side], [data-mobile]) {
|
|
|
847
1000
|
}
|
|
848
1001
|
|
|
849
1002
|
/*
|
|
850
|
-
* Surfaces, from SiyahiProvider's surface prop.
|
|
851
|
-
* every
|
|
852
|
-
*
|
|
853
|
-
*
|
|
854
|
-
*
|
|
855
|
-
*
|
|
856
|
-
*
|
|
857
|
-
*
|
|
858
|
-
* and no padding, so a block's content sits level with the page's text. Each
|
|
859
|
-
* scope stops at a provider that says otherwise. Unlayered, like the rules
|
|
860
|
-
* above, to outrank the core components' utility classes; a picked
|
|
861
|
-
* SectionCards tile keeps its ring, which is important, and an exception
|
|
862
|
-
* keeps its outline, which is more specific.
|
|
1003
|
+
* Surfaces, from SiyahiProvider's surface prop. Open, the default, takes the
|
|
1004
|
+
* box off every core Card: no fill, no ring, no tinted footer band, and no
|
|
1005
|
+
* padding, so a block's content sits level with the page's text and space,
|
|
1006
|
+
* not a box or a rule, parts it from the next. Card is a filled square card
|
|
1007
|
+
* with a hairline ring and no shadow, 8px from the next, for a page of
|
|
1008
|
+
* separate blocks. Each scope stops at a provider that says otherwise.
|
|
1009
|
+
* Unlayered, like the rules above, to outrank the core components' utility
|
|
1010
|
+
* classes.
|
|
863
1011
|
*/
|
|
864
|
-
|
|
865
|
-
|
|
1012
|
+
/*
|
|
1013
|
+
* An open card renames --card to the page colour for everything inside it, and a custom property inherits past a
|
|
1014
|
+
* scope's end, so a card provider nested in an open card would fill its boxes with the page colour. --card-fill keeps
|
|
1015
|
+
* the theme's card colour, set once at the root, and each card provider takes it back.
|
|
1016
|
+
*/
|
|
1017
|
+
:root {
|
|
1018
|
+
--card-fill: var(--card);
|
|
866
1019
|
}
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
[data-slot="card"] {
|
|
870
|
-
--tw-ring-color: transparent;
|
|
871
|
-
}
|
|
872
|
-
/*
|
|
873
|
-
* A card inside a panel or a tile's cell, or nested in another card, is ruled by what holds it; only a holder
|
|
874
|
-
* inside this scope counts, so a ruled provider inside a card starts its own lone cards afresh.
|
|
875
|
-
*/
|
|
876
|
-
[data-slot="card"]:not(
|
|
877
|
-
:scope :is([data-slot="panel"], [data-slot="section-card"], [data-slot="card"]) [data-slot="card"]
|
|
878
|
-
) {
|
|
879
|
-
border-block: 1px solid var(--border);
|
|
880
|
-
}
|
|
1020
|
+
[data-surface="card"] {
|
|
1021
|
+
--card: var(--card-fill);
|
|
881
1022
|
}
|
|
882
1023
|
|
|
883
|
-
@scope ([data-surface="open"]) to ([data-surface="
|
|
1024
|
+
@scope (:root, [data-surface="open"]) to ([data-surface="card"]) {
|
|
884
1025
|
[data-slot="table-container"] {
|
|
885
1026
|
--table-fill: var(--background);
|
|
886
1027
|
}
|
|
1028
|
+
/*
|
|
1029
|
+
* With no fill, what lies under a card's marks is the page, so --card names the page colour inside it: a forecast's
|
|
1030
|
+
* halos, a dot's ring, a bar's tint and an exception's wash mix into the canvas, not into a grey no box shows.
|
|
1031
|
+
*/
|
|
887
1032
|
[data-slot="card"] {
|
|
1033
|
+
--card: var(--background);
|
|
1034
|
+
/* No box, so no edge to clip at: a figure's wash or picker reaching past its text into the gap stays whole. */
|
|
1035
|
+
overflow: visible;
|
|
888
1036
|
background: none;
|
|
889
1037
|
--tw-ring-color: transparent;
|
|
890
1038
|
--card-spacing: 0px;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Delta, SectionCards, Sparkline, useFormat, type SectionCard } from "@siyahi/ui";
|
|
2
|
-
|
|
3
|
-
export default function SectionCardsTrendExample() {
|
|
4
|
-
const { money, moneyCompact } = useFormat();
|
|
5
|
-
const items: SectionCard[] = [
|
|
6
|
-
{
|
|
7
|
-
label: "Revenue",
|
|
8
|
-
value: money(4512300),
|
|
9
|
-
badge: <Delta value={0.031} />,
|
|
10
|
-
trend: <Sparkline data={[3820000, 3950000, 4110000, 4020000, 4380000, 4512300]} />,
|
|
11
|
-
detail: "Month-end, last six months",
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
label: "Cash flow this year",
|
|
15
|
-
value: moneyCompact(-61200),
|
|
16
|
-
badge: <Delta value={-61200} format="money" />,
|
|
17
|
-
// A run of losses: the meaning colour, not a chart slot.
|
|
18
|
-
trend: <Sparkline data={[0, -8200, -14100, -30400, -52800, -61200]} color="var(--negative)" />,
|
|
19
|
-
detail: "Running total since April",
|
|
20
|
-
// The one figure on the page that needs a look, marked by the page: a falling cost would not be.
|
|
21
|
-
tone: "negative",
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
label: "Gift cards this year",
|
|
25
|
-
value: moneyCompact(38400),
|
|
26
|
-
badge: <Delta value={0.18} />,
|
|
27
|
-
trend: <Sparkline data={[4100, 0, 12800, 3300, 9900, 8300]} variant="line" curve="linear" />,
|
|
28
|
-
detail: "Sold each month",
|
|
29
|
-
},
|
|
30
|
-
];
|
|
31
|
-
return <SectionCards items={items} />;
|
|
32
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { Delta, SectionCards, type SectionCard, useFormat } from "@siyahi/ui";
|
|
2
|
-
|
|
3
|
-
export default function SectionCardsExample() {
|
|
4
|
-
const { money, moneyCompact } = useFormat();
|
|
5
|
-
const items: SectionCard[] = [
|
|
6
|
-
{
|
|
7
|
-
label: "Revenue",
|
|
8
|
-
value: money(4512300),
|
|
9
|
-
badge: <Delta value={0.031} />,
|
|
10
|
-
headline: "Up for the third month",
|
|
11
|
-
detail: "Paid orders, less refunds",
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
label: "Target",
|
|
15
|
-
value: money(4025000),
|
|
16
|
-
headline: "Set at the start of the quarter",
|
|
17
|
-
detail: "Across 4 stores",
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
label: "Ahead of target",
|
|
21
|
-
value: moneyCompact(487300),
|
|
22
|
-
badge: <Delta value={0.121} />,
|
|
23
|
-
headline: "Gross margin 14.2%",
|
|
24
|
-
detail: "Since April",
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
label: "Cash flow this year",
|
|
28
|
-
value: moneyCompact(-61200),
|
|
29
|
-
badge: <Delta value={-61200} format="money" />,
|
|
30
|
-
headline: "Two large inventory buys",
|
|
31
|
-
detail: "Before tax",
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
label: "Gift cards this year",
|
|
35
|
-
value: moneyCompact(38400),
|
|
36
|
-
badge: <Delta value={0.18} />,
|
|
37
|
-
headline: "Sold in 11 stores",
|
|
38
|
-
detail: "Paid at purchase",
|
|
39
|
-
},
|
|
40
|
-
];
|
|
41
|
-
return (
|
|
42
|
-
<div className="flex flex-col gap-6">
|
|
43
|
-
<SectionCards items={items.slice(0, 3)} />
|
|
44
|
-
<SectionCards items={items.slice(0, 4)} />
|
|
45
|
-
<SectionCards items={items} />
|
|
46
|
-
</div>
|
|
47
|
-
);
|
|
48
|
-
}
|
package/agents/report-rules.json
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import * as React$1 from "react";
|
|
2
|
-
//#region src/components/registry/dashboard-01/section-cards.d.ts
|
|
3
|
-
export interface SectionCard {
|
|
4
|
-
/** Muted label above the value. */
|
|
5
|
-
label: string;
|
|
6
|
-
value: React$1.ReactNode;
|
|
7
|
-
/** Outline badge, top right; usually a <Delta />. */
|
|
8
|
-
badge?: React$1.ReactNode;
|
|
9
|
-
/** A small chart under the value, usually a <Sparkline /> of the recent run. */
|
|
10
|
-
trend?: React$1.ReactNode;
|
|
11
|
-
/** Bold first footer line. */
|
|
12
|
-
headline?: React$1.ReactNode;
|
|
13
|
-
/** Muted second footer line. */
|
|
14
|
-
detail?: React$1.ReactNode;
|
|
15
|
-
/** In a picker, a tile that cannot be picked, such as one with nothing in it: faded, and its button disabled. */
|
|
16
|
-
disabled?: boolean | undefined;
|
|
17
|
-
/** Marks the tile as the exception on the page: a faint wash of the colour and an outline of it. */
|
|
18
|
-
tone?: "negative" | "warning" | undefined;
|
|
19
|
-
/** Marks the tile's data as old, with since when, such as "3 Nov": the tile greys out and says so. */
|
|
20
|
-
stale?: React$1.ReactNode;
|
|
21
|
-
}
|
|
22
|
-
export interface SectionCardsProps {
|
|
23
|
-
items: SectionCard[];
|
|
24
|
-
/** The label of the picked tile. */
|
|
25
|
-
selected?: string | undefined;
|
|
26
|
-
/** Makes each tile one button, so keep tile content non-interactive; gets the label of the tile picked. */
|
|
27
|
-
onSelect?: ((label: string) => void) | undefined;
|
|
28
|
-
}
|
|
29
|
-
export declare function SectionCards({ items, selected, onSelect }: SectionCardsProps): React$1.JSX.Element;
|
|
30
|
-
//#endregion
|