@siyahi/ui 0.1.9-next.20 → 0.1.9-next.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/DESIGN.md +42 -0
  2. package/README.md +3 -3
  3. package/agents/examples/alert.tsx +4 -6
  4. package/agents/examples/app-sidebar.tsx +3 -1
  5. package/agents/examples/bar-list-sides.tsx +13 -3
  6. package/agents/examples/button-states.tsx +23 -0
  7. package/agents/examples/calendar.tsx +3 -7
  8. package/agents/examples/callout.tsx +33 -0
  9. package/agents/examples/card.tsx +3 -1
  10. package/agents/examples/change-bar.tsx +104 -0
  11. package/agents/examples/chart-area.tsx +5 -4
  12. package/agents/examples/chart-bar-flows.tsx +3 -2
  13. package/agents/examples/chart-bar-sectors.tsx +2 -0
  14. package/agents/examples/collapsible.tsx +2 -2
  15. package/agents/examples/command.tsx +22 -20
  16. package/agents/examples/context-menu.tsx +4 -2
  17. package/agents/examples/data-table-compact.tsx +95 -0
  18. package/agents/examples/distribution-bar-tug.tsx +24 -22
  19. package/agents/examples/heatmap-calendar.tsx +19 -7
  20. package/agents/examples/heatmap-status.tsx +19 -11
  21. package/agents/examples/heatmap-weekday.tsx +34 -15
  22. package/agents/examples/input-states.tsx +33 -0
  23. package/agents/examples/metric-strip.tsx +9 -3
  24. package/agents/examples/overview-dashboard.tsx +18 -0
  25. package/agents/examples/page-hero.tsx +38 -0
  26. package/agents/examples/price-chart.tsx +2 -2
  27. package/agents/examples/range-position-table.tsx +101 -0
  28. package/agents/examples/range-position.tsx +55 -0
  29. package/agents/examples/scroll-area.tsx +12 -10
  30. package/agents/examples/section-cards-select.tsx +8 -5
  31. package/agents/examples/surfaces.tsx +7 -4
  32. package/agents/examples/target-rows.tsx +72 -0
  33. package/agents/examples/treemap.tsx +13 -10
  34. package/agents/manifest.json +153 -3
  35. package/dist/components/registry/charts/area-chart-card.js +25 -7
  36. package/dist/components/registry/charts/bar-chart-card.d.ts +0 -1
  37. package/dist/components/registry/charts/bar-chart-card.js +15 -4
  38. package/dist/components/registry/charts/line-chart-card.js +2 -2
  39. package/dist/components/registry/dashboard-01/section-cards.js +1 -1
  40. package/dist/components/registry/dashboardcn/bar-list.js +11 -5
  41. package/dist/components/registry/dashboardcn/distribution-bar.js +5 -3
  42. package/dist/components/registry/dashboardcn/kpi-row-card.js +2 -2
  43. package/dist/components/registry/dashboardcn/sparkline.d.ts +5 -1
  44. package/dist/components/registry/dashboardcn/sparkline.js +40 -18
  45. package/dist/components/registry/heatmap/calendar-heatmap.d.ts +4 -2
  46. package/dist/components/registry/heatmap/calendar-heatmap.js +67 -33
  47. package/dist/components/registry/heatmap/status-heatmap.d.ts +5 -3
  48. package/dist/components/registry/heatmap/status-heatmap.js +31 -16
  49. package/dist/components/registry/heatmap/weekday-heatmap.d.ts +4 -2
  50. package/dist/components/registry/heatmap/weekday-heatmap.js +28 -12
  51. package/dist/components/registry/niko-table/core/data-table-context.d.ts +16 -1
  52. package/dist/components/registry/niko-table/core/data-table-context.js +5 -20
  53. package/dist/components/registry/niko-table/core/data-table-root.js +39 -4
  54. package/dist/components/registry/niko-table/core/data-table-structure.js +2 -0
  55. package/dist/components/registry/niko-table/lib/styles.js +1 -1
  56. package/dist/components/registry/niko-table/types/index.d.ts +9 -0
  57. package/dist/components/registry/sidebar-07/nav-main.d.ts +1 -0
  58. package/dist/components/registry/sidebar-07/nav-main.js +9 -2
  59. package/dist/components/siyahi/callout.d.ts +24 -0
  60. package/dist/components/siyahi/callout.js +40 -0
  61. package/dist/components/siyahi/change-bar.d.ts +21 -0
  62. package/dist/components/siyahi/change-bar.js +35 -0
  63. package/dist/components/siyahi/data-table-compact-columns-toggle.d.ts +4 -0
  64. package/dist/components/siyahi/data-table-compact-columns-toggle.js +19 -0
  65. package/dist/components/siyahi/data-table-footnote.d.ts +12 -0
  66. package/dist/components/siyahi/data-table-footnote.js +35 -0
  67. package/dist/components/siyahi/default-sidebar-open.d.ts +8 -0
  68. package/dist/components/siyahi/default-sidebar-open.js +13 -0
  69. package/dist/components/siyahi/delta.d.ts +5 -1
  70. package/dist/components/siyahi/delta.js +14 -9
  71. package/dist/components/siyahi/page-hero.d.ts +25 -0
  72. package/dist/components/siyahi/page-hero.js +41 -0
  73. package/dist/components/siyahi/page-toc.d.ts +20 -0
  74. package/dist/components/siyahi/page-toc.js +76 -0
  75. package/dist/components/siyahi/quote.d.ts +12 -0
  76. package/dist/components/siyahi/quote.js +23 -0
  77. package/dist/components/siyahi/range-position.d.ts +24 -0
  78. package/dist/components/siyahi/range-position.js +105 -0
  79. package/dist/components/siyahi/siyahi-provider.d.ts +1 -1
  80. package/dist/components/siyahi/siyahi-provider.js +1 -1
  81. package/dist/components/siyahi/target-rows.d.ts +34 -0
  82. package/dist/components/siyahi/target-rows.js +97 -0
  83. package/dist/components/siyahi/treemap.d.ts +4 -1
  84. package/dist/components/siyahi/treemap.js +36 -28
  85. package/dist/components/siyahi/warning-alert.d.ts +10 -0
  86. package/dist/components/siyahi/warning-alert.js +20 -0
  87. package/dist/index.d.ts +15 -4
  88. package/dist/index.js +12 -1
  89. package/dist/lib/chart-frame.d.ts +12 -2
  90. package/dist/lib/chart-frame.js +51 -7
  91. package/dist/lib/chart-series.d.ts +5 -2
  92. package/dist/lib/compact-columns.d.ts +19 -0
  93. package/dist/lib/compact-columns.js +55 -0
  94. package/dist/lib/format.d.ts +8 -3
  95. package/dist/lib/format.js +25 -6
  96. package/dist/lib/treemap.js +6 -1
  97. package/dist/lib/use-element-width.d.ts +5 -0
  98. package/dist/lib/use-element-width.js +15 -0
  99. package/package.json +2 -1
  100. package/report.css +37 -36
  101. package/styles/siyahi.css +243 -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
