@misoto22/folio 0.16.0 → 0.17.1

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 (197) hide show
  1. package/dist/agent/Accordion.md +1 -1
  2. package/dist/agent/Actions.md +1 -1
  3. package/dist/agent/Alert.md +1 -1
  4. package/dist/agent/AppShell.md +1 -1
  5. package/dist/agent/ArchitectureFigure.md +1 -1
  6. package/dist/agent/AreaChart.md +1 -1
  7. package/dist/agent/Article.md +1 -1
  8. package/dist/agent/AspectRatio.md +1 -1
  9. package/dist/agent/Avatar.md +1 -1
  10. package/dist/agent/Badge.md +1 -1
  11. package/dist/agent/BarChart.md +1 -1
  12. package/dist/agent/BarList.md +1 -1
  13. package/dist/agent/BigNumber.md +1 -1
  14. package/dist/agent/BoxPlot.md +1 -1
  15. package/dist/agent/Breadcrumb.md +1 -1
  16. package/dist/agent/BulletChart.md +1 -1
  17. package/dist/agent/Button.md +1 -1
  18. package/dist/agent/Calendar.md +1 -1
  19. package/dist/agent/CalendarHeatmap.md +1 -1
  20. package/dist/agent/Card.md +1 -1
  21. package/dist/agent/Checkbox.md +1 -1
  22. package/dist/agent/Code.md +1 -1
  23. package/dist/agent/CodeBlock.md +1 -1
  24. package/dist/agent/Collapsible.md +1 -1
  25. package/dist/agent/Collection.md +1 -1
  26. package/dist/agent/ColorPicker.md +1 -1
  27. package/dist/agent/Combobox.md +1 -1
  28. package/dist/agent/Command.md +1 -1
  29. package/dist/agent/ComposedChart.md +1 -1
  30. package/dist/agent/Contact.md +1 -1
  31. package/dist/agent/Content.md +1 -1
  32. package/dist/agent/ContextMenu.md +1 -1
  33. package/dist/agent/Conversation.md +1 -1
  34. package/dist/agent/DataflowFigure.md +1 -1
  35. package/dist/agent/DatePicker.md +1 -1
  36. package/dist/agent/DescriptionList.md +1 -1
  37. package/dist/agent/Diagram.md +1 -1
  38. package/dist/agent/DiagramCanvas.md +1 -1
  39. package/dist/agent/DiagramExportMenu.md +1 -1
  40. package/dist/agent/DiagramInspector.md +1 -1
  41. package/dist/agent/DiagramLegend.md +1 -1
  42. package/dist/agent/DiagramMinimap.md +1 -1
  43. package/dist/agent/DiagramToolbar.md +1 -1
  44. package/dist/agent/Dialog.md +1 -1
  45. package/dist/agent/DropdownMenu.md +1 -1
  46. package/dist/agent/EmptyState.md +1 -1
  47. package/dist/agent/ErrorState.md +1 -1
  48. package/dist/agent/Evidence.md +1 -1
  49. package/dist/agent/Facet.md +1 -1
  50. package/dist/agent/Field.md +1 -1
  51. package/dist/agent/FigureBand.md +1 -1
  52. package/dist/agent/FloatingIconButton.md +1 -1
  53. package/dist/agent/FunnelChart.md +1 -1
  54. package/dist/agent/Heading.md +1 -1
  55. package/dist/agent/Heatmap.md +1 -1
  56. package/dist/agent/Histogram.md +1 -1
  57. package/dist/agent/Input.md +1 -1
  58. package/dist/agent/Kbd.md +1 -1
  59. package/dist/agent/LifecycleFigure.md +1 -1
  60. package/dist/agent/LineChart.md +1 -1
  61. package/dist/agent/LinkArrow.md +1 -1
  62. package/dist/agent/Markdown.md +1 -1
  63. package/dist/agent/Media.md +1 -1
  64. package/dist/agent/Metric.md +1 -1
  65. package/dist/agent/Metrics.md +1 -1
  66. package/dist/agent/Music.md +1 -1
  67. package/dist/agent/NativeSelect.md +1 -1
  68. package/dist/agent/NavItem.md +1 -1
  69. package/dist/agent/NumberField.md +1 -1
  70. package/dist/agent/PageHeader.md +1 -1
  71. package/dist/agent/Pagination.md +1 -1
  72. package/dist/agent/PieChart.md +1 -1
  73. package/dist/agent/Popover.md +1 -1
  74. package/dist/agent/Portfolio.md +1 -1
  75. package/dist/agent/PortfolioIndex.md +1 -1
  76. package/dist/agent/Progress.md +1 -1
  77. package/dist/agent/RadarChart.md +1 -1
  78. package/dist/agent/RadialChart.md +1 -1
  79. package/dist/agent/RadioGroup.md +1 -1
  80. package/dist/agent/Reading.md +1 -1
  81. package/dist/agent/Recovery.md +1 -1
  82. package/dist/agent/SankeyChart.md +1 -1
  83. package/dist/agent/ScatterChart.md +1 -1
  84. package/dist/agent/ScrollArea.md +1 -1
  85. package/dist/agent/SearchPalette.md +1 -1
  86. package/dist/agent/SearchableMenu.md +1 -1
  87. package/dist/agent/Select.md +1 -1
  88. package/dist/agent/Separator.md +1 -1
  89. package/dist/agent/SequenceFigure.md +1 -1
  90. package/dist/agent/Sheet.md +1 -1
  91. package/dist/agent/Sidebar.md +1 -1
  92. package/dist/agent/SiteNavigation.md +1 -1
  93. package/dist/agent/SiteShell.md +1 -1
  94. package/dist/agent/Skeleton.md +1 -1
  95. package/dist/agent/Slider.md +1 -1
  96. package/dist/agent/Sparkline.md +1 -1
  97. package/dist/agent/Spinner.md +1 -1
  98. package/dist/agent/StatusDot.md +1 -1
  99. package/dist/agent/StatusPill.md +1 -1
  100. package/dist/agent/Steps.md +1 -1
  101. package/dist/agent/Switch.md +1 -1
  102. package/dist/agent/Table.md +1 -1
  103. package/dist/agent/Tabs.md +1 -1
  104. package/dist/agent/Tag.md +1 -1
  105. package/dist/agent/Text.md +1 -1
  106. package/dist/agent/Textarea.md +1 -1
  107. package/dist/agent/Timeline.md +1 -1
  108. package/dist/agent/Timestamp.md +1 -1
  109. package/dist/agent/Toast.md +1 -1
  110. package/dist/agent/ToggleGroup.md +1 -1
  111. package/dist/agent/Toolbar.md +1 -1
  112. package/dist/agent/Tooltip.md +1 -1
  113. package/dist/agent/TreemapChart.md +1 -1
  114. package/dist/agent/WaterfallChart.md +1 -1
  115. package/dist/agent/Workbench.md +142 -0
  116. package/dist/agent/WorkflowFigure.md +1 -1
  117. package/dist/agent/catalog.json +51 -2
  118. package/dist/agent/index.md +6 -3
  119. package/dist/article.css +1 -1
  120. package/dist/components/Badge/Badge.js +1 -1
  121. package/dist/components/Badge/Badge.js.map +1 -1
  122. package/dist/components/Button/Button.js +1 -1
  123. package/dist/components/Button/Button.js.map +1 -1
  124. package/dist/components/FigureBand/FigureBand.js +1 -1
  125. package/dist/components/FigureBand/FigureBand.js.map +1 -1
  126. package/dist/components/Heading/Heading.js +1 -1
  127. package/dist/components/Heading/Heading.js.map +1 -1
  128. package/dist/components/Pagination/Pagination.js +2 -2
  129. package/dist/components/Pagination/Pagination.js.map +1 -1
  130. package/dist/components/Table/Table.js +1 -1
  131. package/dist/components/Table/Table.js.map +1 -1
  132. package/dist/components/Tag/Tag.js +3 -3
  133. package/dist/components/Tag/Tag.js.map +1 -1
  134. package/dist/components/Text/Text.js +2 -2
  135. package/dist/components/Text/Text.js.map +1 -1
  136. package/dist/components/ToggleGroup/ToggleGroup.js +2 -2
  137. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  138. package/dist/semantic.css +9 -0
  139. package/dist/styles.css +1 -1
  140. package/dist/themes.css +99 -0
  141. package/dist/tokens.css +29 -2
  142. package/dist/tokens.d.ts +9 -0
  143. package/dist/tokens.js +51 -2
  144. package/dist/tokens.json +51 -2
  145. package/dist/website/Workbench/Chronology.d.ts +32 -0
  146. package/dist/website/Workbench/Chronology.js +32 -0
  147. package/dist/website/Workbench/Chronology.js.map +1 -0
  148. package/dist/website/Workbench/ContactRow.d.ts +13 -0
  149. package/dist/website/Workbench/ContactRow.js +11 -0
  150. package/dist/website/Workbench/ContactRow.js.map +1 -0
  151. package/dist/website/Workbench/PhotoMosaic.d.ts +13 -0
  152. package/dist/website/Workbench/PhotoMosaic.js +15 -0
  153. package/dist/website/Workbench/PhotoMosaic.js.map +1 -0
  154. package/dist/website/Workbench/Reveal.d.ts +19 -0
  155. package/dist/website/Workbench/Reveal.js +29 -0
  156. package/dist/website/Workbench/Reveal.js.map +1 -0
  157. package/dist/website/Workbench/ToolStrip.d.ts +18 -0
  158. package/dist/website/Workbench/ToolStrip.js +15 -0
  159. package/dist/website/Workbench/ToolStrip.js.map +1 -0
  160. package/dist/website/Workbench/Workbench.d.ts +23 -0
  161. package/dist/website/Workbench/Workbench.js +22 -0
  162. package/dist/website/Workbench/Workbench.js.map +1 -0
  163. package/dist/website/Workbench/WorkbenchCard.d.ts +29 -0
  164. package/dist/website/Workbench/WorkbenchCard.js +21 -0
  165. package/dist/website/Workbench/WorkbenchCard.js.map +1 -0
  166. package/dist/website/Workbench/WorkbenchEntries.d.ts +22 -0
  167. package/dist/website/Workbench/WorkbenchEntries.js +24 -0
  168. package/dist/website/Workbench/WorkbenchEntries.js.map +1 -0
  169. package/dist/website/Workbench/WorkbenchFigure.d.ts +20 -0
  170. package/dist/website/Workbench/WorkbenchFigure.js +15 -0
  171. package/dist/website/Workbench/WorkbenchFigure.js.map +1 -0
  172. package/dist/website/Workbench/WorkbenchHero.d.ts +24 -0
  173. package/dist/website/Workbench/WorkbenchHero.js +24 -0
  174. package/dist/website/Workbench/WorkbenchHero.js.map +1 -0
  175. package/dist/website/Workbench/theme.d.ts +22 -0
  176. package/dist/website/Workbench/theme.js +12 -0
  177. package/dist/website/Workbench/theme.js.map +1 -0
  178. package/dist/website/index.d.ts +11 -0
  179. package/dist/website/index.js +11 -0
  180. package/dist/website/index.js.map +1 -1
  181. package/dist/website-collection.css +8 -8
  182. package/dist/website-contact.css +9 -3
  183. package/dist/website-content.css +4 -1
  184. package/dist/website-conversation.css +6 -6
  185. package/dist/website-evidence.css +1 -1
  186. package/dist/website-media.css +13 -13
  187. package/dist/website-metrics.css +1 -1
  188. package/dist/website-music.css +1 -1
  189. package/dist/website-portfolio.css +4 -4
  190. package/dist/website-reading.css +13 -13
  191. package/dist/website-recovery.css +1 -1
  192. package/dist/website-shell.css +1 -1
  193. package/dist/website-timeline.css +3 -3
  194. package/dist/website-workbench.css +162 -0
  195. package/dist/website.css +1 -0
  196. package/package.json +1 -1
  197. package/skills/folio-design/rules/tokens.md +4 -2
