@siyahi/ui 0.1.9-next.3 → 0.1.9-next.30
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 +170 -0
- package/README.md +94 -177
- package/THIRD_PARTY_NOTICES.md +11 -11
- package/agents/examples/accordion.tsx +39 -0
- package/agents/examples/alert.tsx +24 -0
- package/agents/examples/app-sidebar.tsx +40 -0
- package/agents/examples/avatar.tsx +24 -0
- package/agents/examples/badge.tsx +26 -0
- package/agents/examples/bar-list-labels.tsx +22 -0
- package/agents/examples/bar-list-sides.tsx +40 -0
- package/agents/examples/bar-list.tsx +24 -0
- package/agents/examples/breadcrumb.tsx +28 -0
- package/agents/examples/button-states.tsx +23 -0
- package/agents/examples/button.tsx +21 -0
- package/agents/examples/calendar.tsx +11 -0
- package/agents/examples/callout.tsx +33 -0
- package/agents/examples/card.tsx +36 -0
- package/agents/examples/change-bar.tsx +104 -0
- package/agents/examples/change-grid-money.tsx +36 -0
- package/agents/examples/change-grid.tsx +42 -0
- package/agents/examples/chart-area-log.tsx +45 -0
- package/agents/examples/chart-area.tsx +39 -0
- package/agents/examples/chart-bar-flows.tsx +32 -0
- package/agents/examples/chart-bar-hourly.tsx +28 -0
- package/agents/examples/chart-bar-losses.tsx +29 -0
- package/agents/examples/chart-bar-teams.tsx +72 -0
- package/agents/examples/chart-bar-years.tsx +40 -0
- package/agents/examples/chart-bar.tsx +56 -0
- package/agents/examples/chart-category-ticks.tsx +50 -0
- package/agents/examples/chart-line-events.tsx +47 -0
- package/agents/examples/chart-line-forecast.tsx +36 -0
- package/agents/examples/chart-line-log.tsx +39 -0
- package/agents/examples/chart-line-quiet.tsx +114 -0
- package/agents/examples/chart-line-timeline.tsx +41 -0
- package/agents/examples/chart-line.tsx +57 -0
- package/agents/examples/chart-panels.tsx +94 -0
- package/agents/examples/code-block.tsx +40 -0
- package/agents/examples/collapsible.tsx +18 -0
- package/agents/examples/command.tsx +29 -0
- package/agents/examples/context-menu.tsx +17 -0
- package/agents/examples/copy-button.tsx +8 -0
- package/agents/examples/data-bar.tsx +108 -0
- package/agents/examples/data-table-compact.tsx +96 -0
- package/agents/examples/data-table-filters.tsx +103 -0
- package/agents/examples/data-table.tsx +99 -0
- package/agents/examples/delta.tsx +18 -0
- package/agents/examples/dialog.tsx +28 -0
- package/agents/examples/distribution-bar-budget.tsx +30 -0
- package/agents/examples/distribution-bar-tug.tsx +74 -0
- package/agents/examples/drawdown-chart.tsx +35 -0
- package/agents/examples/dropdown-menu.tsx +31 -0
- package/agents/examples/field.tsx +108 -0
- package/agents/examples/formatters.tsx +42 -0
- package/agents/examples/heatmap-calendar-signed.tsx +67 -0
- package/agents/examples/heatmap-calendar.tsx +62 -0
- package/agents/examples/heatmap-status.tsx +61 -0
- package/agents/examples/heatmap-weekday.tsx +72 -0
- package/agents/examples/histogram-chart.tsx +28 -0
- package/agents/examples/input-states.tsx +23 -0
- package/agents/examples/input.tsx +17 -0
- package/agents/examples/local-time.tsx +14 -0
- package/agents/examples/metric-strip.tsx +46 -0
- package/agents/examples/overview-dashboard.tsx +180 -0
- package/agents/examples/page-hero.tsx +38 -0
- package/agents/examples/page-section.tsx +52 -0
- package/agents/examples/popover.tsx +23 -0
- package/agents/examples/price-chart.tsx +32 -0
- package/agents/examples/progress-tone.tsx +29 -0
- package/agents/examples/progress.tsx +10 -0
- package/agents/examples/range-position-table.tsx +101 -0
- package/agents/examples/range-position.tsx +55 -0
- package/agents/examples/scatter-chart-quadrants.tsx +63 -0
- package/agents/examples/scatter-chart-scales.tsx +46 -0
- package/agents/examples/scatter-chart.tsx +78 -0
- package/agents/examples/scroll-area.tsx +22 -0
- package/agents/examples/section-cards-select.tsx +28 -0
- package/agents/examples/section-cards-trend.tsx +32 -0
- package/agents/examples/section-cards.tsx +48 -0
- package/agents/examples/select.tsx +27 -0
- package/agents/examples/separator.tsx +13 -0
- package/agents/examples/sheet.tsx +15 -0
- package/agents/examples/skeleton.tsx +11 -0
- package/agents/examples/slider.tsx +5 -0
- package/agents/examples/slope-chart-market.tsx +33 -0
- package/agents/examples/slope-chart.tsx +26 -0
- package/agents/examples/stale-blocks.tsx +56 -0
- package/agents/examples/surfaces.tsx +116 -0
- package/agents/examples/table.tsx +60 -0
- package/agents/examples/tabs.tsx +16 -0
- package/agents/examples/target-rows.tsx +74 -0
- package/agents/examples/toggle-group.tsx +12 -0
- package/agents/examples/toggle.tsx +11 -0
- package/agents/examples/tooltip.tsx +13 -0
- package/agents/examples/treemap.tsx +93 -0
- package/agents/examples/waterfall-chart-stores.tsx +27 -0
- package/agents/examples/waterfall-chart.tsx +37 -0
- package/agents/manifest.json +1391 -0
- package/agents/report-rules.json +7 -0
- package/dist/components/registry/charts/area-chart-card.d.ts +7 -3
- package/dist/components/registry/charts/area-chart-card.js +56 -10
- package/dist/components/registry/charts/bar-chart-card.d.ts +1 -1
- package/dist/components/registry/charts/bar-chart-card.js +20 -22
- package/dist/components/registry/charts/line-chart-card.d.ts +3 -3
- package/dist/components/registry/charts/line-chart-card.js +44 -9
- package/dist/components/registry/dashboard-01/section-cards.d.ts +6 -0
- package/dist/components/registry/dashboard-01/section-cards.js +51 -35
- package/dist/components/registry/dashboardcn/bar-list.js +17 -10
- package/dist/components/registry/dashboardcn/distribution-bar.js +6 -4
- package/dist/components/registry/dashboardcn/kpi-row-card.d.ts +29 -0
- package/dist/components/registry/dashboardcn/kpi-row-card.js +81 -0
- package/dist/components/registry/dashboardcn/sparkline.d.ts +22 -0
- package/dist/components/registry/dashboardcn/sparkline.js +117 -0
- package/dist/components/registry/heatmap/calendar-heatmap.d.ts +11 -3
- package/dist/components/registry/heatmap/calendar-heatmap.js +164 -45
- package/dist/components/registry/heatmap/status-heatmap.d.ts +5 -3
- package/dist/components/registry/heatmap/status-heatmap.js +115 -54
- package/dist/components/registry/heatmap/weekday-heatmap.d.ts +9 -3
- package/dist/components/registry/heatmap/weekday-heatmap.js +83 -53
- package/dist/components/registry/niko-table/core/data-table-context.d.ts +18 -1
- package/dist/components/registry/niko-table/core/data-table-context.js +7 -1
- package/dist/components/registry/niko-table/core/data-table-grouped-row.js +3 -1
- package/dist/components/registry/niko-table/core/data-table-root.js +39 -4
- package/dist/components/registry/niko-table/core/data-table-structure.js +17 -12
- package/dist/components/registry/niko-table/core/data-table.d.ts +2 -2
- package/dist/components/registry/niko-table/core/data-table.js +5 -1
- package/dist/components/registry/niko-table/filters/table-faceted-filter.js +5 -5
- package/dist/components/registry/niko-table/filters/table-search-filter.js +1 -1
- package/dist/components/registry/niko-table/lib/flex-columns.d.ts +1 -1
- package/dist/components/registry/niko-table/lib/flex-columns.js +3 -3
- package/dist/components/registry/niko-table/lib/styles.d.ts +1 -1
- package/dist/components/registry/niko-table/lib/styles.js +3 -3
- package/dist/components/registry/niko-table/types/index.d.ts +9 -0
- package/dist/components/registry/niko-table/ui/table.js +2 -2
- package/dist/components/registry/sidebar-07/app-sidebar.d.ts +1 -1
- package/dist/components/registry/sidebar-07/app-sidebar.js +3 -1
- package/dist/components/registry/sidebar-07/nav-main.d.ts +2 -1
- package/dist/components/registry/sidebar-07/nav-main.js +16 -3
- package/dist/components/siyahi/callout.d.ts +17 -0
- package/dist/components/siyahi/callout.js +34 -0
- package/dist/components/siyahi/change-bar.d.ts +21 -0
- package/dist/components/siyahi/change-bar.js +35 -0
- package/dist/components/siyahi/change-grid.d.ts +31 -0
- package/dist/components/siyahi/change-grid.js +74 -0
- package/dist/components/siyahi/data-bar.d.ts +19 -0
- package/dist/components/siyahi/data-bar.js +29 -0
- package/dist/components/siyahi/data-table-compact-columns-toggle.d.ts +4 -0
- package/dist/components/siyahi/data-table-compact-columns-toggle.js +19 -0
- package/dist/components/siyahi/data-table-footnote.d.ts +12 -0
- package/dist/components/siyahi/data-table-footnote.js +35 -0
- package/dist/components/siyahi/default-sidebar-open.d.ts +8 -0
- package/dist/components/siyahi/default-sidebar-open.js +13 -0
- package/dist/components/siyahi/delta.d.ts +5 -1
- package/dist/components/siyahi/delta.js +14 -9
- package/dist/components/siyahi/drawdown-chart.d.ts +34 -0
- package/dist/components/siyahi/drawdown-chart.js +102 -0
- package/dist/components/siyahi/histogram-chart.d.ts +57 -0
- package/dist/components/siyahi/histogram-chart.js +134 -0
- package/dist/components/siyahi/page-hero.d.ts +23 -0
- package/dist/components/siyahi/page-hero.js +43 -0
- package/dist/components/siyahi/page-section.d.ts +4 -1
- package/dist/components/siyahi/page-section.js +6 -3
- package/dist/components/siyahi/page-toc.d.ts +20 -0
- package/dist/components/siyahi/page-toc.js +76 -0
- package/dist/components/siyahi/panel-grid.d.ts +24 -0
- package/dist/components/siyahi/panel-grid.js +37 -0
- package/dist/components/siyahi/quote.d.ts +12 -0
- package/dist/components/siyahi/quote.js +23 -0
- package/dist/components/siyahi/range-position.d.ts +24 -0
- package/dist/components/siyahi/range-position.js +105 -0
- package/dist/components/siyahi/scale-toggle.d.ts +32 -0
- package/dist/components/siyahi/scale-toggle.js +61 -0
- package/dist/components/siyahi/scatter-chart.d.ts +69 -0
- package/dist/components/siyahi/scatter-chart.js +395 -0
- package/dist/components/siyahi/siyahi-provider.d.ts +15 -2
- package/dist/components/siyahi/siyahi-provider.js +17 -4
- package/dist/components/siyahi/slope-chart.d.ts +35 -0
- package/dist/components/siyahi/slope-chart.js +159 -0
- package/dist/components/siyahi/stale-note.d.ts +9 -0
- package/dist/components/siyahi/stale-note.js +12 -0
- package/dist/components/siyahi/status-alert.d.ts +15 -0
- package/dist/components/siyahi/status-alert.js +29 -0
- package/dist/components/siyahi/status-badge.d.ts +15 -0
- package/dist/components/siyahi/status-badge.js +24 -0
- package/dist/components/siyahi/target-rows.d.ts +34 -0
- package/dist/components/siyahi/target-rows.js +97 -0
- package/dist/components/siyahi/tone-progress.d.ts +1 -1
- package/dist/components/siyahi/tone-progress.js +1 -1
- package/dist/components/siyahi/treemap.d.ts +8 -5
- package/dist/components/siyahi/treemap.js +80 -34
- package/dist/components/siyahi/waterfall-chart.d.ts +51 -0
- package/dist/components/siyahi/waterfall-chart.js +147 -0
- package/dist/components/ui/accordion.js +5 -5
- package/dist/components/ui/alert.js +2 -2
- package/dist/components/ui/badge.js +1 -1
- package/dist/components/ui/breadcrumb.js +2 -2
- package/dist/components/ui/button.js +10 -10
- package/dist/components/ui/calendar.js +1 -1
- package/dist/components/ui/card.js +4 -4
- package/dist/components/ui/chart.js +1 -1
- package/dist/components/ui/code-block.js +13 -12
- package/dist/components/ui/command.js +7 -6
- package/dist/components/ui/context-menu.js +7 -7
- package/dist/components/ui/dialog.js +5 -5
- package/dist/components/ui/dropdown-menu.js +8 -8
- package/dist/components/ui/field.d.ts +26 -0
- package/dist/components/ui/field.js +109 -0
- package/dist/components/ui/input-group.js +11 -11
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/label.d.ts +5 -0
- package/dist/components/ui/label.js +13 -0
- package/dist/components/ui/popover.js +3 -3
- package/dist/components/ui/progress.js +1 -1
- package/dist/components/ui/select.js +6 -6
- package/dist/components/ui/sheet.js +5 -5
- package/dist/components/ui/sidebar.js +17 -18
- package/dist/components/ui/skeleton.js +1 -1
- package/dist/components/ui/slider.js +2 -2
- package/dist/components/ui/table.js +2 -2
- package/dist/components/ui/tabs.js +2 -2
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/toggle-group.js +2 -2
- package/dist/components/ui/toggle.js +4 -4
- package/dist/components/ui/tooltip.js +2 -2
- package/dist/index.d.ts +35 -8
- package/dist/index.js +30 -4
- package/dist/lib/chart-frame.d.ts +51 -7
- package/dist/lib/chart-frame.js +390 -133
- package/dist/lib/chart-marks.d.ts +83 -0
- package/dist/lib/chart-marks.js +216 -0
- package/dist/lib/chart-series.d.ts +42 -10
- package/dist/lib/compact-columns.d.ts +19 -0
- package/dist/lib/compact-columns.js +55 -0
- package/dist/lib/fit-text.d.ts +6 -0
- package/dist/lib/fit-text.js +11 -0
- package/dist/lib/format.d.ts +8 -3
- package/dist/lib/format.js +55 -17
- package/dist/lib/heat.d.ts +7 -0
- package/dist/lib/heat.js +14 -0
- package/dist/lib/heatmap-navigation.d.ts +8 -0
- package/dist/lib/heatmap-navigation.js +49 -0
- package/dist/lib/scatter.d.ts +88 -0
- package/dist/lib/scatter.js +179 -0
- package/dist/lib/ticks.d.ts +27 -0
- package/dist/lib/ticks.js +67 -0
- package/dist/lib/tone.d.ts +36 -0
- package/dist/lib/tone.js +35 -0
- package/dist/lib/treemap.d.ts +0 -5
- package/dist/lib/treemap.js +1 -11
- package/dist/lib/use-element-width.d.ts +5 -0
- package/dist/lib/use-element-width.js +15 -0
- package/package.json +5 -1
- package/registry.lock.json +16 -0
- package/report.css +63 -47
- package/styles/siyahi.css +708 -55
package/styles/siyahi.css
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Siyahi theme: the only file in the package that holds colour values.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* shadcn/ui `neutral` greys for both schemes, with ink indigo (hue 275)
|
|
4
|
+
* placed, not spread: links, the selected sidebar item's text and the first
|
|
5
|
+
* chart series. Buttons, badges, toggles and focus stay monochrome, where
|
|
6
|
+
* black and white read strongest. Plus three meaning
|
|
7
|
+
* colours and a five-hue chart palette. `packages/tools/contrast.mjs` checks every value
|
|
5
8
|
* here in the build; when a check fails, change the value, never the bar.
|
|
6
9
|
*/
|
|
7
10
|
@import "tailwindcss";
|
|
@@ -31,6 +34,12 @@
|
|
|
31
34
|
* fallback draws and every prose block rewraps when Geist arrives.
|
|
32
35
|
*/
|
|
33
36
|
--max-width-prose: 43.1em;
|
|
37
|
+
/*
|
|
38
|
+
* 11px, for labels over figures: a stat tile's label and footer lines and a
|
|
39
|
+
* table's column heads, set as `font-mono text-2xs tracking-wider uppercase`.
|
|
40
|
+
*/
|
|
41
|
+
--text-2xs: 0.6875rem;
|
|
42
|
+
--text-2xs--line-height: 1rem;
|
|
34
43
|
--color-background: var(--background);
|
|
35
44
|
--color-foreground: var(--foreground);
|
|
36
45
|
--color-card: var(--card);
|
|
@@ -69,6 +78,7 @@
|
|
|
69
78
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
70
79
|
--color-sidebar-border: var(--sidebar-border);
|
|
71
80
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
81
|
+
--radius-xs: calc(var(--radius) * 0.3);
|
|
72
82
|
--radius-sm: calc(var(--radius) * 0.6);
|
|
73
83
|
--radius-md: calc(var(--radius) * 0.8);
|
|
74
84
|
--radius-lg: var(--radius);
|
|
@@ -80,8 +90,9 @@
|
|
|
80
90
|
|
|
81
91
|
:root {
|
|
82
92
|
color-scheme: light dark;
|
|
83
|
-
|
|
84
|
-
--
|
|
93
|
+
/* Square: panels, buttons, fields, badges and toggles alike. Only a dot, an avatar or a slider thumb stays round. */
|
|
94
|
+
--radius: 0rem;
|
|
95
|
+
--background: oklch(0.985 0 0);
|
|
85
96
|
--foreground: oklch(0.145 0 0);
|
|
86
97
|
--card: oklch(1 0 0);
|
|
87
98
|
--card-foreground: oklch(0.145 0 0);
|
|
@@ -92,83 +103,137 @@
|
|
|
92
103
|
--secondary: oklch(0.97 0 0);
|
|
93
104
|
--secondary-foreground: oklch(0.205 0 0);
|
|
94
105
|
--muted: oklch(0.97 0 0);
|
|
95
|
-
|
|
106
|
+
/* Below shadcn's 0.556, so muted text keeps 4.5:1 on the muted fill and on every Callout's tint. */
|
|
107
|
+
--muted-foreground: oklch(0.51 0 0);
|
|
96
108
|
--accent: oklch(0.97 0 0);
|
|
97
109
|
--accent-foreground: oklch(0.205 0 0);
|
|
98
|
-
|
|
99
|
-
--
|
|
100
|
-
|
|
101
|
-
--
|
|
102
|
-
/*
|
|
103
|
-
--
|
|
110
|
+
/* Deeper than the loss red, so a destructive button's label reads on its own red tint, hovered too. */
|
|
111
|
+
--destructive: oklch(0.49 0.19 27.325);
|
|
112
|
+
/* A step firmer than shadcn's 0.922 grey: with square, filled panels the hairlines carry the page's structure. */
|
|
113
|
+
--border: oklch(0.145 0 0 / 18%);
|
|
114
|
+
/* The edge of a field (input, textarea, select, input group) at 3:1: --border stays faint for dividers. */
|
|
115
|
+
--input: oklch(0.64 0 0);
|
|
116
|
+
/* Focus is the text colour, as black reads strongest: a field's edge thickens in it, any other control takes a halo. */
|
|
117
|
+
--ring: oklch(0.145 0 0);
|
|
118
|
+
/* Tailwind green-700, red-600, amber-700, with their chroma lowered to the sRGB edge. */
|
|
119
|
+
--positive: oklch(0.527 0.145 150.069);
|
|
104
120
|
--positive-foreground: oklch(0.985 0 0);
|
|
105
|
-
--negative: oklch(0.577 0.
|
|
121
|
+
--negative: oklch(0.577 0.235 27.325);
|
|
106
122
|
--negative-foreground: oklch(0.985 0 0);
|
|
107
|
-
--warning: oklch(0.555 0.
|
|
123
|
+
--warning: oklch(0.555 0.149 48.998);
|
|
108
124
|
--warning-foreground: oklch(0.985 0 0);
|
|
109
|
-
--link: oklch(0.546 0.245 262.881);
|
|
110
125
|
/*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
126
|
+
* Heat steps: how much of --positive or --negative each of five steps mixes
|
|
127
|
+
* into the card, for Treemap, ChangeGrid and a signed CalendarHeatmap
|
|
128
|
+
* (lib/heat.ts). The contrast check reads them from here.
|
|
129
|
+
*/
|
|
130
|
+
--heat-1: 12%;
|
|
131
|
+
--heat-2: 24%;
|
|
132
|
+
--heat-3: 36%;
|
|
133
|
+
--heat-4: 48%;
|
|
134
|
+
--heat-5: 60%;
|
|
135
|
+
/*
|
|
136
|
+
* An exception (a stat tile or a table row the page marks with a tone) takes this share of --negative or
|
|
137
|
+
* --warning over the card, and a tile an outline of the tone at --wash-edge.
|
|
138
|
+
*/
|
|
139
|
+
--wash: 7%;
|
|
140
|
+
--wash-edge: 45%;
|
|
141
|
+
/* An invalid field's wash: this share of --destructive over the card. */
|
|
142
|
+
--field-wash: 8%;
|
|
143
|
+
--link: oklch(0.51 0.2 275);
|
|
144
|
+
/*
|
|
145
|
+
* Indigo, amber, plum, sky, teal: no green or red, so no series reads as a
|
|
146
|
+
* gain or a loss. Each slot reaches 3:1 against the canvas, the card and the
|
|
147
|
+
* slots next to it, which only lightness can give, so indigo, plum and teal
|
|
148
|
+
* are the deep members and amber and sky the pale ones, in both schemes: a
|
|
149
|
+
* pale indigo in dark read as lavender and lost the accent. Slots two apart
|
|
114
150
|
* share a lightness and are told apart by hue; series take slots in order.
|
|
115
151
|
* Every chart value is inside sRGB, so the browser paints what the contrast
|
|
116
152
|
* check scored.
|
|
117
153
|
*/
|
|
118
|
-
--chart-1: oklch(0.
|
|
119
|
-
--chart-2: oklch(0.
|
|
120
|
-
--chart-3: oklch(0.
|
|
121
|
-
--chart-4: oklch(0.
|
|
122
|
-
--chart-5: oklch(0.
|
|
154
|
+
--chart-1: oklch(0.4 0.17 275);
|
|
155
|
+
--chart-2: oklch(0.66 0.13 70);
|
|
156
|
+
--chart-3: oklch(0.38 0.13 335);
|
|
157
|
+
--chart-4: oklch(0.65 0.11 235);
|
|
158
|
+
--chart-5: oklch(0.36 0.06 205);
|
|
123
159
|
--sidebar: oklch(0.985 0 0);
|
|
124
160
|
--sidebar-foreground: oklch(0.145 0 0);
|
|
125
161
|
--sidebar-primary: oklch(0.205 0 0);
|
|
126
162
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
127
163
|
--sidebar-accent: oklch(0.97 0 0);
|
|
128
|
-
--sidebar-accent-foreground: oklch(0.
|
|
129
|
-
--sidebar-border: oklch(0.
|
|
130
|
-
--sidebar-ring: oklch(0.
|
|
164
|
+
--sidebar-accent-foreground: oklch(0.45 0.2 275);
|
|
165
|
+
--sidebar-border: oklch(0.145 0 0 / 18%);
|
|
166
|
+
--sidebar-ring: oklch(0.145 0 0);
|
|
131
167
|
|
|
132
168
|
@variant dark {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
169
|
+
/*
|
|
170
|
+
* The canvas lifts just off black and text sits a step below white:
|
|
171
|
+
* near-white on near-black (19:1) blurs for many readers, and with the
|
|
172
|
+
* cards' grey gone on a ruled page the canvas is what text sits on.
|
|
173
|
+
* Claude Code (#181818, about #e8e8e8 text) and T3 Code (#0a0a0a, about
|
|
174
|
+
* #bebebe) both narrow that gap; this sits between them, at 14.9:1.
|
|
175
|
+
* Cards sit one step above the canvas, so data reads as raised, as the
|
|
176
|
+
* white card does on the light canvas; the sidebar is a shade darker.
|
|
177
|
+
*/
|
|
178
|
+
--background: oklch(0.14 0 0);
|
|
179
|
+
--foreground: oklch(0.922 0 0);
|
|
180
|
+
--card: oklch(0.215 0 0);
|
|
181
|
+
--card-foreground: oklch(0.922 0 0);
|
|
182
|
+
--popover: oklch(0.215 0 0);
|
|
183
|
+
--popover-foreground: oklch(0.922 0 0);
|
|
139
184
|
--primary: oklch(0.922 0 0);
|
|
140
185
|
--primary-foreground: oklch(0.205 0 0);
|
|
141
186
|
--secondary: oklch(0.269 0 0);
|
|
142
|
-
--secondary-foreground: oklch(0.
|
|
187
|
+
--secondary-foreground: oklch(0.922 0 0);
|
|
143
188
|
--muted: oklch(0.269 0 0);
|
|
144
|
-
|
|
189
|
+
/* A step above shadcn's 0.708: subtitles, footnotes and table labels at data density read too dim. */
|
|
190
|
+
--muted-foreground: oklch(0.74 0 0);
|
|
145
191
|
--accent: oklch(0.269 0 0);
|
|
146
|
-
--accent-foreground: oklch(0.
|
|
147
|
-
|
|
148
|
-
--
|
|
149
|
-
--
|
|
150
|
-
--
|
|
151
|
-
|
|
152
|
-
|
|
192
|
+
--accent-foreground: oklch(0.922 0 0);
|
|
193
|
+
/* Paler and calmer than the loss red, so a destructive label reads on its red tint over the card, hovered too. */
|
|
194
|
+
--destructive: oklch(0.8 0.1 22.216);
|
|
195
|
+
--border: oklch(1 0 0 / 20%);
|
|
196
|
+
--input: oklch(0.62 0 0);
|
|
197
|
+
--ring: oklch(0.922 0 0);
|
|
198
|
+
/*
|
|
199
|
+
* Green toned down from Tailwind green-400, below red-400's lightness so a
|
|
200
|
+
* treemap's deepest green tile keeps its light label at 4.5:1; amber-400.
|
|
201
|
+
* Red, amber and the link sit at the sRGB edge.
|
|
202
|
+
*/
|
|
203
|
+
--positive: oklch(0.69 0.16 151.711);
|
|
153
204
|
--positive-foreground: oklch(0.145 0 0);
|
|
154
|
-
--negative: oklch(0.704 0.
|
|
205
|
+
--negative: oklch(0.704 0.187 22.216);
|
|
155
206
|
--negative-foreground: oklch(0.145 0 0);
|
|
156
|
-
--warning: oklch(0.828 0.
|
|
207
|
+
--warning: oklch(0.828 0.169 84.429);
|
|
157
208
|
--warning-foreground: oklch(0.145 0 0);
|
|
158
|
-
|
|
159
|
-
--
|
|
160
|
-
--
|
|
161
|
-
--
|
|
162
|
-
--
|
|
163
|
-
--
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
209
|
+
/* A 12% tint of a dark card reads as the card itself, so dark starts at 25%. */
|
|
210
|
+
--heat-1: 25%;
|
|
211
|
+
--heat-2: 33.75%;
|
|
212
|
+
--heat-3: 42.5%;
|
|
213
|
+
--heat-4: 51.25%;
|
|
214
|
+
--heat-5: 60%;
|
|
215
|
+
/*
|
|
216
|
+
* As with heat, a faint share of a dark card reads as the card itself; above
|
|
217
|
+
* 12% the amber wash takes --chart-1, a tile's Sparkline or a row's DataBar,
|
|
218
|
+
* under 3:1.
|
|
219
|
+
*/
|
|
220
|
+
--wash: 12%;
|
|
221
|
+
--wash-edge: 55%;
|
|
222
|
+
--field-wash: 12%;
|
|
223
|
+
--link: oklch(0.75 0.128 275);
|
|
224
|
+
--chart-1: oklch(0.58 0.2 275);
|
|
225
|
+
--chart-2: oklch(0.88 0.08 75);
|
|
226
|
+
--chart-3: oklch(0.58 0.16 335);
|
|
227
|
+
--chart-4: oklch(0.88 0.05 235);
|
|
228
|
+
--chart-5: oklch(0.56 0.08 205);
|
|
229
|
+
--sidebar: oklch(0.145 0 0);
|
|
230
|
+
--sidebar-foreground: oklch(0.922 0 0);
|
|
231
|
+
--sidebar-primary: oklch(0.922 0 0);
|
|
232
|
+
--sidebar-primary-foreground: oklch(0.205 0 0);
|
|
168
233
|
--sidebar-accent: oklch(0.269 0 0);
|
|
169
|
-
--sidebar-accent-foreground: oklch(0.
|
|
170
|
-
--sidebar-border: oklch(1 0 0 /
|
|
171
|
-
--sidebar-ring: oklch(0.
|
|
234
|
+
--sidebar-accent-foreground: oklch(0.8 0.1 275);
|
|
235
|
+
--sidebar-border: oklch(1 0 0 / 20%);
|
|
236
|
+
--sidebar-ring: oklch(0.922 0 0);
|
|
172
237
|
}
|
|
173
238
|
}
|
|
174
239
|
|
|
@@ -195,6 +260,494 @@
|
|
|
195
260
|
}
|
|
196
261
|
}
|
|
197
262
|
|
|
263
|
+
/*
|
|
264
|
+
* Every selected toggle, a view switch or a filter chip alike, fills with the
|
|
265
|
+
* foreground, black on white and white on black, so the reader sees at once
|
|
266
|
+
* what is on; stock shadcn marks it with a faint grey. A default (pill) tab
|
|
267
|
+
* selects the same way: stock fills it with the canvas colour, 1.05:1 against
|
|
268
|
+
* its grey list. Unlayered, to outrank the stock aria-pressed:bg-muted.
|
|
269
|
+
*/
|
|
270
|
+
:is([data-slot="toggle"], [data-slot="toggle-group-item"]):is([aria-pressed="true"], [data-pressed]),
|
|
271
|
+
[data-slot="tabs-list"][data-variant="default"] [data-slot="tabs-trigger"][data-active] {
|
|
272
|
+
background-color: var(--foreground);
|
|
273
|
+
color: var(--background);
|
|
274
|
+
border-color: var(--foreground);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/*
|
|
278
|
+
* A toggle group's items sit flush and share their borders, one control with
|
|
279
|
+
* its parts side by side; stock spaces them 8px apart. A group set to
|
|
280
|
+
* spacing={0} already joins its items by dropping their inner borders. The
|
|
281
|
+
* pressed item rises over its neighbours, so its own edge shows on both sides.
|
|
282
|
+
*/
|
|
283
|
+
[data-slot="toggle-group"]:not([data-spacing="0"]) {
|
|
284
|
+
gap: 0;
|
|
285
|
+
> [data-slot="toggle-group-item"] {
|
|
286
|
+
&[aria-pressed="true"],
|
|
287
|
+
&[data-pressed] {
|
|
288
|
+
z-index: 1;
|
|
289
|
+
}
|
|
290
|
+
&[data-variant="outline"] + [data-slot="toggle-group-item"] {
|
|
291
|
+
margin-inline-start: -1px;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
&[data-orientation="vertical"]
|
|
295
|
+
> [data-slot="toggle-group-item"][data-variant="outline"]
|
|
296
|
+
+ [data-slot="toggle-group-item"] {
|
|
297
|
+
margin-inline-start: 0;
|
|
298
|
+
margin-block-start: -1px;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/*
|
|
303
|
+
* A component that lifts a part of itself with a z-index is a stacking context
|
|
304
|
+
* of its own: SectionCards' picker button, an avatar's badge, a calendar's
|
|
305
|
+
* days, a focused toggle, a code block's buttons, a price chart's layers. The
|
|
306
|
+
* lifted part then ranks only against the rest of its component, and can never
|
|
307
|
+
* sit over, or take a tap from, a page's sticky header or section strip (also
|
|
308
|
+
* z-10, where the later element in the page won the tie). In a group, the
|
|
309
|
+
* group is the context, so a badge still sits over the next, overlapping
|
|
310
|
+
* avatar. The data table's scroll container isolates itself.
|
|
311
|
+
*/
|
|
312
|
+
:is(
|
|
313
|
+
[data-slot="card"],
|
|
314
|
+
[data-slot="avatar-group"],
|
|
315
|
+
[data-slot="avatar"]:not([data-slot="avatar-group"] > *),
|
|
316
|
+
[data-slot="calendar"],
|
|
317
|
+
[data-slot="toggle-group"],
|
|
318
|
+
[data-slot="code-block"],
|
|
319
|
+
[data-slot="price-chart"]
|
|
320
|
+
) {
|
|
321
|
+
isolation: isolate;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/*
|
|
325
|
+
* A copy button sits in a row above the code, never over it. Core floats a
|
|
326
|
+
* CodeBlockActions written straight in the block over the code's top right
|
|
327
|
+
* (z-10, shown on hover), where it covers the end of a line and painted over
|
|
328
|
+
* the page's sticky section strip; here it takes a row of its own, as in a
|
|
329
|
+
* CodeBlockHeader, always shown, with the header's plain button.
|
|
330
|
+
*/
|
|
331
|
+
[data-slot="code-block"] > [data-slot="code-block-actions"] {
|
|
332
|
+
position: static;
|
|
333
|
+
align-self: flex-end;
|
|
334
|
+
margin: 0.5rem 0.5rem 0;
|
|
335
|
+
opacity: 1;
|
|
336
|
+
}
|
|
337
|
+
/*
|
|
338
|
+
* Core's floating fill and faint resting ring go. Its fill outranks the ghost
|
|
339
|
+
* button's own hover fill, so that is set again here, as a header's button
|
|
340
|
+
* shows it; the focus ring is left alone.
|
|
341
|
+
*/
|
|
342
|
+
[data-slot="code-block"] > [data-slot="code-block-actions"] > button {
|
|
343
|
+
background-color: transparent;
|
|
344
|
+
&:hover {
|
|
345
|
+
background-color: var(--muted);
|
|
346
|
+
@variant dark {
|
|
347
|
+
background-color: color-mix(in oklab, var(--muted) 50%, transparent);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
&:not(:focus-visible) {
|
|
351
|
+
--tw-ring-shadow: 0 0 #0000;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/*
|
|
356
|
+
* --input is the edge of a field, where the reader types or picks. A button,
|
|
357
|
+
* a toggle and a code block are framed by their label or their content, so
|
|
358
|
+
* their outline stays the faint divider colour; scoping the variable keeps
|
|
359
|
+
* the fill each derives from it faint too. A button is matched by its own
|
|
360
|
+
* group/button class: rendered as a popover or menu trigger, it takes that
|
|
361
|
+
* trigger's data-slot instead of "button".
|
|
362
|
+
*/
|
|
363
|
+
:is([class~="group/button"], [data-slot="toggle"], [data-slot="toggle-group-item"], [data-slot="code-block"]) {
|
|
364
|
+
--input: var(--border);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/*
|
|
368
|
+
* The command palette's search field is a field: the --input edge on a muted
|
|
369
|
+
* fill, and the muted placeholder every other field uses (stock leaves it at
|
|
370
|
+
* Tailwind's half-strength text, 3.7:1).
|
|
371
|
+
*/
|
|
372
|
+
[data-slot="command-input-wrapper"] > [data-slot="input-group"] {
|
|
373
|
+
border-color: var(--input);
|
|
374
|
+
background-color: color-mix(in oklab, var(--muted) 50%, transparent);
|
|
375
|
+
}
|
|
376
|
+
[data-slot="command-input"]::placeholder {
|
|
377
|
+
color: var(--muted-foreground);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/*
|
|
381
|
+
* Focus is drawn inside the control, never outside it, so no neighbour and no
|
|
382
|
+
* box that clips can cover it: a 2px ring in the text colour (--ring) on the
|
|
383
|
+
* control's edge, and a 2px ring of the page colour just inside that, so the
|
|
384
|
+
* pair reads on any fill, black, white or hovered, and the outer ring meets
|
|
385
|
+
* the page around the control at the text's contrast. A field shows focus as
|
|
386
|
+
* its own edge, 2px in the text colour; an input group and the command search
|
|
387
|
+
* show it round the whole group. A chart mark is drawn in SVG, where these
|
|
388
|
+
* rings do not paint: it takes a dashed stroke in the text colour, apart from a
|
|
389
|
+
* picked mark's solid one. Menu items and options show focus as their
|
|
390
|
+
* highlight, and popups as themselves, so they take none.
|
|
391
|
+
*/
|
|
392
|
+
:focus-visible:not(
|
|
393
|
+
svg,
|
|
394
|
+
svg *,
|
|
395
|
+
[role="menuitem"],
|
|
396
|
+
[role="menuitemcheckbox"],
|
|
397
|
+
[role="menuitemradio"],
|
|
398
|
+
[role="option"],
|
|
399
|
+
[role="dialog"],
|
|
400
|
+
[role="alertdialog"],
|
|
401
|
+
[role="menu"],
|
|
402
|
+
[role="listbox"],
|
|
403
|
+
[data-slot="input"],
|
|
404
|
+
[data-slot="textarea"],
|
|
405
|
+
[data-slot="select-trigger"],
|
|
406
|
+
[data-slot="input-group-control"],
|
|
407
|
+
[data-slot="command-input"]
|
|
408
|
+
),
|
|
409
|
+
[data-slot="slider-thumb"]:has(input:focus-visible) {
|
|
410
|
+
outline: none;
|
|
411
|
+
box-shadow:
|
|
412
|
+
inset 0 0 0 2px var(--ring),
|
|
413
|
+
inset 0 0 0 4px var(--background);
|
|
414
|
+
}
|
|
415
|
+
:is([data-slot="input"], [data-slot="textarea"], [data-slot="select-trigger"]):focus-visible,
|
|
416
|
+
[data-slot="input-group"]:has(:is([data-slot="input-group-control"], [data-slot="command-input"]):focus-visible) {
|
|
417
|
+
outline: none;
|
|
418
|
+
border-color: var(--ring);
|
|
419
|
+
box-shadow: inset 0 0 0 1px var(--ring);
|
|
420
|
+
}
|
|
421
|
+
:is([data-slot="input"], [data-slot="textarea"], [data-slot="select-trigger"])[aria-invalid="true"]:focus-visible,
|
|
422
|
+
[data-slot="input-group"]:has([data-slot="input-group-control"][aria-invalid="true"]:focus-visible) {
|
|
423
|
+
border-color: var(--destructive);
|
|
424
|
+
box-shadow: inset 0 0 0 1px var(--destructive);
|
|
425
|
+
}
|
|
426
|
+
[class~="group/button"][class~="text-destructive"]:focus-visible {
|
|
427
|
+
box-shadow:
|
|
428
|
+
inset 0 0 0 2px var(--destructive),
|
|
429
|
+
inset 0 0 0 4px var(--background);
|
|
430
|
+
}
|
|
431
|
+
/* A code block shows its scrolling content's focus itself; its own frame keeps no halo. */
|
|
432
|
+
[data-slot="code-block"]:has(> [data-slot="code-block-content"]:focus-visible) {
|
|
433
|
+
--tw-ring-shadow: 0 0 #0000;
|
|
434
|
+
}
|
|
435
|
+
svg :is(rect, circle, path)[tabindex]:focus-visible,
|
|
436
|
+
[data-slot="treemap-tile"]:focus-visible > rect:first-child {
|
|
437
|
+
outline: none;
|
|
438
|
+
stroke: var(--ring);
|
|
439
|
+
stroke-width: 2px;
|
|
440
|
+
stroke-dasharray: 3 2;
|
|
441
|
+
}
|
|
442
|
+
/* A treemap tile clips at its own edge, so half its stroke shows: twice the width keeps 2px. */
|
|
443
|
+
[data-slot="treemap-tile"]:focus-visible > rect:first-child {
|
|
444
|
+
stroke-width: 4px;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/*
|
|
448
|
+
* A destructive control focuses, and an invalid field is edged, in the full
|
|
449
|
+
* destructive colour; stock fades both to 40-50%, under 3:1.
|
|
450
|
+
*/
|
|
451
|
+
[class~="group/button"][class~="text-destructive"]:focus-visible,
|
|
452
|
+
[data-slot="code-block"][data-variant="destructive"]:has(> [data-slot="code-block-content"]:focus-visible),
|
|
453
|
+
:is(
|
|
454
|
+
[data-slot="input"],
|
|
455
|
+
[data-slot="textarea"],
|
|
456
|
+
[data-slot="select-trigger"],
|
|
457
|
+
[class~="group/button"]
|
|
458
|
+
)[aria-invalid="true"],
|
|
459
|
+
[data-slot="input-group"]:has([data-slot][aria-invalid="true"]) {
|
|
460
|
+
border-color: var(--destructive);
|
|
461
|
+
}
|
|
462
|
+
/* An invalid field also takes a faint wash of the red, so it reads as the one to fix before its message is read. */
|
|
463
|
+
:is([data-slot="input"], [data-slot="textarea"], [data-slot="select-trigger"])[aria-invalid="true"],
|
|
464
|
+
[data-slot="input-group"]:has([data-slot][aria-invalid="true"]) {
|
|
465
|
+
background-color: color-mix(in oklab, var(--destructive) var(--field-wash), var(--card));
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/*
|
|
469
|
+
* A field's label sits above it in small monospaced capitals, muted, as the
|
|
470
|
+
* labels over figures do: FieldLabel in a Field (directly or in its
|
|
471
|
+
* FieldContent), the home a page gets it from. A label for a checkbox, a
|
|
472
|
+
* switch or a radio reads as a sentence and keeps the sans face, whatever the
|
|
473
|
+
* Field's orientation.
|
|
474
|
+
*/
|
|
475
|
+
[data-slot="field"]:not(:has([role="checkbox"], [role="switch"], [role="radio"])) [data-slot="field-label"] {
|
|
476
|
+
@apply font-mono text-2xs tracking-wider text-muted-foreground uppercase;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/* Progress is a bar, square as every bar is; stock draws it as a pill. */
|
|
480
|
+
[data-slot="progress-track"] {
|
|
481
|
+
border-radius: 0;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/*
|
|
485
|
+
* A slider's unfilled track is outlined in the field edge, so its extent reads
|
|
486
|
+
* at 3:1; the muted track alone is 1.05:1 on the canvas.
|
|
487
|
+
*/
|
|
488
|
+
[data-slot="slider-track"] {
|
|
489
|
+
box-shadow: inset 0 0 0 1px var(--input);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/*
|
|
493
|
+
* A number column (meta.variant "number") right-aligns its head and cells, so
|
|
494
|
+
* figures line up by their units down the column, as a ledger reads.
|
|
495
|
+
*/
|
|
496
|
+
:is([data-slot="table-head"], [data-slot="table-cell"])[data-numeric] {
|
|
497
|
+
text-align: right;
|
|
498
|
+
}
|
|
499
|
+
[data-slot="table-head"][data-numeric] > *,
|
|
500
|
+
[data-slot="table-cell"][data-numeric] > :is(div, span)[class*="flex"] {
|
|
501
|
+
justify-content: flex-end;
|
|
502
|
+
margin-inline-start: auto;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/*
|
|
506
|
+
* Column heads are labels, in small monospaced capitals; a sort header's
|
|
507
|
+
* button and a data table's column title take the head's type rather than
|
|
508
|
+
* their own. A total row reads as a
|
|
509
|
+
* ledger's: one rule above it and a double rule under it in the text colour,
|
|
510
|
+
* with no band.
|
|
511
|
+
*/
|
|
512
|
+
[data-slot="table-head"] {
|
|
513
|
+
@apply font-mono text-2xs tracking-wider uppercase;
|
|
514
|
+
:is([class~="group/button"], [data-slot="column-title"]) {
|
|
515
|
+
font: inherit;
|
|
516
|
+
letter-spacing: inherit;
|
|
517
|
+
text-transform: inherit;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
/*
|
|
521
|
+
* Tables are dense, as a ledger is: compact rows, a rule in the text colour
|
|
522
|
+
* under the column heads, the first column (what each row is) in medium
|
|
523
|
+
* weight, and figures in the mono face, a size down so a column of them takes
|
|
524
|
+
* no more room than the sans did. A figure is a cell marked data-numeric (the
|
|
525
|
+
* data table's number columns, or a plain TableCell the page marks), which
|
|
526
|
+
* also right-aligns it, and any Delta in a cell. Alignment alone says nothing:
|
|
527
|
+
* a right-aligned word stays in the sans. Row hairlines stay; no stripes, no
|
|
528
|
+
* column rules.
|
|
529
|
+
*/
|
|
530
|
+
[data-slot="table-head"] {
|
|
531
|
+
height: 1.75rem;
|
|
532
|
+
}
|
|
533
|
+
[data-slot="table-cell"] {
|
|
534
|
+
padding-block: 0.2rem;
|
|
535
|
+
}
|
|
536
|
+
[data-slot="table-header"] [data-slot="table-row"] {
|
|
537
|
+
border-bottom-color: var(--foreground);
|
|
538
|
+
}
|
|
539
|
+
/* A data table's sticky header draws its rule as a line of its own, which stays when the rows scroll under it. */
|
|
540
|
+
[data-slot="table-header"]::after {
|
|
541
|
+
background-color: var(--foreground);
|
|
542
|
+
}
|
|
543
|
+
[data-slot="table-body"] [data-slot="table-cell"]:first-child {
|
|
544
|
+
font-weight: 500;
|
|
545
|
+
}
|
|
546
|
+
:is([data-slot="table-cell"][data-numeric], [data-slot="table-cell"] [data-slot="delta"]) {
|
|
547
|
+
@apply font-mono;
|
|
548
|
+
font-size: 0.8125rem;
|
|
549
|
+
}
|
|
550
|
+
[data-slot="table-footer"] {
|
|
551
|
+
background: none;
|
|
552
|
+
border-top: 1px solid var(--foreground);
|
|
553
|
+
border-bottom: 3px double var(--foreground);
|
|
554
|
+
font-weight: 600;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/*
|
|
558
|
+
* An exception the page marks, a stat tile (SectionCard's tone) or a table
|
|
559
|
+
* row (<TableRow data-tone="negative">), takes a faint wash of its colour over
|
|
560
|
+
* the card, and a tile an outline of it, so the one figure needing attention
|
|
561
|
+
* stands out of a run of neutral ones. Unlayered, to outrank a row's hover band.
|
|
562
|
+
*/
|
|
563
|
+
:is([data-slot="card"], [data-slot="table-row"]) {
|
|
564
|
+
&[data-tone="negative"] {
|
|
565
|
+
--exception: var(--negative);
|
|
566
|
+
}
|
|
567
|
+
&[data-tone="warning"] {
|
|
568
|
+
--exception: var(--warning);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
[data-slot="card"]:is([data-tone="negative"], [data-tone="warning"]) {
|
|
572
|
+
background-color: color-mix(in oklab, var(--exception) var(--wash), var(--card));
|
|
573
|
+
--tw-ring-color: color-mix(in oklab, var(--exception) var(--wash-edge), transparent);
|
|
574
|
+
}
|
|
575
|
+
[data-slot="table-row"]:is([data-tone="negative"], [data-tone="warning"]) {
|
|
576
|
+
background-color: color-mix(in oklab, var(--exception) var(--wash), transparent);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/*
|
|
580
|
+
* Panels in a PanelGrid are numbered 01, 02 before their title, in the mono
|
|
581
|
+
* face and the link colour, so the text can point to one. A panel without a
|
|
582
|
+
* card title, such as a MetricStrip with none, takes no number; a block
|
|
583
|
+
* wrapped in its own SiyahiProvider (which draws no box) still counts. The panel
|
|
584
|
+
* counts, not the title: a card header is a size container, whose style
|
|
585
|
+
* containment would keep each title's count to itself.
|
|
586
|
+
*/
|
|
587
|
+
[data-slot="panel-grid"] {
|
|
588
|
+
counter-reset: panel;
|
|
589
|
+
}
|
|
590
|
+
[data-slot="panel"]:has(
|
|
591
|
+
> [data-slot="card"] > [data-slot="card-header"] > [data-slot="card-title"],
|
|
592
|
+
> [data-slot="siyahi-provider"] > [data-slot="card"] > [data-slot="card-header"] > [data-slot="card-title"]
|
|
593
|
+
) {
|
|
594
|
+
counter-increment: panel;
|
|
595
|
+
}
|
|
596
|
+
:is([data-slot="panel"], [data-slot="panel"] > [data-slot="siyahi-provider"])
|
|
597
|
+
> [data-slot="card"]
|
|
598
|
+
> [data-slot="card-header"]
|
|
599
|
+
> [data-slot="card-title"]::before {
|
|
600
|
+
content: counter(panel, decimal-leading-zero);
|
|
601
|
+
@apply font-mono text-2xs font-medium text-link;
|
|
602
|
+
letter-spacing: 0.04em;
|
|
603
|
+
margin-inline-end: 0.7em;
|
|
604
|
+
position: relative;
|
|
605
|
+
top: -0.12em;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/*
|
|
609
|
+
* A data table inside a card drops its own border: the card's ring or the
|
|
610
|
+
* ruled page's hairlines already frame it. On an open page, with no frame,
|
|
611
|
+
* it keeps its border.
|
|
612
|
+
*/
|
|
613
|
+
@scope (:root, [data-surface="ruled"], [data-surface="card"]) to ([data-surface="open"]) {
|
|
614
|
+
[data-slot="card"] [data-slot="table-container"] {
|
|
615
|
+
border-color: transparent;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/*
|
|
620
|
+
* A series in a forecast is drawn twice (LineChartCard, AreaChartCard): solid
|
|
621
|
+
* up to forecastFrom and dashed after it, each copy clipped to its side by the
|
|
622
|
+
* clip paths lib/chart-marks draws. The chart sets the two clip paths' urls.
|
|
623
|
+
*/
|
|
624
|
+
[data-slot="chart"] .chart-actual {
|
|
625
|
+
clip-path: var(--chart-actual);
|
|
626
|
+
}
|
|
627
|
+
[data-slot="chart"] .chart-forecast {
|
|
628
|
+
clip-path: var(--chart-forecast);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/*
|
|
632
|
+
* Pen-stroke motion: a chart's lines draw in left to right over 1.3s, and a
|
|
633
|
+
* stack's fills are revealed with them (Recharts' own animation, which reduced
|
|
634
|
+
* motion turns off). Then the names at the line ends fade in; a line's end
|
|
635
|
+
* dots appear once it has drawn and fade in then; bars, with no line to wait
|
|
636
|
+
* for, fade in at once rather than grow. The names are Siyahi's own element,
|
|
637
|
+
* kept across data updates, so they fade in once. Reduced motion shows them
|
|
638
|
+
* all at once.
|
|
639
|
+
*/
|
|
640
|
+
@keyframes chart-fade-in {
|
|
641
|
+
from {
|
|
642
|
+
opacity: 0;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
[data-slot="chart"] :is(.recharts-bar-rectangles, .recharts-line-dots, .recharts-area-dots) {
|
|
646
|
+
animation: chart-fade-in 0.6s ease-out backwards;
|
|
647
|
+
}
|
|
648
|
+
[data-slot="chart"] [data-slot="end-labels"] {
|
|
649
|
+
animation: chart-fade-in 0.6s ease-out 1.3s backwards;
|
|
650
|
+
}
|
|
651
|
+
@media (prefers-reduced-motion: reduce) {
|
|
652
|
+
[data-slot="chart"]
|
|
653
|
+
:is(.recharts-bar-rectangles, .recharts-line-dots, .recharts-area-dots, [data-slot="end-labels"]) {
|
|
654
|
+
animation: none;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/*
|
|
659
|
+
* A block the page marks stale (stale on a chart card or a SectionCards tile)
|
|
660
|
+
* loses its colour: its chart, sparkline and badge turn grey, and a tile's
|
|
661
|
+
* figure and badge turn muted. The chart and sparkline also draw at 65%, so a
|
|
662
|
+
* dark slot's line does not read heavier than a fresh one: a stale block is
|
|
663
|
+
* meant to be harder to read, so its marks are exempt from the 3:1 mark bar.
|
|
664
|
+
* The small "Stale · 3 Nov" note under the title and the figure keep their
|
|
665
|
+
* text contrast.
|
|
666
|
+
*/
|
|
667
|
+
[data-slot="card"][data-stale] :is([data-slot="chart"], [data-slot="sparkline"], [data-slot="badge"]) {
|
|
668
|
+
filter: grayscale(1);
|
|
669
|
+
}
|
|
670
|
+
[data-slot="card"][data-stale] :is([data-slot="chart"], [data-slot="sparkline"]) {
|
|
671
|
+
opacity: 0.65;
|
|
672
|
+
}
|
|
673
|
+
[data-slot="section-card"] [data-slot="card"][data-stale] :is([data-slot="card-title"], [data-slot="badge"]) {
|
|
674
|
+
color: var(--muted-foreground);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/*
|
|
678
|
+
* Chart gridlines take the full border colour, solid: stock shadcn draws them
|
|
679
|
+
* dashed at half of it, which all but vanishes on a dark card (5% white).
|
|
680
|
+
*/
|
|
681
|
+
[data-slot="chart"] .recharts-cartesian-grid line {
|
|
682
|
+
stroke: var(--border);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/*
|
|
686
|
+
* The sidebar is parted from the page by a hairline on its page-side edge,
|
|
687
|
+
* its group labels are small monospaced capitals, as every label over content
|
|
688
|
+
* is, and its rows are compact. Fill, icons and the current-page mark stay as
|
|
689
|
+
* shadcn draws them.
|
|
690
|
+
*/
|
|
691
|
+
/* shadcn edges a collapsible sidebar on its page side already; a non-collapsible one, set in the page's flow, has none. */
|
|
692
|
+
div[data-slot="sidebar"]:not([data-side], [data-mobile]) {
|
|
693
|
+
border-inline-end: 1px solid var(--sidebar-border);
|
|
694
|
+
}
|
|
695
|
+
[data-slot="sidebar-group-label"] {
|
|
696
|
+
@apply font-mono text-2xs tracking-wider uppercase;
|
|
697
|
+
}
|
|
698
|
+
[data-slot="sidebar-menu"] {
|
|
699
|
+
gap: 0;
|
|
700
|
+
}
|
|
701
|
+
/* A row that opens a group renders as its collapsible trigger, which takes that slot, so rows are found by data-sidebar. */
|
|
702
|
+
[data-sidebar="menu-button"]:not([data-size="lg"]) {
|
|
703
|
+
height: 1.75rem;
|
|
704
|
+
}
|
|
705
|
+
[data-slot="sidebar-menu-sub"] {
|
|
706
|
+
gap: 0;
|
|
707
|
+
padding-block: 0.125rem;
|
|
708
|
+
}
|
|
709
|
+
[data-sidebar="menu-sub-button"] {
|
|
710
|
+
height: 1.5rem;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/*
|
|
714
|
+
* The selected sub-page marks its stretch of the sidebar's rail in the accent,
|
|
715
|
+
* so the current place reads at a glance in a long list, as a docs index does.
|
|
716
|
+
*/
|
|
717
|
+
[data-slot="sidebar-menu-sub-item"]:has(> [data-active])::before {
|
|
718
|
+
content: "";
|
|
719
|
+
position: absolute;
|
|
720
|
+
inset-block: 2px;
|
|
721
|
+
left: -11px;
|
|
722
|
+
width: 2px;
|
|
723
|
+
border-radius: 1px;
|
|
724
|
+
background: var(--sidebar-accent-foreground);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/* Touch targets follow input capability, including touch laptops. */
|
|
728
|
+
@media (pointer: coarse) {
|
|
729
|
+
:is(
|
|
730
|
+
[class~="group/button"],
|
|
731
|
+
[data-slot="toggle"],
|
|
732
|
+
[data-slot="toggle-group-item"],
|
|
733
|
+
[data-slot="select-trigger"],
|
|
734
|
+
[data-slot="tabs-trigger"],
|
|
735
|
+
[data-slot="input-group-button"]
|
|
736
|
+
) {
|
|
737
|
+
min-height: --spacing(11);
|
|
738
|
+
min-width: --spacing(11);
|
|
739
|
+
}
|
|
740
|
+
:is([data-slot="input"], [data-slot="textarea"], [data-slot="input-group"], [data-slot="input-group-control"]) {
|
|
741
|
+
min-height: --spacing(11);
|
|
742
|
+
}
|
|
743
|
+
:is([data-slot="input"], [data-slot="textarea"], [data-slot="input-group-control"], [data-slot="command-input"]) {
|
|
744
|
+
font-size: 1rem;
|
|
745
|
+
}
|
|
746
|
+
[role="search"] > [data-slot="input"] {
|
|
747
|
+
padding-right: --spacing(14);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
198
751
|
/*
|
|
199
752
|
* On a narrow card a header action (a toggle, a button) drops under the title
|
|
200
753
|
* and description, left-aligned. Stock CardHeader keeps the action in its own
|
|
@@ -215,3 +768,103 @@
|
|
|
215
768
|
}
|
|
216
769
|
}
|
|
217
770
|
}
|
|
771
|
+
|
|
772
|
+
/*
|
|
773
|
+
* Surfaces, from SiyahiProvider's surface prop. Ruled, the default, keeps
|
|
774
|
+
* every card's fill and drops its ring: PanelGrid and SectionCards fill their
|
|
775
|
+
* grid with the card colour and draw hairlines between the blocks, with a rule
|
|
776
|
+
* above and below the group and none on its left and right edges, and a card
|
|
777
|
+
* that stands alone, such as a MetricStrip or a table's card, takes those
|
|
778
|
+
* rules above and below itself. Card is a filled square card with a hairline
|
|
779
|
+
* ring and no shadow, 8px from the next, for a page of separate blocks. Open
|
|
780
|
+
* takes the box off every core Card: no fill, no ring, no tinted footer band,
|
|
781
|
+
* and no padding, so a block's content sits level with the page's text. Each
|
|
782
|
+
* scope stops at a provider that says otherwise. Unlayered, like the rules
|
|
783
|
+
* above, to outrank the core components' utility classes; a picked
|
|
784
|
+
* SectionCards tile keeps its ring, which is important, and an exception
|
|
785
|
+
* keeps its outline, which is more specific.
|
|
786
|
+
*/
|
|
787
|
+
[data-slot="card"] {
|
|
788
|
+
--tw-ring-color: var(--border);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
@scope (:root, [data-surface="ruled"]) to ([data-surface="card"], [data-surface="open"]) {
|
|
792
|
+
[data-slot="card"] {
|
|
793
|
+
--tw-ring-color: transparent;
|
|
794
|
+
}
|
|
795
|
+
/*
|
|
796
|
+
* A card inside a panel or a tile's cell, or nested in another card, is ruled by what holds it; only a holder
|
|
797
|
+
* inside this scope counts, so a ruled provider inside a card starts its own lone cards afresh.
|
|
798
|
+
*/
|
|
799
|
+
[data-slot="card"]:not(
|
|
800
|
+
:scope :is([data-slot="panel"], [data-slot="section-card"], [data-slot="card"]) [data-slot="card"]
|
|
801
|
+
) {
|
|
802
|
+
border-block: 1px solid var(--border);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
@scope ([data-surface="open"]) to ([data-surface="ruled"], [data-surface="card"]) {
|
|
807
|
+
[data-slot="card"] {
|
|
808
|
+
background: none;
|
|
809
|
+
--tw-ring-color: transparent;
|
|
810
|
+
--card-spacing: 0px;
|
|
811
|
+
gap: calc(var(--spacing) * 4);
|
|
812
|
+
}
|
|
813
|
+
[data-slot="card-footer"] {
|
|
814
|
+
background: none;
|
|
815
|
+
border-top-width: 0;
|
|
816
|
+
/* The band's own top padding stacked on the card's gap, and with no band it read as a hole under the figure. */
|
|
817
|
+
padding-top: 0;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/*
|
|
822
|
+
* A table, or a page's section strip, that scrolls sideways fades at the edge
|
|
823
|
+
* with more behind it,
|
|
824
|
+
* so a phone reader sees there is more without a visible scrollbar. The fade
|
|
825
|
+
* follows the scroll position; a table that fits has no timeline and no fade.
|
|
826
|
+
*/
|
|
827
|
+
@property --table-fade-start {
|
|
828
|
+
syntax: "<length>";
|
|
829
|
+
inherits: false;
|
|
830
|
+
initial-value: 0px;
|
|
831
|
+
}
|
|
832
|
+
@property --table-fade-end {
|
|
833
|
+
syntax: "<length>";
|
|
834
|
+
inherits: false;
|
|
835
|
+
initial-value: 0px;
|
|
836
|
+
}
|
|
837
|
+
@keyframes table-edge-fade {
|
|
838
|
+
0% {
|
|
839
|
+
--table-fade-start: 0px;
|
|
840
|
+
--table-fade-end: 2rem;
|
|
841
|
+
}
|
|
842
|
+
8%,
|
|
843
|
+
92% {
|
|
844
|
+
--table-fade-start: 2rem;
|
|
845
|
+
--table-fade-end: 2rem;
|
|
846
|
+
}
|
|
847
|
+
100% {
|
|
848
|
+
--table-fade-start: 2rem;
|
|
849
|
+
--table-fade-end: 0px;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
@layer base {
|
|
853
|
+
:is([data-slot="table-container"], [data-slot="page-toc"] > div) {
|
|
854
|
+
mask-image: linear-gradient(
|
|
855
|
+
to right,
|
|
856
|
+
rgb(0 0 0 / 0.2),
|
|
857
|
+
#000 var(--table-fade-start),
|
|
858
|
+
#000 calc(100% - var(--table-fade-end)),
|
|
859
|
+
rgb(0 0 0 / 0.2)
|
|
860
|
+
);
|
|
861
|
+
animation: table-edge-fade linear both;
|
|
862
|
+
animation-timeline: scroll(self inline);
|
|
863
|
+
}
|
|
864
|
+
/* Scroll position, not motion: the reduced-motion reset above must not collapse it. */
|
|
865
|
+
@media (prefers-reduced-motion: reduce) {
|
|
866
|
+
:is([data-slot="table-container"], [data-slot="page-toc"] > div) {
|
|
867
|
+
animation-duration: auto !important;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|