- * Stock shadcn/ui `neutral` for both schemes, plus four meaning colours and a
4
- * five-hue chart palette. `packages/tools/contrast.mjs` checks every value
3
+ * shadcn/ui `neutral` greys for both schemes, with ink indigo (hue 275)
4
+ * placed, not spread: links, focus rings, the selected sidebar item's text
5
+ * and the first chart series. Buttons, badges and toggles stay monochrome,
6
+ * where 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";
@@ -80,8 +83,9 @@
80
83
 
81
84
  :root {
82
85
  color-scheme: light dark;
83
- --radius: 0.625rem;
84
- --background: oklch(1 0 0);
86
+ /* shadcn's "small": crisper than its 0.625rem default, still not square. Cards 10px, buttons 7px. */
87
+ --radius: 0.45rem;
88
+ --background: oklch(0.985 0 0);
85
89
  --foreground: oklch(0.145 0 0);
86
90
  --card: oklch(1 0 0);
87
91
  --card-foreground: oklch(0.145 0 0);
@@ -92,42 +96,47 @@
92
96
  --secondary: oklch(0.97 0 0);
93
97
  --secondary-foreground: oklch(0.205 0 0);
94
98
  --muted: oklch(0.97 0 0);
95
- --muted-foreground: oklch(0.556 0 0);
99
+ /* Below shadcn's 0.556, so muted text keeps 4.5:1 on the muted fill and on every Callout's tint. */
100
+ --muted-foreground: oklch(0.51 0 0);
96
101
  --accent: oklch(0.97 0 0);
97
102
  --accent-foreground: oklch(0.205 0 0);
98
- --destructive: oklch(0.577 0.245 27.325);
103
+ /* Deeper than the loss red, so a destructive button's label reads on its own red tint, hovered too. */
104
+ --destructive: oklch(0.49 0.19 27.325);
99
105
  --border: oklch(0.922 0 0);
100
- --input: oklch(0.922 0 0);
101
- --ring: oklch(0.708 0 0);
102
- /* Tailwind green-700, red-600, amber-700, blue-600. */
103
- --positive: oklch(0.527 0.154 150.069);
106
+ /* The edge of a field (input, textarea, select, input group) at 3:1: --border stays faint for dividers. */
107
+ --input: oklch(0.64 0 0);
108
+ --ring: oklch(0.62 0.16 275);
109
+ /* Tailwind green-700, red-600, amber-700, with their chroma lowered to the sRGB edge. */
110
+ --positive: oklch(0.527 0.145 150.069);
104
111
  --positive-foreground: oklch(0.985 0 0);
105
- --negative: oklch(0.577 0.245 27.325);
112
+ --negative: oklch(0.577 0.235 27.325);
106
113
  --negative-foreground: oklch(0.985 0 0);
107
- --warning: oklch(0.555 0.163 48.998);
114
+ --warning: oklch(0.555 0.149 48.998);
108
115
  --warning-foreground: oklch(0.985 0 0);
109
- --link: oklch(0.546 0.245 262.881);
116
+ --link: oklch(0.51 0.2 275);
110
117
  /*
111
- * Blue, emerald, amber, violet, rose. Each slot reaches 3:1 against the card
112
- * and against the slots next to it, which only lightness can give, so
113
- * emerald and violet are the deep members in both schemes. Slots two apart
118
+ * Indigo, amber, plum, sky, teal: no green or red, so no series reads as a
119
+ * gain or a loss. Each slot reaches 3:1 against the canvas, the card and the
120
+ * slots next to it, which only lightness can give, so indigo, plum and teal
121
+ * are the deep members and amber and sky the pale ones, in both schemes: a
122
+ * pale indigo in dark read as lavender and lost the accent. Slots two apart
114
123
  * share a lightness and are told apart by hue; series take slots in order.
115
124
  * Every chart value is inside sRGB, so the browser paints what the contrast
116
125
  * check scored.
117
126
  */
118
- --chart-1: oklch(0.65 0.17 259.8);
119
- --chart-2: oklch(0.35 0.07 163.2);
120
- --chart-3: oklch(0.66 0.15 60);
121
- --chart-4: oklch(0.36 0.17 293);
122
- --chart-5: oklch(0.65 0.21 16.4);
127
+ --chart-1: oklch(0.4 0.17 275);
128
+ --chart-2: oklch(0.66 0.13 70);
129
+ --chart-3: oklch(0.38 0.13 335);
130
+ --chart-4: oklch(0.65 0.11 235);
131
+ --chart-5: oklch(0.36 0.06 205);
123
132
  --sidebar: oklch(0.985 0 0);
124
133
  --sidebar-foreground: oklch(0.145 0 0);
125
134
  --sidebar-primary: oklch(0.205 0 0);
126
135
  --sidebar-primary-foreground: oklch(0.985 0 0);
127
136
  --sidebar-accent: oklch(0.97 0 0);
128
- --sidebar-accent-foreground: oklch(0.205 0 0);
137
+ --sidebar-accent-foreground: oklch(0.45 0.2 275);
129
138
  --sidebar-border: oklch(0.922 0 0);
130
- --sidebar-ring: oklch(0.708 0 0);
139
+ --sidebar-ring: oklch(0.62 0.16 275);
131
140
 
132
141
  @variant dark {
133
142
  /*
@@ -136,49 +145,54 @@
136
145
  * cards' grey gone on a ruled page the canvas is what text sits on.
137
146
  * Claude Code (#181818, about #e8e8e8 text) and T3 Code (#0a0a0a, about
138
147
  * #bebebe) both narrow that gap; this sits between them, at 14.9:1.
139
- * The card matches the canvas, so the contrast check scores the colour
140
- * a ruled chart is drawn on, and the sidebar is a shade darker, as in
141
- * the light scheme.
148
+ * Cards sit one step above the canvas, so data reads as raised, as the
149
+ * white card does on the light canvas; the sidebar is a shade darker.
142
150
  */
143
- --background: oklch(0.18 0 0);
151
+ --background: oklch(0.14 0 0);
144
152
  --foreground: oklch(0.922 0 0);
145
- --card: oklch(0.18 0 0);
153
+ --card: oklch(0.215 0 0);
146
154
  --card-foreground: oklch(0.922 0 0);
147
- --popover: oklch(0.18 0 0);
155
+ --popover: oklch(0.215 0 0);
148
156
  --popover-foreground: oklch(0.922 0 0);
149
157
  --primary: oklch(0.922 0 0);
150
158
  --primary-foreground: oklch(0.205 0 0);
151
159
  --secondary: oklch(0.269 0 0);
152
160
  --secondary-foreground: oklch(0.922 0 0);
153
161
  --muted: oklch(0.269 0 0);
154
- --muted-foreground: oklch(0.708 0 0);
162
+ /* A step above shadcn's 0.708: subtitles, footnotes and table labels at data density read too dim. */
163
+ --muted-foreground: oklch(0.74 0 0);
155
164
  --accent: oklch(0.269 0 0);
156
165
  --accent-foreground: oklch(0.922 0 0);
157
- --destructive: oklch(0.704 0.191 22.216);
166
+ /* Paler and calmer than the loss red, so a destructive label reads on its red tint over the card, hovered too. */
167
+ --destructive: oklch(0.8 0.1 22.216);
158
168
  --border: oklch(1 0 0 / 10%);
159
- --input: oklch(1 0 0 / 15%);
160
- --ring: oklch(0.556 0 0);
161
- /* Tailwind green-400, red-400, amber-400, blue-400. */
162
- --positive: oklch(0.792 0.209 151.711);
169
+ --input: oklch(0.62 0 0);
170
+ --ring: oklch(0.6 0.14 275);
171
+ /*
172
+ * Green toned down from Tailwind green-400, below red-400's lightness so a
173
+ * treemap's deepest green tile keeps its light label at 4.5:1; amber-400.
174
+ * Red, amber and the link sit at the sRGB edge.
175
+ */
176
+ --positive: oklch(0.69 0.16 151.711);
163
177
  --positive-foreground: oklch(0.145 0 0);
164
- --negative: oklch(0.704 0.191 22.216);
178
+ --negative: oklch(0.704 0.187 22.216);
165
179
  --negative-foreground: oklch(0.145 0 0);
166
- --warning: oklch(0.828 0.189 84.429);
180
+ --warning: oklch(0.828 0.169 84.429);
167
181
  --warning-foreground: oklch(0.145 0 0);
168
- --link: oklch(0.707 0.165 254.624);
169
- --chart-1: oklch(0.84 0.08 255);
170
- --chart-2: oklch(0.52 0.11 163.2);
171
- --chart-3: oklch(0.86 0.13 80);
172
- --chart-4: oklch(0.55 0.25 293);
173
- --chart-5: oklch(0.84 0.085 12);
182
+ --link: oklch(0.75 0.128 275);
183
+ --chart-1: oklch(0.58 0.2 275);
184
+ --chart-2: oklch(0.88 0.08 75);
185
+ --chart-3: oklch(0.58 0.16 335);
186
+ --chart-4: oklch(0.88 0.05 235);
187
+ --chart-5: oklch(0.56 0.08 205);
174
188
  --sidebar: oklch(0.145 0 0);
175
189
  --sidebar-foreground: oklch(0.922 0 0);
176
- --sidebar-primary: oklch(0.488 0.243 264.376);
177
- --sidebar-primary-foreground: oklch(0.985 0 0);
190
+ --sidebar-primary: oklch(0.922 0 0);
191
+ --sidebar-primary-foreground: oklch(0.205 0 0);
178
192
  --sidebar-accent: oklch(0.269 0 0);
179
- --sidebar-accent-foreground: oklch(0.922 0 0);
193
+ --sidebar-accent-foreground: oklch(0.8 0.1 275);
180
194
  --sidebar-border: oklch(1 0 0 / 10%);
181
- --sidebar-ring: oklch(0.556 0 0);
195
+ --sidebar-ring: oklch(0.6 0.14 275);
182
196
  }
183
197
  }