package/dist/themes.css CHANGED
@@ -47,6 +47,7 @@
47
47
  cobalt 8.05 9.21 7.86 8.41 10.23 8.27
48
48
  plum 8.49 9.55 8.12 8.74 10.43 8.41
49
49
  moss 5.80 8.30 7.24 8.76 11.32 9.06
50
+ ember 5.46 6.34 5.47 7.81 8.86 7.31
50
51
 
51
52
  `--accent-foreground` resolves to `--paper`, so paper printed ON the accent
52
53
  fill is the mark column read the other way up. */
@@ -94,6 +95,16 @@
94
95
  --red-soft: rgba(86, 107, 82, 0.1);
95
96
  }
96
97
 
98
+ /* A burnt orange for the Workbench preset: it marks a link or the current
99
+ state and nothing else, so it can carry heat without competing with the
100
+ photographs it sits beside. The lowest-contrast mark after moss; its
101
+ ink is a step darker so a link on a stone panel still clears AA. */
102
+ [data-accent='ember'] {
103
+ --clay: #b8430a;
104
+ --clay-ink: #a83c08;
105
+ --red-soft: rgba(184, 67, 10, 0.1);
106
+ }
107
+
97
108
  /* Re-derives the chain, on any element that names an accent.
98
109
 
99
110
  A `var()` is substituted where it is DECLARED, not where it is read:
@@ -117,6 +128,8 @@
117
128
  --accent-foreground: var(--on-red);
118
129
  --accent-on-dark: var(--red);
119
130
  --accent-on-muted: var(--red-ink);
131
+ --action: var(--red);
132
+ --action-foreground: var(--on-red);
120
133
  }
121
134
 
122
135
  /* On the dark ground the same hues have to come UP to clear AA, not down. An
@@ -160,6 +173,31 @@
160
173
  --clay-ink: #b8cbb3;
161
174
  --red-soft: rgba(156, 181, 151, 0.14);
162
175
  }
176
+ [data-mode='dark'][data-accent='ember'],
177
+ [data-mode='dark'] [data-accent='ember'] {
178
+ --clay: #f08a4b;
179
+ --clay-ink: #f29a62;
180
+ --red-soft: rgba(240, 138, 75, 0.14);
181
+ }
182
+
183
+ /* ─── Action ───────────────────────────────────────────────────────────────
184
+ What the large solid fills are made of: the primary button, a segmented
185
+ control's selected pill, an active filter chip, the current page. By
186
+ default they are the accent, which is what law 7 asks for. `ink` takes them
187
+ back to the page's own ink while the accent keeps marking links and small
188
+ chosen states — the Workbench preset's split, where a burnt-orange block
189
+ would be the loudest thing on a page of photographs. Declared after the accent block so it wins
190
+ on the same element, and re-derived on it for the same reason that block
191
+ is: a `var()` resolves where it is written. Ink and paper already swap in
192
+ dark mode, so this axis needs no dark block of its own. */
193
+ [data-action='accent'] {
194
+ --action: var(--accent);
195
+ --action-foreground: var(--accent-foreground);
196
+ }
197
+ [data-action='ink'] {
198
+ --action: var(--ink);
199
+ --action-foreground: var(--paper);
200
+ }
163
201
 
164
202
  /* ─── Surface ──────────────────────────────────────────────────────────────
165
203
  The ground and its two steps. Law 8 still holds — dark is a value swap, so
@@ -234,6 +272,40 @@
234
272
  --rule-2: rgba(240, 244, 250, 0.26);
235
273
  }
236
274
 
275
+ /* Linen, the Workbench ground. Unlike `warm` and `cool` it also moves the
276
+ three text steps, because the preset's warmth is carried by the ink as much
277
+ as by the paper: a neutral #101010 on a linen ground reads as a cold page
278
+ with a tinted background. The text steps keep the root's ladder — ink,
279
+ a secondary step, and an AA floor — measured on every linen ground:
280
+ light ink 14.33 / 13.60 / 12.42, --ink-2 8.13 / 7.72 / 7.05, --ink-3-aa
281
+ 6.15 / 5.84 / 5.33 against paper / paper-2 / stone; dark ink 15.57 /
282
+ 14.44 / 13.15, --ink-2 9.67 / 8.96 / 8.16, --ink-3-aa 7.18 / 6.66 / 6.07.
283
+ Rules are the ink itself at the root's two weights, so a hairline stays
284
+ the colour of the type it separates. */
285
+ [data-surface='linen'] {
286
+ --paper: #f7f7f4;
287
+ --paper-2: #f2f1ed;
288
+ --stone: #e8e7e2;
289
+ --rule: rgba(38, 37, 30, 0.1);
290
+ --rule-2: rgba(38, 37, 30, 0.2);
291
+ --ink: #26251e;
292
+ --ink-2: #4d4b44;
293
+ --ink-3: rgba(38, 37, 30, 0.55);
294
+ --ink-3-aa: #5f5d55;
295
+ }
296
+ [data-mode='dark'][data-surface='linen'],
297
+ [data-mode='dark'] [data-surface='linen'] {
298
+ --paper: #14130f;
299
+ --paper-2: #1c1b17;
300
+ --stone: #25231e;
301
+ --rule: rgba(236, 235, 230, 0.14);
302
+ --rule-2: rgba(236, 235, 230, 0.26);
303
+ --ink: #ecebe6;
304
+ --ink-2: #bdbbb3;
305
+ --ink-3: rgba(236, 235, 230, 0.55);
306
+ --ink-3-aa: #a3a199;
307
+ }
308
+
237
309
  /* Glass, and the one place this system allows a blur.
238
310
  *
239
311
  * Law 2 says depth is a hairline and never a blur, and this does not break it:
@@ -312,6 +384,9 @@
312
384
  [data-rules='quiet'] {
313
385
  --rule: #f4f2ee;
314
386
  --rule-2: #e8e5df;
387
+ /* Quiet leans on space and the surface steps, so the heavy ink divider
388
+ between sections goes entirely rather than dimming to a hairline. */
389
+ --rule-divider: transparent;
315
390
  }
316
391
  [data-mode='dark'][data-rules='quiet'],
317
392
  [data-mode='dark'] [data-rules='quiet'] {
@@ -340,6 +415,30 @@
340
415
  --font-heading: var(--serif);
341
416
  }
342
417
 
418
+ /* ─── Voice ────────────────────────────────────────────────────────────────
419
+ How loud the page speaks, which the type axis does not decide: that axis
420
+ picks a FACE and leaves the ramp alone. `quiet` is the Workbench setting —
421
+ page titles at roughly two-thirds of the ramp, display type at a regular
422
+ weight rather than a light one, and labels set as sentence-case sans in
423
+ place of tracked mono capitals. Body, record and item steps do not move, so
424
+ a quiet page keeps its hierarchy; only its top two steps come down. */
425
+ [data-voice='display'] {
426
+ --display-scale: 1;
427
+ --display-weight: 300;
428
+ --label-font: var(--font-mono);
429
+ --label-case: uppercase;
430
+ --label-tracking-scale: 1;
431
+ --body-weight: 300;
432
+ }
433
+ [data-voice='quiet'] {
434
+ --display-scale: 0.62;
435
+ --display-weight: 400;
436
+ --label-font: var(--font-sans);
437
+ --label-case: none;
438
+ --label-tracking-scale: 0;
439
+ --body-weight: 400;
440
+ }
441
+
343
442
  /* ─── Motion ───────────────────────────────────────────────────────────────
344
443
  `prefers-reduced-motion` is honoured separately and is not a theme: a
345
444
  reader's stated need is not one of five looks. This axis is taste. */
package/dist/tokens.css CHANGED
@@ -79,6 +79,11 @@
79
79
  --rule: #edebe6;
80
80
  --rule-2: #dbdbd6;
81
81
  --rule-hard: var(--ink);
82
+ /* The hard rule as a section DIVIDER — under a band heading, above a form,
83
+ between groups. Its own name so the rules axis can take dividers away
84
+ without touching the hard edges controls draw (a slider track, a table
85
+ head, a quotation bar). */
86
+ --rule-divider: var(--rule-hard);
82
87
 