184
198
 
@@ -205,6 +219,130 @@
205
219
  }
206
220
  }
207
221
 
222
+ /*
223
+ * Every selected toggle, a view switch or a filter chip alike, fills with the
224
+ * foreground, black on white and white on black, so the reader sees at once
225
+ * what is on; stock shadcn marks it with a faint grey. A default (pill) tab
226
+ * selects the same way: stock fills it with the canvas colour, 1.05:1 against
227
+ * its grey list. Unlayered, to outrank the stock aria-pressed:bg-muted.
228
+ */
229
+ :is([data-slot="toggle"], [data-slot="toggle-group-item"]):is([aria-pressed="true"], [data-pressed]),
230
+ [data-slot="tabs-list"][data-variant="default"] [data-slot="tabs-trigger"][data-active] {
231
+ background-color: var(--foreground);
232
+ color: var(--background);
233
+ border-color: var(--foreground);
234
+ }
235
+
236
+ /*
237
+ * --input is the edge of a field, where the reader types or picks. A button,
238
+ * a toggle and a code block are framed by their label or their content, so
239
+ * their outline stays the faint divider colour; scoping the variable keeps
240
+ * the fill each derives from it faint too.
241
+ */
242
+ :is([data-slot="button"], [data-slot="toggle"], [data-slot="toggle-group-item"], [data-slot="code-block"]) {
243
+ --input: var(--border);
244
+ }
245
+
246
+ /*
247
+ * The command palette's search field is a field: the --input edge on a muted
248
+ * fill, and the muted placeholder every other field uses (stock leaves it at
249
+ * Tailwind's half-strength text, 3.7:1).
250
+ */
251
+ [data-slot="command-input-wrapper"] > [data-slot="input-group"] {
252
+ border-color: var(--input);
253
+ background-color: color-mix(in oklab, var(--muted) 50%, transparent);
254
+ }
255
+ [data-slot="command-input"]::placeholder {
256
+ color: var(--muted-foreground);
257
+ }
258
+
259
+ /*
260
+ * Focus is opaque wherever the ring is the only sign of it: stock draws the
261
+ * ring at half strength (under 2:1 on the canvas) and relies on an opaque
262
+ * border these controls do not have. The slider's thumb is a div around a
263
+ * hidden range input, so it shows the input's focus.
264
+ */
265
+ :is([data-slot="toggle"], [data-slot="toggle-group-item"], [data-slot="scroll-area-viewport"]):focus-visible,
266
+ [data-slot="button"][data-day]:focus-visible {
267
+ --tw-ring-color: var(--ring);
268
+ }
269
+ [data-slot="slider-thumb"]:has(input:focus-visible) {
270
+ box-shadow: 0 0 0 3px var(--ring);
271
+ }
272
+
273
+ /*
274
+ * A destructive control focuses, and an invalid field is edged, in the full
275
+ * destructive colour; stock fades both to 40-50%, under 3:1.
276
+ */
277
+ [data-slot="button"][class~="text-destructive"]:focus-visible,
278
+ [data-slot="code-block"][data-variant="destructive"]:has(> [data-slot="code-block-content"]:focus-visible),
279
+ :is(
280
+ [data-slot="input"],
281
+ [data-slot="textarea"],
282
+ [data-slot="select-trigger"],
283
+ [data-slot="button"]
284
+ )[aria-invalid="true"],
285
+ [data-slot="input-group"]:has([data-slot][aria-invalid="true"]) {
286
+ border-color: var(--destructive);
287
+ }
288
+
289
+ /*
290
+ * A slider's unfilled track is outlined in the field edge, so its extent reads
291
+ * at 3:1; the muted track alone is 1.05:1 on the canvas.
292
+ */
293
+ [data-slot="slider-track"] {
294
+ box-shadow: inset 0 0 0 1px var(--input);
295
+ }
296
+
297
+ /*
298
+ * A number column (meta.variant "number") right-aligns its head and cells, so
299
+ * figures line up by their units down the column, as a ledger reads.
300
+ */
301
+ :is([data-slot="table-head"], [data-slot="table-cell"])[data-numeric] {
302
+ text-align: right;
303
+ }
304
+ [data-slot="table-head"][data-numeric] > *,
305
+ [data-slot="table-cell"][data-numeric] > :is(div, span)[class*="flex"] {
306
+ justify-content: flex-end;
307
+ margin-inline-start: auto;
308
+ }
309
+
310
+ /*
311
+ * A card on the card surface lifts off the tinted canvas with a faint shadow
312
+ * under its 1px ring (Tailwind composes --tw-shadow with the ring), and a data
313
+ * table inside it drops its own border, so there is one frame, not two.
314
+ */
315
+ @scope (:root, [data-surface="card"]) to ([data-surface="ruled"], [data-surface="open"]) {
316
+ [data-slot="card"] {
317
+ --tw-shadow: 0 1px 2px 0 oklch(0 0 0 / 0.06), 0 1px 3px 0 oklch(0 0 0 / 0.04);
318
+ }
319
+ [data-slot="card"] [data-slot="table-container"] {
320
+ border-color: transparent;
321
+ }
322
+ }
323
+
324
+ /*
325
+ * Chart gridlines take the full border colour: stock shadcn draws them at half
326
+ * of it, which all but vanishes on a dark card (5% white).
327
+ */
328
+ [data-slot="chart"] .recharts-cartesian-grid line {
329
+ stroke: var(--border);
330
+ }
331
+
332
+ /*
333
+ * The selected sub-page marks its stretch of the sidebar's rail in the accent,
334
+ * so the current place reads at a glance in a long list, as a docs index does.
335
+ */
336
+ [data-slot="sidebar-menu-sub-item"]:has(> [data-active])::before {
337
+ content: "";
338
+ position: absolute;
339
+ inset-block: 2px;
340
+ left: -11px;
341
+ width: 2px;
342
+ border-radius: 1px;
343
+ background: var(--sidebar-accent-foreground);
344
+ }
345
+
208
346
  /* Touch targets follow input capability, including touch laptops. */