83
88
  /* Historic role names, kept so a consumer written against the previous
84
89
  palette keeps resolving. Their hue is now ink; the role remains "the
@@ -191,6 +196,11 @@
191
196
  --rule: rgba(244, 244, 242, 0.14);
192
197
  --rule-2: rgba(244, 244, 242, 0.26);
193
198
  --rule-hard: var(--ink);
199
+ /* The hard rule as a section DIVIDER — under a band heading, above a form,
200
+ between groups. Its own name so the rules axis can take dividers away
201
+ without touching the hard edges controls draw (a slider track, a table
202
+ head, a quotation bar). */
203
+ --rule-divider: var(--rule-hard);
194
204
 
195
205
  --red: var(--clay);
196
206
  --red-ink: var(--clay);
@@ -279,14 +289,31 @@
279
289
 
280
290
  }
281
291
 
292
+ /* Display voice. How loud a page's display type and labels are, read by the
293
+ website compositions instead of literal values so the voice axis in
294
+ `themes.css` can quiet them. Declared on the root only — never on a fluid
295
+ frame — so an axis set on an ancestor is not reset inside one. */
296
+ :root {
297
+ --display-scale: 1;
298
+ --display-weight: 300;
299
+ --label-font: var(--font-mono);
300
+ --label-case: uppercase;
301
+ --label-tracking-scale: 1;
302
+ /* Running text: the White Reset sets it light; a quiet voice sets it at a
303
+ regular weight to match its regular-weight headings. */
304
+ --body-weight: 300;
305
+ }
306
+
282
307
  /* The fluid ramp, computed wherever `--fluid` is set.
283
308
  `[data-fluid-frame]` must sit INSIDE the element carrying `container-type`:
284
309
  a container query unit resolves against an ANCESTOR container, never against
285
310
  the element that declares the container itself. */
286
311
  :root,