209
347
  @media (pointer: coarse) {
210
348
  :is(
@@ -251,17 +389,16 @@
251
389
  }
252
390
 
253
391
  /*
254
- * Surfaces, from SiyahiProvider's surface prop. Ruled, the default, and open
255
- * take the box off every core Card: no fill, no corner, no ring, and no
256
- * tinted footer band; PanelGrid draws the hairlines between ruled blocks.
257
- * Open also drops the card's padding, so a block's content sits level with
258
- * the page's text. A card provider gives shadcn's own card back. Each scope
259
- * starts at the root, so a Card outside any provider is ruled too, and stops
260
- * at a provider that says otherwise. Unlayered, like the rule above, to
392
+ * Surfaces, from SiyahiProvider's surface prop. Card, the default, is
393
+ * shadcn's own card on the tinted canvas. Ruled and open take the box off
394
+ * every core Card: no fill, no corner, no ring, and no tinted footer band;
395
+ * PanelGrid draws the hairlines between ruled blocks. Open also drops the
396
+ * card's padding, so a block's content sits level with the page's text. Each
397
+ * scope stops at a provider that says otherwise. Unlayered, like the rule above, to
261
398
  * outrank the core components' utility classes; a picked SectionCards tile
262
399
  * keeps its ring, which is important.
263
400
  */
264
- @scope (:root, [data-surface="ruled"], [data-surface="open"]) to ([data-surface="card"]) {
401
+ @scope ([data-surface="ruled"], [data-surface="open"]) to ([data-surface="card"]) {
265
402
  [data-slot="card"] {
266
403
  background: none;
267
404
  border-radius: 0;
@@ -281,3 +418,54 @@
281
418
  gap: calc(var(--spacing) * 4);
282
419
  }
283
420
  }
421
+
422
+ /*
423
+ * A table, or a page's section strip, that scrolls sideways fades at the edge
424
+ * with more behind it,
425
+ * so a phone reader sees there is more without a visible scrollbar. The fade
426
+ * follows the scroll position; a table that fits has no timeline and no fade.
427
+ */
428
+ @property --table-fade-start {
429
+ syntax: "<length>";
430
+ inherits: false;
431
+ initial-value: 0px;
432
+ }
433
+ @property --table-fade-end {
434
+ syntax: "<length>";
435
+ inherits: false;
436
+ initial-value: 0px;
437
+ }
438
+ @keyframes table-edge-fade {
439
+ 0% {
440
+ --table-fade-start: 0px;
441
+ --table-fade-end: 2rem;
442
+ }
443
+ 8%,
444
+ 92% {
445
+ --table-fade-start: 2rem;
446
+ --table-fade-end: 2rem;
447
+ }
448
+ 100% {
449
+ --table-fade-start: 2rem;
450
+ --table-fade-end: 0px;
451
+ }
452
+ }
453
+ @layer base {
454
+ :is([data-slot="table-container"], [data-slot="page-toc"] > div) {
455
+ mask-image: linear-gradient(
456
+ to right,
457
+ rgb(0 0 0 / 0.2),
458
+ #000 var(--table-fade-start),
459
+ #000 calc(100% - var(--table-fade-end)),
460
+ rgb(0 0 0 / 0.2)
461
+ );
462
+ animation: table-edge-fade linear both;
463
+ animation-timeline: scroll(self inline);
464
+ }
465
+ /* Scroll position, not motion: the reduced-motion reset above must not collapse it. */
466
+ @media (prefers-reduced-motion: reduce) {
467
+ :is([data-slot="table-container"], [data-slot="page-toc"] > div) {
468
+ animation-duration: auto !important;
469
+ }
470
+ }
471
+ }