287
312
  [data-fluid-frame] {
288
- --fs-title: clamp(2.5rem, calc(4.6 * var(--fluid)), 3.375rem); /* page h1 40 → 54px */
289
- --fs-lead: clamp(1.875rem, calc(3 * var(--fluid)), 2.625rem); /* band heading 30 → 42px */
313
+ /* `--display-scale` is the voice axis's lever on the two display steps; at
314
+ 1 they are the ramp as drawn. */
315
+ --fs-title: calc(clamp(2.5rem, calc(4.6 * var(--fluid)), 3.375rem) * var(--display-scale, 1)); /* page h1 40 → 54px */
316
+ --fs-lead: calc(clamp(1.875rem, calc(3 * var(--fluid)), 2.625rem) * var(--display-scale, 1)); /* band heading 30 → 42px */
290
317
  --fs-heading: clamp(1.5rem, calc(2.1 * var(--fluid)), 1.8125rem); /* record title 24 → 29px */
291
318
  --fs-sub: clamp(1.3125rem, calc(1.7 * var(--fluid)), 1.5625rem); /* card sub-head 21 → 25px */
292
319
  --fs-item: clamp(1.125rem, calc(1.35 * var(--fluid)), 1.25rem); /* in-card title 18 → 20px */
package/dist/tokens.d.ts CHANGED
@@ -19,11 +19,14 @@ export type TokenName =
19
19
  | 'accent-on-dark'
20
20
  | 'accent-on-muted'
21
21
  | 'accent-wash'
22
+ | 'action'
23
+ | 'action-foreground'
22
24
  | 'background'
23
25
  | 'background-elevated'
24
26
  | 'bar'
25
27
  | 'bar-2'
26
28
  | 'bar-h'
29
+ | 'body-weight'
27
30
  | 'border-color'
28
31
  | 'border-subtle'
29
32
  | 'brand-plate'
@@ -73,6 +76,8 @@ export type TokenName =
73
76
  | 'diagram-sigil'
74
77
  | 'diagram-surface'
75
78
  | 'disabled-opacity'
79
+ | 'display-scale'
80
+ | 'display-weight'
76
81
  | 'duration-base'
77
82
  | 'duration-fast'
78
83
  | 'duration-slow'
@@ -104,6 +109,9 @@ export type TokenName =
104
109
  | 'ink-2'
105
110
  | 'ink-3'
106
111
  | 'ink-3-aa'
112
+ | 'label-case'
113
+ | 'label-font'
114
+ | 'label-tracking-scale'
107
115
  | 'lh-cjk'
108
116
  | 'lift'
109
117
  | 'lift-sm'
@@ -149,6 +157,7 @@ export type TokenName =
149
157
  | 'row'
150
158
  | 'rule'
151
159
  | 'rule-2'
160
+ | 'rule-divider'
152
161
  | 'rule-hard'
153
162
  | 'sans'
154
163
  | 'scrim'
package/dist/tokens.js CHANGED
@@ -137,6 +137,13 @@ export const TOKENS = {
137
137
  "light": "var(--ink)",
138
138
  "dark": "var(--ink)"
139
139
  },
140
+ "rule-divider": {
141
+ "layer": "tokens",
142
+ "category": "colour",
143
+ "light": "var(--rule-hard)",
144
+ "comment": "The hard rule as a section DIVIDER — under a band heading, above a form, between groups. Its own name so the rules axis can take dividers away without touching the hard edges controls draw (a slider track, a table head, a quotation bar).",
145
+ "dark": "var(--rule-hard)"
146
+ },
140
147
  "red": {
141
148
  "layer": "tokens",
142
149
  "category": "colour",
@@ -294,16 +301,47 @@ export const TOKENS = {
294
301
  "category": "space",
295
302
  "light": "52px"
296
303
  },
304
+ "display-scale": {
305
+ "layer": "tokens",
306
+ "category": "other",
307
+ "light": "1"
308
+ },
309
+ "display-weight": {
310
+ "layer": "tokens",
311
+ "category": "other",
312
+ "light": "300"
313
+ },
314
+ "label-font": {
315
+ "layer": "tokens",
316
+ "category": "other",
317
+ "light": "var(--font-mono)"
318
+ },
319
+ "label-case": {
320
+ "layer": "tokens",
321
+ "category": "other",
322
+ "light": "uppercase"
323
+ },
324
+ "label-tracking-scale": {
325
+ "layer": "tokens",
326
+ "category": "other",
327
+ "light": "1"
328
+ },
329
+ "body-weight": {
330
+ "layer": "tokens",
331
+ "category": "other",
332
+ "light": "300",
333
+ "comment": "Running text: the White Reset sets it light; a quiet voice sets it at a regular weight to match its regular-weight headings."
334
+ },
297
335
  "fs-title": {
298
336
  "layer": "tokens",
299
337
  "category": "type",
300
- "light": "clamp(2.5rem, calc(4.6 * var(--fluid)), 3.375rem)",
338
+ "light": "calc(clamp(2.5rem, calc(4.6 * var(--fluid)), 3.375rem) * var(--display-scale, 1))",
301
339
  "comment": "page h1 40 → 54px"
302
340
  },
303
341
  "fs-lead": {
304
342
  "layer": "tokens",
305
343
  "category": "type",
306
- "light": "clamp(1.875rem, calc(3 * var(--fluid)), 2.625rem)",
344
+ "light": "calc(clamp(1.875rem, calc(3 * var(--fluid)), 2.625rem) * var(--display-scale, 1))",
307
345
  "comment": "band heading 30 → 42px"
308
346
  },
309
347
  "fs-heading": {
@@ -748,6 +786,17 @@ export const TOKENS = {
748
786
  "light": "var(--red-ink)",
749
787
  "comment": "Accent text sitting on an accent-muted panel, which --accent cannot do: it clears AA on paper by 4.58:1, and any tint of itself underneath pushes it below 4.5. This names the situation so the choice is not left to whoever writes the next panel."
750
788
  },
789
+ "action": {
790
+ "layer": "semantic",
791
+ "category": "other",
792
+ "light": "var(--accent)",
793
+ "comment": "The SOLID fill of a primary action or a chosen option — the primary button, the sliding pill of a segmented control, the active filter chip, the current page. It follows the accent unless `data-action` says otherwise: a preset that keeps its accent for links can hand these large fills back to ink, while small marks (a checked box, a switch, a slider's range) keep the accent."
794
+ },
795
+ "action-foreground": {
796
+ "layer": "semantic",
797
+ "category": "other",
798
+ "light": "var(--accent-foreground)"
799
+ },
751
800
  "success": {
752
801
  "layer": "semantic",
753
802
  "category": "colour",
package/dist/tokens.json CHANGED
@@ -136,6 +136,13 @@
136
136
  "light": "var(--ink)",
137
137
  "dark": "var(--ink)"
138
138
  },
139
+ "rule-divider": {
140
+ "layer": "tokens",
141
+ "category": "colour",
142
+ "light": "var(--rule-hard)",
143
+ "comment": "The hard rule as a section DIVIDER — under a band heading, above a form, between groups. Its own name so the rules axis can take dividers away without touching the hard edges controls draw (a slider track, a table head, a quotation bar).",
144
+ "dark": "var(--rule-hard)"
145
+ },
139
146
  "red": {
140
147
  "layer": "tokens",
141
148
  "category": "colour",
@@ -293,16 +300,47 @@
293
300
  "category": "space",
294
301
  "light": "52px"
295
302
  },
303
+ "display-scale": {
304
+ "layer": "tokens",
305
+ "category": "other",
306
+ "light": "1"
307
+ },
308
+ "display-weight": {
309
+ "layer": "tokens",
310
+ "category": "other",
311
+ "light": "300"
312
+ },
313
+ "label-font": {
314
+ "layer": "tokens",
315
+ "category": "other",
316
+ "light": "var(--font-mono)"
317
+ },
318
+ "label-case": {
319
+ "layer": "tokens",
320
+ "category": "other",
321
+ "light": "uppercase"
322
+ },
323
+ "label-tracking-scale": {
324
+ "layer": "tokens",
325
+ "category": "other",
326
+ "light": "1"
327
+ },
328
+ "body-weight": {
329
+ "layer": "tokens",
330
+ "category": "other",
331
+ "light": "300",
332
+ "comment": "Running text: the White Reset sets it light; a quiet voice sets it at a regular weight to match its regular-weight headings."
333
+ },
296
334
  "fs-title": {
297
335
  "layer": "tokens",
298
336
  "category": "type",
299
- "light": "clamp(2.5rem, calc(4.6 * var(--fluid)), 3.375rem)",
337
+ "light": "calc(clamp(2.5rem, calc(4.6 * var(--fluid)), 3.375rem) * var(--display-scale, 1))",
300
338
  "comment": "page h1 40 → 54px"
301
339
  },
302
340
  "fs-lead": {
303
341
  "layer": "tokens",
304
342
  "category": "type",
305
- "light": "clamp(1.875rem, calc(3 * var(--fluid)), 2.625rem)",
343
+ "light": "calc(clamp(1.875rem, calc(3 * var(--fluid)), 2.625rem) * var(--display-scale, 1))",
306
344
  "comment": "band heading 30 → 42px"
307
345
  },
308
346
  "fs-heading": {
@@ -747,6 +785,17 @@
747
785
  "light": "var(--red-ink)",
748
786
  "comment": "Accent text sitting on an accent-muted panel, which --accent cannot do: it clears AA on paper by 4.58:1, and any tint of itself underneath pushes it below 4.5. This names the situation so the choice is not left to whoever writes the next panel."
749
787
  },
788
+ "action": {
789
+ "layer": "semantic",
790
+ "category": "other",
791
+ "light": "var(--accent)",
792
+ "comment": "The SOLID fill of a primary action or a chosen option — the primary button, the sliding pill of a segmented control, the active filter chip, the current page. It follows the accent unless `data-action` says otherwise: a preset that keeps its accent for links can hand these large fills back to ink, while small marks (a checked box, a switch, a slider's range) keep the accent."
793
+ },
794
+ "action-foreground": {
795
+ "layer": "semantic",
796
+ "category": "other",
797
+ "light": "var(--accent-foreground)"
798
+ },
750
799
  "success": {
751
800
  "layer": "semantic",
752
801
  "category": "colour",
@@ -0,0 +1,32 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface ChronologyItem {
5
+ id: string;
6
+ title: ReactNode;
7
+ organisation?: ReactNode;
8
+ /** Decimal years, e.g. 2019.5 for July 2019. */
9
+ start: number;
10
+ /** Decimal years; omit for an item that is still going. */
11
+ end?: number;
12
+ kind: 'role' | 'study';
13
+ }
14
+ interface ChronologyProps {
15
+ /** Accessible name of the chart. */
16
+ label: string;
17
+ /** First and last year on the axis, inclusive. */
18
+ from: number;
19
+ to: number;
20
+ items: ChronologyItem[];
21
+ /** Text marking a current item, such as "Now". */
22
+ currentLabel?: ReactNode;
23
+ }
24
+ /**
25
+ * Roles and studies as bars on one year axis, one lane each. Roles are filled,
26
+ * studies outlined, and a current role is ink with an accent end. The bars are
27
+ * a list underneath, so a screen reader hears titles and dates rather than
28
+ * geometry.
29
+ */
30
+ declare function Chronology({ label, from, to, items, currentLabel }: ChronologyProps): react.JSX.Element;
31
+
32
+ export { Chronology, type ChronologyItem, type ChronologyProps };
@@ -0,0 +1,32 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ function Chronology({ label, from, to, items, currentLabel }) {
3
+ const span = to + 1 - from;
4
+ const years = Array.from({ length: to - from + 1 }, (_, index) => from + index);
5
+ const position = (value) => `${((value - from) / span * 100).toFixed(3)}%`;
6
+ return /* @__PURE__ */ jsxs("figure", { className: "folio-wb-chronology", "aria-label": label, children: [
7
+ /* @__PURE__ */ jsx("ol", { children: items.map((item) => {
8
+ const current = item.end === void 0;
9
+ const align = current || (item.start - from) / span >= 0.4 ? "end" : "start";
10
+ const style = { "--start": position(item.start), "--end": position(item.end ?? to + 1) };
11
+ return /* @__PURE__ */ jsxs("li", { "data-kind": item.kind, "data-current": current || void 0, "data-align": align, style, children: [
12
+ /* @__PURE__ */ jsxs("p", { children: [
13
+ /* @__PURE__ */ jsx("span", { children: item.title }),
14
+ item.organisation && /* @__PURE__ */ jsxs("span", { className: "folio-wb-chronology-org", children: [
15
+ " \xB7 ",
16
+ item.organisation
17
+ ] }),
18
+ current && currentLabel && /* @__PURE__ */ jsxs("span", { className: "folio-wb-chronology-now", children: [
19
+ " \xB7 ",
20
+ currentLabel
21
+ ] })
22
+ ] }),
23
+ /* @__PURE__ */ jsx("span", { className: "folio-wb-chronology-bar", "aria-hidden": true })
24
+ ] }, item.id);
25
+ }) }),
26
+ /* @__PURE__ */ jsx("div", { className: "folio-wb-chronology-axis", "aria-hidden": true, children: years.map((year) => /* @__PURE__ */ jsx("span", { children: year }, year)) })
27
+ ] });
28
+ }
29
+ export {
30
+ Chronology
31
+ };
32
+ //# sourceMappingURL=Chronology.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/website/Workbench/Chronology.tsx"],"sourcesContent":["import type { CSSProperties, ReactNode } from 'react'\n\nexport interface ChronologyItem {\n id: string\n title: ReactNode\n organisation?: ReactNode\n /** Decimal years, e.g. 2019.5 for July 2019. */\n start: number\n /** Decimal years; omit for an item that is still going. */\n end?: number\n kind: 'role' | 'study'\n}\n\nexport interface ChronologyProps {\n /** Accessible name of the chart. */\n label: string\n /** First and last year on the axis, inclusive. */\n from: number\n to: number\n items: ChronologyItem[]\n /** Text marking a current item, such as \"Now\". */\n currentLabel?: ReactNode\n}\n\n/**\n * Roles and studies as bars on one year axis, one lane each. Roles are filled,\n * studies outlined, and a current role is ink with an accent end. The bars are\n * a list underneath, so a screen reader hears titles and dates rather than\n * geometry.\n */\nexport function Chronology({ label, from, to, items, currentLabel }: ChronologyProps) {\n const span = to + 1 - from\n const years = Array.from({ length: to - from + 1 }, (_, index) => from + index)\n const position = (value: number) => `${(((value - from) / span) * 100).toFixed(3)}%`\n return (\n <figure className=\"folio-wb-chronology\" aria-label={label}>\n <ol>\n {items.map((item) => {\n const current = item.end === undefined\n // A bar that starts in the later part of the axis hangs its label from its end,\n // so a long title never runs off the right of the axis.\n const align = current || (item.start - from) / span >= 0.4 ? 'end' : 'start'\n const style = { '--start': position(item.start), '--end': position(item.end ?? to + 1) } as CSSProperties\n return (\n <li key={item.id} data-kind={item.kind} data-current={current || undefined} data-align={align} style={style}>\n <p>\n <span>{item.title}</span>\n {item.organisation && <span className=\"folio-wb-chronology-org\"> · {item.organisation}</span>}\n {current && currentLabel && <span className=\"folio-wb-chronology-now\"> · {currentLabel}</span>}\n </p>\n <span className=\"folio-wb-chronology-bar\" aria-hidden />\n </li>\n )\n })}\n </ol>\n <div className=\"folio-wb-chronology-axis\" aria-hidden>\n {years.map((year) => (\n <span key={year}>{year}</span>\n ))}\n </div>\n </figure>\n )\n}\n"],"mappings":"AA8CgB,cACsB,YADtB;AAhBT,SAAS,WAAW,EAAE,OAAO,MAAM,IAAI,OAAO,aAAa,GAAoB;AACpF,QAAM,OAAO,KAAK,IAAI;AACtB,QAAM,QAAQ,MAAM,KAAK,EAAE,QAAQ,KAAK,OAAO,EAAE,GAAG,CAAC,GAAG,UAAU,OAAO,KAAK;AAC9E,QAAM,WAAW,CAAC,UAAkB,KAAM,QAAQ,QAAQ,OAAQ,KAAK,QAAQ,CAAC,CAAC;AACjF,SACE,qBAAC,YAAO,WAAU,uBAAsB,cAAY,OAClD;AAAA,wBAAC,QACE,gBAAM,IAAI,CAAC,SAAS;AACnB,YAAM,UAAU,KAAK,QAAQ;AAG7B,YAAM,QAAQ,YAAY,KAAK,QAAQ,QAAQ,QAAQ,MAAM,QAAQ;AACrE,YAAM,QAAQ,EAAE,WAAW,SAAS,KAAK,KAAK,GAAG,SAAS,SAAS,KAAK,OAAO,KAAK,CAAC,EAAE;AACvF,aACE,qBAAC,QAAiB,aAAW,KAAK,MAAM,gBAAc,WAAW,QAAW,cAAY,OAAO,OAC7F;AAAA,6BAAC,OACC;AAAA,8BAAC,UAAM,eAAK,OAAM;AAAA,UACjB,KAAK,gBAAgB,qBAAC,UAAK,WAAU,2BAA0B;AAAA;AAAA,YAAI,KAAK;AAAA,aAAa;AAAA,UACrF,WAAW,gBAAgB,qBAAC,UAAK,WAAU,2BAA0B;AAAA;AAAA,YAAI;AAAA,aAAa;AAAA,WACzF;AAAA,QACA,oBAAC,UAAK,WAAU,2BAA0B,eAAW,MAAC;AAAA,WAN/C,KAAK,EAOd;AAAA,IAEJ,CAAC,GACH;AAAA,IACA,oBAAC,SAAI,WAAU,4BAA2B,eAAW,MAClD,gBAAM,IAAI,CAAC,SACV,oBAAC,UAAiB,kBAAP,IAAY,CACxB,GACH;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,13 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface ContactRowProps {
5
+ /** The address, set large — usually a `mailto:` link. */
6
+ address: ReactNode;
7
+ /** One button beside it. */
8
+ action?: ReactNode;
9
+ }
10
+ /** An address and one action on a single line of the content subgrid. */
11
+ declare function ContactRow({ address, action }: ContactRowProps): react.JSX.Element;
12
+
13
+ export { ContactRow, type ContactRowProps };
@@ -0,0 +1,11 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ function ContactRow({ address, action }) {
3
+ return /* @__PURE__ */ jsxs("div", { className: "folio-wb-contact", children: [
4
+ /* @__PURE__ */ jsx("div", { className: "folio-wb-contact-address", children: address }),
5
+ action && /* @__PURE__ */ jsx("div", { className: "folio-wb-contact-action", children: action })
6
+ ] });
7
+ }
8
+ export {
9
+ ContactRow
10
+ };
11
+ //# sourceMappingURL=ContactRow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/website/Workbench/ContactRow.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\nexport interface ContactRowProps {\n /** The address, set large — usually a `mailto:` link. */\n address: ReactNode\n /** One button beside it. */\n action?: ReactNode\n}\n\n/** An address and one action on a single line of the content subgrid. */\nexport function ContactRow({ address, action }: ContactRowProps) {\n return (\n <div className=\"folio-wb-contact\">\n <div className=\"folio-wb-contact-address\">{address}</div>\n {action && <div className=\"folio-wb-contact-action\">{action}</div>}\n </div>\n )\n}\n"],"mappings":"AAYI,SACE,KADF;AAFG,SAAS,WAAW,EAAE,SAAS,OAAO,GAAoB;AAC/D,SACE,qBAAC,SAAI,WAAU,oBACb;AAAA,wBAAC,SAAI,WAAU,4BAA4B,mBAAQ;AAAA,IAClD,UAAU,oBAAC,SAAI,WAAU,2BAA2B,kBAAO;AAAA,KAC9D;AAEJ;","names":[]}
@@ -0,0 +1,13 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface PhotoMosaicProps {
5
+ /** Exactly the photographs to show: the first is the large one, the next four fill a 2×2 beside it. */
6
+ children: ReactNode[];
7
+ /** Caption under the large photograph. */
8
+ caption?: ReactNode;
9
+ }
10
+ /** One large photograph beside a 2×2 of smaller ones, all on the content subgrid. */
11
+ declare function PhotoMosaic({ children, caption }: PhotoMosaicProps): react.JSX.Element;
12
+
13
+ export { PhotoMosaic, type PhotoMosaicProps };
@@ -0,0 +1,15 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ function PhotoMosaic({ children, caption }) {
3
+ const [lead, ...rest] = children;
4
+ return /* @__PURE__ */ jsxs("div", { className: "folio-wb-mosaic", children: [
5
+ /* @__PURE__ */ jsxs("figure", { className: "folio-wb-mosaic-lead", children: [
6
+ /* @__PURE__ */ jsx("div", { className: "folio-wb-mosaic-frame", children: lead }),
7
+ caption && /* @__PURE__ */ jsx("figcaption", { children: caption })
8
+ ] }),
9
+ rest.slice(0, 4).map((child, index) => /* @__PURE__ */ jsx("div", { className: "folio-wb-mosaic-frame", children: child }, index))
10
+ ] });
11
+ }
12
+ export {
13
+ PhotoMosaic
14
+ };
15
+ //# sourceMappingURL=PhotoMosaic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/website/Workbench/PhotoMosaic.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\nexport interface PhotoMosaicProps {\n /** Exactly the photographs to show: the first is the large one, the next four fill a 2×2 beside it. */\n children: ReactNode[]\n /** Caption under the large photograph. */\n caption?: ReactNode\n}\n\n/** One large photograph beside a 2×2 of smaller ones, all on the content subgrid. */\nexport function PhotoMosaic({ children, caption }: PhotoMosaicProps) {\n const [lead, ...rest] = children\n return (\n <div className=\"folio-wb-mosaic\">\n <figure className=\"folio-wb-mosaic-lead\">\n <div className=\"folio-wb-mosaic-frame\">{lead}</div>\n {caption && <figcaption>{caption}</figcaption>}\n </figure>\n {rest.slice(0, 4).map((child, index) => (\n <div key={index} className=\"folio-wb-mosaic-frame\">\n {child}\n </div>\n ))}\n </div>\n )\n}\n"],"mappings":"AAcM,SACE,KADF;AAJC,SAAS,YAAY,EAAE,UAAU,QAAQ,GAAqB;AACnE,QAAM,CAAC,MAAM,GAAG,IAAI,IAAI;AACxB,SACE,qBAAC,SAAI,WAAU,mBACb;AAAA,yBAAC,YAAO,WAAU,wBAChB;AAAA,0BAAC,SAAI,WAAU,yBAAyB,gBAAK;AAAA,MAC5C,WAAW,oBAAC,gBAAY,mBAAQ;AAAA,OACnC;AAAA,IACC,KAAK,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,OAAO,UAC5B,oBAAC,SAAgB,WAAU,yBACxB,mBADO,KAEV,CACD;AAAA,KACH;AAEJ;","names":[]}
@@ -0,0 +1,19 @@
1
+ import * as react from 'react';
2
+ import { ReactElement } from 'react';
3
+
4
+ interface RevealProps {
5
+ /** One element; it keeps its own tag and receives the reveal state. */
6
+ children: ReactElement;
7
+ }
8
+ /**
9
+ * Fades and lifts its child the first time it scrolls into view.
10
+ *
11
+ * The server renders the child visible. Only after hydration, and only for an
12
+ * element that starts below the fold, is it hidden and then revealed — so a
13
+ * page without JavaScript, a crawler and anything already on screen never see
14
+ * an empty frame. Reduced motion skips the whole thing, and the duration is the
15
+ * motion axis's own, so `data-motion="still"` makes it instant.
16
+ */
17
+ declare function Reveal({ children }: RevealProps): react.JSX.Element;
18
+
19
+ export { Reveal, type RevealProps };
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Slot } from "@radix-ui/react-slot";
4
+ import { useEffect, useRef } from "react";
5
+ function Reveal({ children }) {
6
+ const ref = useRef(null);
7
+ useEffect(() => {
8
+ const node = ref.current;
9
+ if (!node || typeof IntersectionObserver === "undefined") return;
10
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
11
+ if (node.getBoundingClientRect().top < window.innerHeight) return;
12
+ node.dataset.reveal = "hidden";
13
+ const observer = new IntersectionObserver(
14
+ (entries) => {
15
+ if (!entries.some((entry) => entry.isIntersecting)) return;
16
+ node.dataset.reveal = "shown";
17
+ observer.disconnect();
18
+ },
19
+ { rootMargin: "0px 0px -10% 0px" }
20
+ );
21
+ observer.observe(node);
22
+ return () => observer.disconnect();
23
+ }, []);
24
+ return /* @__PURE__ */ jsx(Slot, { ref, className: "folio-wb-reveal", children });
25
+ }
26
+ export {
27
+ Reveal
28
+ };
29
+ //# sourceMappingURL=Reveal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/website/Workbench/Reveal.tsx"],"sourcesContent":["'use client'\n\nimport { Slot } from '@radix-ui/react-slot'\nimport { useEffect, useRef, type ReactElement } from 'react'\n\nexport interface RevealProps {\n /** One element; it keeps its own tag and receives the reveal state. */\n children: ReactElement\n}\n\n/**\n * Fades and lifts its child the first time it scrolls into view.\n *\n * The server renders the child visible. Only after hydration, and only for an\n * element that starts below the fold, is it hidden and then revealed — so a\n * page without JavaScript, a crawler and anything already on screen never see\n * an empty frame. Reduced motion skips the whole thing, and the duration is the\n * motion axis's own, so `data-motion=\"still\"` makes it instant.\n */\nexport function Reveal({ children }: RevealProps) {\n const ref = useRef<HTMLElement>(null)\n useEffect(() => {\n const node = ref.current\n if (!node || typeof IntersectionObserver === 'undefined') return\n if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) return\n if (node.getBoundingClientRect().top < window.innerHeight) return\n node.dataset.reveal = 'hidden'\n const observer = new IntersectionObserver(\n (entries) => {\n if (!entries.some((entry) => entry.isIntersecting)) return\n node.dataset.reveal = 'shown'\n observer.disconnect()\n },\n { rootMargin: '0px 0px -10% 0px' },\n )\n observer.observe(node)\n return () => observer.disconnect()\n }, [])\n return <Slot ref={ref} className=\"folio-wb-reveal\">{children}</Slot>\n}\n"],"mappings":";AAsCS;AApCT,SAAS,YAAY;AACrB,SAAS,WAAW,cAAiC;AAgB9C,SAAS,OAAO,EAAE,SAAS,GAAgB;AAChD,QAAM,MAAM,OAAoB,IAAI;AACpC,YAAU,MAAM;AACd,UAAM,OAAO,IAAI;AACjB,QAAI,CAAC,QAAQ,OAAO,yBAAyB,YAAa;AAC1D,QAAI,OAAO,aAAa,kCAAkC,EAAE,QAAS;AACrE,QAAI,KAAK,sBAAsB,EAAE,MAAM,OAAO,YAAa;AAC3D,SAAK,QAAQ,SAAS;AACtB,UAAM,WAAW,IAAI;AAAA,MACnB,CAAC,YAAY;AACX,YAAI,CAAC,QAAQ,KAAK,CAAC,UAAU,MAAM,cAAc,EAAG;AACpD,aAAK,QAAQ,SAAS;AACtB,iBAAS,WAAW;AAAA,MACtB;AAAA,MACA,EAAE,YAAY,mBAAmB;AAAA,IACnC;AACA,aAAS,QAAQ,IAAI;AACrB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,CAAC;AACL,SAAO,oBAAC,QAAK,KAAU,WAAU,mBAAmB,UAAS;AAC/D;","names":[]}
@@ -0,0 +1,18 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface ToolStripItem {
5
+ id: string;
6
+ /** The tool's name; it is the tile's accessible label and tooltip. */
7
+ name: string;
8
+ /** A monochrome mark drawn in `currentColor`. */
9
+ icon: ReactNode;
10
+ }
11
+ interface ToolStripProps {
12
+ label: ReactNode;
13
+ items: ToolStripItem[];
14
+ }
15
+ /** A row of equal tiles, one monochrome tool mark each, under a centred caption. */
16
+ declare function ToolStrip({ label, items }: ToolStripProps): react.JSX.Element | null;
17
+
18
+ export { ToolStrip, type ToolStripItem, type ToolStripProps };
@@ -0,0 +1,15 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ function ToolStrip({ label, items }) {
3
+ if (!items.length) return null;
4
+ return /* @__PURE__ */ jsxs("section", { className: "folio-wb-tools", "aria-label": typeof label === "string" ? label : void 0, children: [
5
+ /* @__PURE__ */ jsx("p", { className: "folio-wb-tools-label", children: label }),
6
+ /* @__PURE__ */ jsx("ul", { children: items.map((item) => /* @__PURE__ */ jsxs("li", { title: item.name, children: [
7
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: item.icon }),
8
+ /* @__PURE__ */ jsx("span", { className: "folio-wb-visually-hidden", children: item.name })
9
+ ] }, item.id)) })
10
+ ] });
11
+ }
12
+ export {
13
+ ToolStrip
14
+ };
15
+ //# sourceMappingURL=ToolStrip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/website/Workbench/ToolStrip.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\nexport interface ToolStripItem {\n id: string\n /** The tool's name; it is the tile's accessible label and tooltip. */\n name: string\n /** A monochrome mark drawn in `currentColor`. */\n icon: ReactNode\n}\n\nexport interface ToolStripProps {\n label: ReactNode\n items: ToolStripItem[]\n}\n\n/** A row of equal tiles, one monochrome tool mark each, under a centred caption. */\nexport function ToolStrip({ label, items }: ToolStripProps) {\n if (!items.length) return null\n return (\n <section className=\"folio-wb-tools\" aria-label={typeof label === 'string' ? label : undefined}>\n <p className=\"folio-wb-tools-label\">{label}</p>\n <ul>\n {items.map((item) => (\n <li key={item.id} title={item.name}>\n <span aria-hidden>{item.icon}</span>\n <span className=\"folio-wb-visually-hidden\">{item.name}</span>\n </li>\n ))}\n </ul>\n </section>\n )\n}\n"],"mappings":"AAoBM,cAGI,YAHJ;AAJC,SAAS,UAAU,EAAE,OAAO,MAAM,GAAmB;AAC1D,MAAI,CAAC,MAAM,OAAQ,QAAO;AAC1B,SACE,qBAAC,aAAQ,WAAU,kBAAiB,cAAY,OAAO,UAAU,WAAW,QAAQ,QAClF;AAAA,wBAAC,OAAE,WAAU,wBAAwB,iBAAM;AAAA,IAC3C,oBAAC,QACE,gBAAM,IAAI,CAAC,SACV,qBAAC,QAAiB,OAAO,KAAK,MAC5B;AAAA,0BAAC,UAAK,eAAW,MAAE,eAAK,MAAK;AAAA,MAC7B,oBAAC,UAAK,WAAU,4BAA4B,eAAK,MAAK;AAAA,SAF/C,KAAK,EAGd,CACD,GACH;AAAA,KACF;AAEJ;","names":[]}