@crewlethq/ui 0.12.0 → 0.14.0

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 (157) hide show
  1. package/README.md +82 -5
  2. package/dist/AppShell/index.js +2 -1
  3. package/dist/Avatar/index.js +1 -1
  4. package/dist/{Avatar-QHYOFID3.css → Avatar-BPJ36QZR.css} +1 -1
  5. package/dist/AvatarPicker/index.js +5 -5
  6. package/dist/Breadcrumb/Breadcrumb.d.ts +68 -0
  7. package/dist/Breadcrumb/index.d.ts +3 -0
  8. package/dist/Breadcrumb/index.js +3 -0
  9. package/dist/Breadcrumb-YMWVM3SV.css +193 -0
  10. package/dist/ChoiceButtons/ChoiceButtons.d.ts +43 -0
  11. package/dist/ChoiceButtons/index.d.ts +3 -0
  12. package/dist/ChoiceButtons/index.js +3 -0
  13. package/dist/ChoiceButtons-DJ5EOGUA.css +127 -0
  14. package/dist/Combobox/index.js +2 -2
  15. package/dist/CommandPalette/CommandPalette.d.ts +48 -1
  16. package/dist/CommandPalette/index.js +3 -3
  17. package/dist/{CommandPalette-C5AMKGN2.css → CommandPalette-HBTNKHZQ.css} +55 -2
  18. package/dist/DataTable/index.js +4 -4
  19. package/dist/DataView/index.js +11 -11
  20. package/dist/DateTimePicker/index.js +2 -2
  21. package/dist/{DateTimePicker-4AZBTODT.css → DateTimePicker-NJ6XH2TE.css} +4 -3
  22. package/dist/Document/Document.d.ts +56 -0
  23. package/dist/Document/index.d.ts +3 -0
  24. package/dist/Document/index.js +2 -0
  25. package/dist/Document-PQGXJHB7.css +288 -0
  26. package/dist/EntityChip/index.js +1 -1
  27. package/dist/Highlight/Highlight.d.ts +45 -0
  28. package/dist/Highlight/index.d.ts +3 -0
  29. package/dist/Highlight/index.js +2 -0
  30. package/dist/Highlight-DXLV2R6L.css +29 -0
  31. package/dist/HuePicker/index.js +3 -3
  32. package/dist/ImageUpload/index.js +2 -2
  33. package/dist/{ImageUpload-L2GE4JUM.css → ImageUpload-NVGMN7DP.css} +9 -6
  34. package/dist/InboxList/InboxList.d.ts +116 -0
  35. package/dist/InboxList/index.d.ts +3 -0
  36. package/dist/InboxList/index.js +5 -0
  37. package/dist/InboxList-YMFWKGGS.css +252 -0
  38. package/dist/KeyChip/KeyChip.d.ts +21 -0
  39. package/dist/KeyChip/index.d.ts +3 -0
  40. package/dist/KeyChip/index.js +2 -0
  41. package/dist/KeyChip-4XJU4BBE.css +30 -0
  42. package/dist/Link/Link.d.ts +17 -2
  43. package/dist/Link/index.js +1 -1
  44. package/dist/{Link-6BEFNMN2.css → Link-WPMVLKLY.css} +36 -1
  45. package/dist/Listbox/index.d.ts +2 -0
  46. package/dist/Listbox/index.js +1 -1
  47. package/dist/Listbox/useHighlightRest.d.ts +35 -0
  48. package/dist/MarkdownEditor/MarkdownEditor.d.ts +93 -0
  49. package/dist/MarkdownEditor/format.d.ts +34 -0
  50. package/dist/MarkdownEditor/index.d.ts +5 -0
  51. package/dist/MarkdownEditor/index.js +12 -0
  52. package/dist/MarkdownEditor-ZLSHTOIZ.css +146 -0
  53. package/dist/Modal/Modal.d.ts +5 -1
  54. package/dist/Modal/index.js +1 -1
  55. package/dist/{Modal-BNT3TDV2.css → Modal-RQAYGG4L.css} +34 -1
  56. package/dist/OrgLabel/index.js +1 -1
  57. package/dist/OrgNode/index.js +1 -1
  58. package/dist/OrgTable/index.js +4 -4
  59. package/dist/Outline/Outline.d.ts +72 -0
  60. package/dist/Outline/index.d.ts +3 -0
  61. package/dist/Outline/index.js +2 -0
  62. package/dist/Outline-VCXKKU3E.css +78 -0
  63. package/dist/Popover/Popover.d.ts +21 -1
  64. package/dist/Popover/index.js +1 -1
  65. package/dist/ReadingPane/ReadingPane.d.ts +95 -0
  66. package/dist/ReadingPane/index.d.ts +3 -0
  67. package/dist/ReadingPane/index.js +4 -0
  68. package/dist/ReadingPane-5UOLPWY5.css +194 -0
  69. package/dist/SegmentedControl/SegmentedControl.d.ts +9 -1
  70. package/dist/SegmentedControl/index.js +3 -3
  71. package/dist/{SegmentedControl-76HODRBT.css → SegmentedControl-PKBLJVSA.css} +18 -0
  72. package/dist/Select/Select.d.ts +22 -0
  73. package/dist/Select/index.js +2 -2
  74. package/dist/{Select-4ANVLHUY.css → Select-2P7AWUE4.css} +56 -6
  75. package/dist/SidebarNav/index.js +2 -1
  76. package/dist/{SidebarNav-X5NLZNWA.css → SidebarNav-KF66MLOM.css} +4 -19
  77. package/dist/Switcher/Switcher.d.ts +151 -0
  78. package/dist/Switcher/index.d.ts +3 -0
  79. package/dist/Switcher/index.js +16 -0
  80. package/dist/Switcher-B44Q4WF4.css +346 -0
  81. package/dist/Tabs/Tabs.d.ts +7 -1
  82. package/dist/Tabs/index.js +1 -1
  83. package/dist/{Tabs-5I5Q5KKM.css → Tabs-ITJYJFMU.css} +49 -0
  84. package/dist/TagsInput/index.js +2 -2
  85. package/dist/ThemeGallery/ThemeGallery.d.ts +65 -0
  86. package/dist/ThemeGallery/index.d.ts +3 -0
  87. package/dist/ThemeGallery/index.js +6 -0
  88. package/dist/ThemeGallery-NITASULQ.css +93 -0
  89. package/dist/ThemeSwatch/ThemeSwatch.d.ts +71 -0
  90. package/dist/ThemeSwatch/index.d.ts +3 -0
  91. package/dist/ThemeSwatch/index.js +2 -0
  92. package/dist/ThemeSwatch-4DL457ZB.css +256 -0
  93. package/dist/ThemeSwitcher/ThemeSwitcher.d.ts +5 -1
  94. package/dist/ThemeSwitcher/index.js +4 -4
  95. package/dist/ThemeToggle/ThemeToggle.d.ts +1 -1
  96. package/dist/ThemeToggle/index.js +1 -1
  97. package/dist/TimeWindowPicker/index.js +4 -4
  98. package/dist/{TimeWindowPicker-GBF4JDFI.css → TimeWindowPicker-BZ5HHATU.css} +5 -4
  99. package/dist/Toaster/index.js +1 -1
  100. package/dist/{Toaster-NVDCEIP5.css → Toaster-VZKISP2S.css} +81 -87
  101. package/dist/Tree/index.d.ts +1 -1
  102. package/dist/Tree/index.js +1 -1
  103. package/dist/Tree/useTreeState.d.ts +37 -7
  104. package/dist/TreeCanvas/index.js +2 -2
  105. package/dist/TreeGrid/TreeGrid.d.ts +22 -2
  106. package/dist/TreeGrid/index.js +2 -2
  107. package/dist/{TreeGrid-NSW7VMOX.css → TreeGrid-FBX3MSJA.css} +19 -0
  108. package/dist/{chunk-EBBAAQDY.js → chunk-26PEPONN.js} +3 -1
  109. package/dist/{chunk-GOUKOXJK.js → chunk-3VVKJ7AZ.js} +3 -3
  110. package/dist/{chunk-HV6IZFBK.js → chunk-4FFHJBEN.js} +2 -2
  111. package/dist/{chunk-JHQUT4WJ.js → chunk-4LDTQWS7.js} +1 -1
  112. package/dist/{chunk-GXYNEDNA.js → chunk-4Y7WKXAW.js} +1 -1
  113. package/dist/{chunk-IINFVK3W.js → chunk-5IXMLWGW.js} +6 -6
  114. package/dist/{chunk-MCPKHYMC.js → chunk-6LXMLQUM.js} +17 -4
  115. package/dist/{chunk-3UK4VFSK.js → chunk-6NIZCQUM.js} +1 -1
  116. package/dist/{chunk-ORNOOMVY.js → chunk-6TSZCBID.js} +2 -2
  117. package/dist/{chunk-ZMJHK7DR.js → chunk-75WOCYGC.js} +2 -2
  118. package/dist/chunk-77IEHFY4.js +299 -0
  119. package/dist/chunk-A5YKNRG6.js +94 -0
  120. package/dist/{chunk-JCNUQ3US.js → chunk-AXNQCAFC.js} +2 -2
  121. package/dist/{chunk-UG5ZMAXW.js → chunk-CBZSSKH3.js} +5 -5
  122. package/dist/{chunk-LAQK4V3Z.js → chunk-DJMLHCLD.js} +1 -1
  123. package/dist/{chunk-6MU7HNB4.js → chunk-F73MC7EZ.js} +47 -2
  124. package/dist/{chunk-ZPSOF6VL.js → chunk-FBSEJO2C.js} +1 -1
  125. package/dist/chunk-FYTIJM2T.js +40 -0
  126. package/dist/chunk-H6VKCIWV.js +41 -0
  127. package/dist/{chunk-HLBRPES3.js → chunk-IFM63LAG.js} +3 -3
  128. package/dist/chunk-IG75YMUN.js +22 -0
  129. package/dist/{chunk-SEFMO2EV.js → chunk-IXUU5DLZ.js} +55 -6
  130. package/dist/{chunk-ZHOM65MI.js → chunk-JCO3SIST.js} +1 -1
  131. package/dist/chunk-KLIEPFEZ.js +95 -0
  132. package/dist/{chunk-7LD6P32I.js → chunk-L3CTFFLZ.js} +2 -18
  133. package/dist/chunk-MNFU2RNT.js +74 -0
  134. package/dist/{chunk-S3BCVWTU.js → chunk-MY374UUD.js} +49 -31
  135. package/dist/{chunk-K5Q2DK3P.js → chunk-NXDEPUVG.js} +1 -1
  136. package/dist/{chunk-PDUZO35I.js → chunk-O2ZDZZS7.js} +15 -6
  137. package/dist/{chunk-5WEVLIB6.js → chunk-RZ7RGECB.js} +1 -1
  138. package/dist/{chunk-WEIINZPA.js → chunk-SBRE7IML.js} +6 -2
  139. package/dist/{chunk-TEEJSTFG.js → chunk-TFMJH2E6.js} +1 -1
  140. package/dist/{chunk-IVFJD7WY.js → chunk-UCCC7YVZ.js} +3 -2
  141. package/dist/chunk-VWNC2L6W.js +110 -0
  142. package/dist/chunk-VYY56AMX.js +2 -0
  143. package/dist/chunk-WI6K5M5X.js +24 -0
  144. package/dist/{chunk-XV65NTIT.js → chunk-WYQOHFML.js} +94 -41
  145. package/dist/chunk-XYCADWZN.js +100 -0
  146. package/dist/chunk-Y6NR7QJ3.js +336 -0
  147. package/dist/chunk-YSJC7AZC.js +62 -0
  148. package/dist/chunk-ZAJHGDAW.js +93 -0
  149. package/dist/generated/index.d.ts +12 -0
  150. package/dist/generated/index.js +64 -51
  151. package/dist/index.js +64 -51
  152. package/dist/styles.css +2711 -402
  153. package/dist/utils/highlight.d.ts +17 -0
  154. package/dist/utils/useMediaMatch.d.ts +12 -0
  155. package/package.json +3 -3
  156. package/dist/chunk-UAQIUSSK.js +0 -2
  157. /package/dist/{chunk-74OVQWA5.js → chunk-6AR2T5Y4.js} +0 -0
package/README.md CHANGED
@@ -41,17 +41,17 @@ import { Button } from '@crewlethq/ui/Button'; // 8 KB of CSS, not 182 KB
41
41
  | Area | Components |
42
42
  | ---- | ---------- |
43
43
  | Actions | `Button` and `ButtonLink` (variants `primary`, `secondary`, `outline`, `ghost`, `accent`, `danger`; sizes `small`, `medium`, `large`; shapes `square`, `pill`), `IconButton`, `Menu`, `Link`, `Copyable`, `CopyButton`, `Kbd` |
44
- | Content and text | `Text` (the eight registers), `Prose`, `InlineCode`, `List` and `ListItem`, `DescriptionList`, `Timeline`, `RelativeTime` |
45
- | Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `ImageUpload`, `CharacterPicker`, `HuePicker`, `AvatarPicker`, `FactList`, `ChoiceList`, `Composer` |
44
+ | Content and text | `Text` (the eight registers), `Prose`, `Document` and `DocumentTitle`, `Highlight`, `InlineCode`, `List` and `ListItem`, `DescriptionList`, `Timeline`, `RelativeTime` |
45
+ | Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `ImageUpload`, `CharacterPicker`, `HuePicker`, `AvatarPicker`, `FactList`, `ChoiceList`, `Composer`, `MarkdownEditor` |
46
46
  | Data display | `DataTable`, `CopyableCell`, `Table`, `StatCard`, `Meter`, `SegmentedMeter`, `Stepper`, `PricingCard`, `Avatar`, `AvatarStack`, `EntityChip`, `CodeBlock`, `Skeleton`, `Eyebrow` |
47
47
  | List screens | `DataView`, and its parts on their own: `DataViewToolbar`, `FilterAxisBar`, `FilterAxisChip`, plus the filter model (`filterDefsFromColumns`, `applyColumnFilters`, `filterPredicate`, `blankFilterValue`) |
48
48
  | Feedback and overlays | `Callout`, `Toaster`, `Modal` (with `variant="sheet"`), `ConfirmModal`, `CommandPalette`, `Tooltip`, `Popover`, `Announcer` |
49
49
  | Charts | `Legend`, `BarList`, `StackedBar`, `StackedColumns`, `ActivityStrip`, `TimeSeries`, `Sparkline`, the reading layer they share (`ChartTooltip`, `useChartHover`), and the ramp (`DATA_COLORS`, `DATA_COLOR_OTHER`, `dataColor`) |
50
50
  | Marks | `Count`, `StatusDot`, `EmptyValue`, `VisuallyHidden` |
51
- | Hooks and seams | `useModalLayer`, `usePopupLayer`, `LayerHost`, `useListbox`, `useOptionKeys`, `useClipboard`, `useAnnouncer`, `useNow`, `formatRelative`, `HeadingLevelProvider`, `cx`, the node hues (`NODE_HUES`, `NODE_HUE_NAMES`, `isNodeHue`) |
52
- | Layout | `Container`, `Section`, `Card`, `Tabs`, `Accordion`, `Disclosure` |
51
+ | Hooks and seams | `useModalLayer`, `usePopupLayer`, `LayerHost`, `useListbox`, `useOptionKeys`, `useHighlightRest` and `HIGHLIGHT_REST_MS`, `useClipboard`, `useAnnouncer`, `useNow`, `formatRelative`, `HeadingLevelProvider`, `cx`, the node hues (`NODE_HUES`, `NODE_HUE_NAMES`, `isNodeHue`) |
52
+ | Layout | `Container`, `Section`, `Card`, `Tabs`, `Accordion`, `Disclosure`, `Outline` (with `useActiveHeading` and `scrollToHeading`), `TreeGrid` |
53
53
  | Application shell | `AppShell` with `AppShell.Rail`, `AppShell.Topbar` and `useAppShell`, `SidebarNav` and `NavItem`, `BrandLockup`, `SearchTrigger`, `PageHeader`, `Toolbar`, `Stack`, `Inline`, `Spacer`, `AutoGrid`, `ErrorBoundary`, `useShortcut`, `useFullscreen` |
54
- | Preferences | `ThemeToggle`, `ThemeSwitcher`, `DensitySwitcher`, `useThemePreference`, `useDensityPreference`, `useSystemTheme`, `resolveTheme`, `applyStoredPreferences` |
54
+ | Preferences | `ThemeToggle`, `ThemeSwitcher`, `DensitySwitcher`, `ThemeGallery`, `ThemeSwatch`, `useThemePreference`, `useDensityPreference`, `useSystemTheme`, `resolveTheme`, `applyStoredPreferences` |
55
55
 
56
56
  Storybook stories live under `apps/storybook/src/stories`.
57
57
 
@@ -74,6 +74,13 @@ land at once.
74
74
  `storageKey` shows one value; an application that owns the preference holds
75
75
  it with one `useThemePreference` and passes `value` and `onChange` to each
76
76
  control, which then writes nothing of its own.
77
+ - **Named themes.** Import `@crewlethq/tokens/css/themes/all` after
78
+ `css/themes` and set `data-palette` on `<html>` to a theme's id; removing it
79
+ paints the default. A theme is a pair of palettes, so `data-theme` keeps
80
+ choosing light or dark for every theme alike: the interface calls that
81
+ choice Mode and the palette Theme. `ThemeGallery` offers the themes and
82
+ `ThemeSwatch` draws one, both from the registry `@crewlethq/tokens`
83
+ exports, so a thumbnail shows a theme that is not the one on screen.
77
84
  - **Density.** Import `@crewlethq/tokens/css/density` and set
78
85
  `data-density="compact"` or `"comfortable"` on `<html>`. Every spacing and
79
86
  size token scales with it, and the small control and row steps floor at 24px
@@ -191,6 +198,76 @@ places:
191
198
  - A surface that pans or zooms dispatches `LAYER_REPOSITION_EVENT` on its host
192
199
  when the content beneath it moves, and every panel anchored inside follows.
193
200
 
201
+ ## Changes in 0.14.0
202
+
203
+ 0.14.0 draws toasts as the first release did and quiets the pickers.
204
+
205
+ **Toasts are the first release's strip again**: a dark strip on `--color-surface-background` with the large shadow and the rim light, a 12px corner, a 20px mark, a slide in from the right, and the countdown as a wash behind the words that drains from the right. A variant spends its hue on its mark (the feedback ink step) and its wash (the feedback soft step) only, so a failure is a rose mark on a faint rose wash with a rose action, never red words on a red edge. Reduced motion keeps the strip still and draws no wash.
206
+
207
+ **A picker's boundary is a button's.** `Select`, `DateTimePicker` and `TimeWindowPicker` draw their trigger on `--color-border-strong`, as the secondary button does, and step to `--color-border-strong-hover` under the pointer. Text boxes, checkboxes and switches keep `--color-border-control` at 3:1, as do the typed boxes inside the pickers' panels.
208
+
209
+ **A `Select` list that leads with marks ends its rows with the tick**, so a swatch, an avatar or a glyph starts at the panel's edge. A list of words keeps the tick's column first.
210
+
211
+ **`--crewlet-select-menu-max-height` is the list's height.** The shared listbox cap of min(360px, 60vh) no longer overrides a taller panel; the panel keeps the 60vh cap on the window.
212
+
213
+ `@crewlethq/tokens`: Origin's dark `--color-border-default` is #2e2e30, and the comfortable density is 1.16 (it was 1.14), a shade larger at every gap, pad, row and control.
214
+
215
+ What to change: a test that reads a failure toast's red edge or red words, or a picker's control boundary, reads the new ones. Everything else is unchanged.
216
+
217
+ ## Changes in 0.13.0
218
+
219
+ 0.13.0 adds the parts the engine console's themes, project switcher, inbox and knowledge base are built with, and calls the light and dark choice Mode.
220
+
221
+ **`ThemeSwatch`** draws a theme from its registry entry in `@crewlethq/tokens` (its `swatch`): `size="chip"` is a mark beside a name, `"card"` a picker's thumbnail and `"pane"` a preview large enough to read as a screen. Both palettes are drawn by default, dark beside light, and `mode` draws one. It is decoration unless given a `label`.
222
+
223
+ **`ThemeGallery`** is the choice of a theme: every theme as a card under its group, in one radio group whose arrows choose as they move. The chosen card is its own border in the accent. `onPreview` is told the card the pointer comes to rest on, and `null` when it leaves, for a pane beside the gallery. It reads `themeRegistry` and `themeGroups` by default; `themes`, `groups` and `columns` adapt it.
224
+
225
+ **`Select` options take `leading` and `hint`.** `leading` is drawn before the label, in the list and on the trigger while the option is chosen; `hint` is a quieter word at the end of the label's line. `onHighlight` is told where the highlight comes to rest: at once for a key, once the pointer has rested on a row for `HIGHLIGHT_REST_MS` (120 ms), and `null` when the list closes or the pointer leaves it.
226
+
227
+ **`CommandPalette` takes a `step`.** `{ label, onBack, startAt? }` draws a chip naming the step at the field's start, and the chip, Escape and Backspace in an empty field go back rather than closing. The step opens on the row `startAt` names (the choice in effect), or on its first row, and going back puts the highlight on the row that led in. A row whose `onSelect` returns `false` keeps the surface open, which is how a row leads into a step. `onHighlight` reports the resting highlight with the same timing as `Select`'s.
228
+
229
+ **`SegmentedControl` takes `fill`**: the row fills the width it is given and every option takes an equal share, so rows stacked in one panel draw one box. `ThemeSwitcher` and `DensitySwitcher` pass it through.
230
+
231
+ **`useHighlightRest`** is the timing `Select` and `CommandPalette` share, for a list of your own that previews its highlight.
232
+
233
+ **The light and dark choice is called Mode.** `ThemeSwitcher`'s row is named "Mode", and `ThemeToggle` says "Switch to light mode" and "Switch to dark mode": "Theme" now names the palette. Code keeps `data-theme`.
234
+
235
+ **Labels on the danger fill and the scrim read their own tokens.** `ImageUpload`'s remove button and a danger toast's action read `--color-text-on-danger`, and the upload scrim, its spinner and a seeded `Avatar` read `--color-text-on-scrim`, so they stay white on a theme whose accent carries a dark label.
236
+
237
+ **`Switcher`** is a page's subject drawn as a button, and the panel that switches it: a search band, a list that scrolls, and a footer that does not. It opens under its button, centred when it has none (a panel opened by a key), and as a sheet from the bottom edge below 640px. Focus stays in the field while the arrows walk `pinned`, a `message`'s actions, `groups` and `footer` as one listbox. A row with an `href` is a link, so a middle click, a Command click and Command+Enter open it in a new tab; a `held` row keeps its reason and is passed over, and a `keepsOpen` row (Try again) is taken without closing the panel. `loading`, `note` and `announcement` cover the rest, and the caller searches and ranks.
238
+
239
+ **`Breadcrumb`** moves into the kit from the engine dashboard: ancestors that lead back out, the page's own `h1` last, a floor measured from what the trail holds, and a tab stop only while it overflows. `current` draws a control, such as a `Switcher`, as the heading's words. Below 640px an ancestor with a glyph is drawn as its glyph alone, its word kept for a screen reader.
240
+
241
+ **`KeyChip`** draws a project's key as the small mono chip, with a search's letters in bold (`highlight`). `SidebarNav` now draws an item's `lead` with it.
242
+
243
+ **`Tabs` take `variant="chip"`**: compact chips whose selected one is outlined by its own accent hairline, with no fill and no underline, for an object's lenses beside its name.
244
+
245
+ **`Popover`** takes `alignOffset` (a shift along the anchor, or a function that measures one), `sideOffset` (its distance from the anchor) and `initialFocus`.
246
+
247
+ **`Modal` takes `placement="bottom"`**: a dialog pinned to the bottom edge at the window's width, rounded on its top corners, rising from that edge.
248
+
249
+ **`InboxList`** is an inbox's column: `InboxGroup` labels a run of rows, and each `InboxRow` is one control (a button, or a link with `href`) naming who it is from, a `qualifier` and a `meta` time, with its words in up to three lines and an `excerpt` in the quiet ink. `aside` sits under the control as a control of its own, for the task the row is about. `selected` takes the neutral pressed fill and `aria-current`; `unread`, `actionable` and `done` set the ink, and `unread` and `done` are read out. `InboxMore` and `InboxToggle` are the quiet toggles at the end of the list.
250
+
251
+ **`ChoiceButtons`** sends one answer with one press: a group of buttons as tall as a touch target, the `recommended` option first, filled and marked. `pending` and `disabledReason` hold every option still without taking its focus.
252
+
253
+ **`ReadingPane`** reads one item in a single column named by its title: `ReadingPaneByline`, `ReadingPaneTitle`, `ReadingPaneLead`, `ReadingPaneQuote` (`verbatim` for a machine's words), `ReadingPaneReasoning`, `ReadingPaneOutcome` (a status line) and `ReadingPaneSection`.
254
+
255
+ **`Link`** adds `variant="quiet"`, a reference among other words in `--color-text-link-quiet` that never underlines and steps up to the accent ink on hover, and `size="inherit"`.
256
+
257
+ **`Document`** sets rendered markdown as a document: headings, paragraphs, lists and task lists, quotes, code, tables, rules, links and images, every size a step on the type ramp. It styles the plain elements a renderer emits rather than parsing anything itself, so the application keeps its own parser and its own rules about what a body may contain. A task item is recognised by the checkbox it starts with and a wide table by the element it scrolls in; that element takes `tabIndex={0}`, a `region` role and a name. `size="sm"` sets it for a comment or a side pane, and `DocumentTitle` is the page's title above it.
258
+
259
+ **`Outline`** is "On this page": the headings of the document beside it, as buttons rather than anchors, because a hash router keeps its own route in the fragment. The entry for the section in view takes the accent edge and `aria-current="location"`. `useActiveHeading(ids, { root, offset })` says which heading that is, measured on a frame, and the last one once the scroller can go no further; `scrollToHeading(id)` brings a heading into view and moves focus to it.
260
+
261
+ **`MarkdownEditor`** writes markdown: a formatting bar that writes it at the caret (headings, bold, italic, code, the three lists and a table), the source in a monospace field, and Write and Preview as a tab list whose panel is the field or the preview. Every control is a toggle, Command or Control with B and I are bold and italic, and with Enter the caller's `onSubmit`. `renderPreview` draws the preview with the application's renderer, `actions` adds the caller's own controls to the bar (they are handed the editor, so a "Link a page" picker inserts at the caret) and `footer` holds a count. The rules are pure functions a caller can use alone: `formatMarkdown`, `insertMarkdown` and `markdownWords`.
262
+
263
+ **`Highlight`** marks the words a search matched in a line of text, in `mark` elements on a ground behind the text's own ink. `highlightTerms(query)` turns a query into its terms and `highlightRanges(text, terms)` says where they start a word, never inside one.
264
+
265
+ **`TreeGrid` reads a hierarchy one level at a time.** `defaultExpanded="none"` opens no row until somebody opens it, so a level that has just been read arrives closed and asks for nothing on its own; `expanded` and `onExpandedChange` let the caller hold the open rows, to keep them across a visit or to read a level the moment it opens. `useTreeState` takes the same options (`TreeStateOptions`, `TreeDefaultExpanded`). `headless` keeps the column names for a screen reader and draws no header band, for an outline whose cells say what they are. A text field in a cell keeps the keys that move its caret (Left, Right, Home and End, and Up and Down in a field of several lines), so a name typed in place can be corrected; from a single line, Up and Down still walk the rows.
266
+
267
+ `@crewlethq/icons` adds `Heading2Glyph`, `Heading3Glyph`, `BoldGlyph`, `ItalicGlyph`, `ListOrderedGlyph` and `ListChecksGlyph`, which `MarkdownEditor` draws, and `PaletteGlyph` (Lucide's `palette`).
268
+
269
+ What to change: a test or a query that finds the theme row by the name "Theme", or the toggle by "Switch to the light theme" or "Switch to the dark theme", uses the new names. A `CommandPalette` row whose `onSelect` returns `false` by accident now keeps the surface open; return nothing to close it. A `TreeGrid` given none of the new props opens every row as it did. Everything else is new.
270
+
194
271
  ## Changes in 0.12.0
195
272
 
196
273
  0.12.0 adds the parts a question is answered in place with.
@@ -1,4 +1,5 @@
1
- export { AppShell, AppShellRail, AppShellRailRow, AppShellToggle, AppShellTopbar, useAppShell } from '../chunk-7LD6P32I.js';
1
+ export { AppShell, AppShellRail, AppShellRailRow, AppShellToggle, AppShellTopbar, useAppShell } from '../chunk-L3CTFFLZ.js';
2
+ import '../chunk-IG75YMUN.js';
2
3
  import '../chunk-4FQT6ZAY.js';
3
4
  import '../chunk-7RPGY3JE.js';
4
5
  import '../chunk-R25RGPUB.js';
@@ -1,3 +1,3 @@
1
- export { AvatarStack, avatarStackLabel } from '../chunk-3UK4VFSK.js';
1
+ export { AvatarStack, avatarStackLabel } from '../chunk-6NIZCQUM.js';
2
2
  export { AVATAR_CHARACTER_FULL_DETAIL, AVATAR_CORNER_RATIO, AVATAR_SIZES, AVATAR_STATUSES, AVATAR_STATUS_LABELS, Avatar, avatarCorner, avatarPixels, avatarTint, getInitials } from '../chunk-HI6Q2UXQ.js';
3
3
  import '../chunk-ODEQTPFM.js';
@@ -142,7 +142,7 @@
142
142
  }
143
143
 
144
144
  .crewlet-avatar--seeded {
145
- color: var(--color-text-on-accent);
145
+ color: var(--color-text-on-scrim);
146
146
  }
147
147
 
148
148
  .crewlet-avatar--tint-0 { background: var(--color-avatar-tint-0); }
@@ -1,9 +1,9 @@
1
- export { AvatarPicker } from '../chunk-HV6IZFBK.js';
2
- import '../chunk-K5Q2DK3P.js';
1
+ export { AvatarPicker } from '../chunk-4FFHJBEN.js';
2
+ import '../chunk-NXDEPUVG.js';
3
3
  import '../chunk-AYKXMIOR.js';
4
- import '../chunk-EBBAAQDY.js';
5
- import '../chunk-JHQUT4WJ.js';
6
- import '../chunk-PDUZO35I.js';
4
+ import '../chunk-26PEPONN.js';
5
+ import '../chunk-4LDTQWS7.js';
6
+ import '../chunk-O2ZDZZS7.js';
7
7
  import '../chunk-YXMCUKFI.js';
8
8
  import '../chunk-CBOICT64.js';
9
9
  import '../chunk-BHHIIK3M.js';
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The trail in a top bar: where the reader is, and the way back out.
3
+ *
4
+ * THE LAST CRUMB IS THE PAGE'S HEADING. The bar is the page's banner and sits
5
+ * outside `main`, and the name of the place the reader is at is what a reader
6
+ * navigating by heading lands on first, so the last crumb is the `h1` and every
7
+ * screen's own headings start at level 2 under it. It carries no link: a trail
8
+ * whose final crumb links to the page it is on teaches a reader that the
9
+ * control does nothing.
10
+ *
11
+ * A PAGE WHOSE SUBJECT CAN BE SWITCHED draws its control as that heading's
12
+ * words (`current`): a switcher's button names the project the page is about
13
+ * and opens the list of the others, so the reader changes the subject where
14
+ * the subject is named rather than in a second control beside it.
15
+ *
16
+ * THE TRAIL IS A SCROLLPORT, AND WHILE IT OVERFLOWS IT IS A TAB STOP. It
17
+ * scrolls sideways past its floor rather than clipping the ancestry as a box,
18
+ * and its scrollbar is not drawn, because on a bar this short it would sit on
19
+ * the baseline the trail is written on. A pointer can drag a hidden scrollport
20
+ * and a trackpad can swipe it, but a keyboard reaches a scroll container's
21
+ * arrow keys only once it can be focused, and a deep trail overflows on its
22
+ * ancestor spans, which are not links and take no focus of their own. A trail
23
+ * that fits has nowhere for the arrows to go, so the stop is there only while
24
+ * `scrollWidth` exceeds `clientWidth`.
25
+ *
26
+ * ITS FLOOR IS THE WAY BACK OUT (`--crewlet-breadcrumb-floor`): the ancestors
27
+ * at their width and the last crumb at its stub, measured. The bar around it
28
+ * breaks a line when that floor cannot sit beside its controls, so a trail
29
+ * that cannot show its ancestry takes a line of its own, and it scrolls only
30
+ * when its ancestry alone is wider than the whole bar.
31
+ *
32
+ * Ported from the engine dashboard's `app/header/PageHeader.tsx`.
33
+ */
34
+ import { type HTMLAttributes, type ReactNode } from 'react';
35
+ export interface BreadcrumbCrumb {
36
+ /** The crumb's words, and what a screen reader calls it. */
37
+ label: string;
38
+ /**
39
+ * Where the crumb leads. Left out on the last crumb, which is the page
40
+ * itself, and on an ancestor with no page of its own.
41
+ */
42
+ href?: string | undefined;
43
+ /** A glyph before the label: the workspace's mark on the first crumb. Decorative. */
44
+ icon?: ReactNode;
45
+ /**
46
+ * A project's key, drawn as a `KeyChip` before a NAMED label, the way every
47
+ * row and rail draws a project. Only where the label is a name: a crumb
48
+ * still showing the raw key would say it twice.
49
+ */
50
+ keyChip?: string | undefined;
51
+ /** A badge before the label: a seat's avatar on its own crumb. Decorative. */
52
+ badge?: ReactNode;
53
+ /** Draws the label in the mono face: an identifier still waiting for its name. */
54
+ mono?: boolean | undefined;
55
+ }
56
+ export interface BreadcrumbProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
57
+ crumbs: readonly BreadcrumbCrumb[];
58
+ /**
59
+ * A control drawn as the last crumb's words, inside the page's heading: a
60
+ * switcher's button where the page's subject is one of several the reader
61
+ * can move between. The control carries its own words; the last crumb is
62
+ * still what the trail is measured by.
63
+ */
64
+ current?: ReactNode;
65
+ /** Names the navigation landmark. */
66
+ label?: string | undefined;
67
+ }
68
+ export declare function Breadcrumb({ crumbs, current, label, className, style, ...rest }: BreadcrumbProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import './Breadcrumb.css';
2
+ export { Breadcrumb } from './Breadcrumb.js';
3
+ export type { BreadcrumbCrumb, BreadcrumbProps } from './Breadcrumb.js';
@@ -0,0 +1,3 @@
1
+ export { Breadcrumb } from '../chunk-VWNC2L6W.js';
2
+ import '../chunk-FYTIJM2T.js';
3
+ import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,193 @@
1
+ /*
2
+ * The trail in a top bar.
3
+ *
4
+ * IT IS WHAT GIVES WAY, AND WHAT TAKES THE SLACK. `flex-grow: 1` is the spacer
5
+ * a bar would otherwise need: the trail absorbs the free space, so the
6
+ * controls after it stay in the bar's far corner. Its BASIS is 0, which is
7
+ * what lets a wrapping bar break by what it holds rather than at a width, and
8
+ * `flex-shrink: 100` puts it ahead of anything else on the bar that can give.
9
+ *
10
+ * ITS FLOOR IS THE WAY BACK OUT, measured by the component
11
+ * (`--crewlet-breadcrumb-floor`): the ancestors at their width and the last
12
+ * crumb's stub, at least 20ch, and never more than the whole bar. A fixed 20ch
13
+ * alone let a seat's trail share a phone's line with its one button and cut
14
+ * "Engineering · Co" mid-letter under it; measured, a trail that cannot show
15
+ * its ancestry beside the controls makes the bar break a line instead.
16
+ *
17
+ * Past the whole bar the trail SCROLLS rather than clipping as a box: the
18
+ * ancestors never shrink, so once they alone are wider than the bar there is
19
+ * nothing left to ellipsise, and a clip lands wherever the pixels ran out.
20
+ */
21
+ .crewlet-breadcrumb {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 2px;
25
+ flex: 1 100 0;
26
+ min-width: min(100%, max(20ch, var(--crewlet-breadcrumb-floor, 0px)));
27
+ overflow-x: auto;
28
+ overflow-y: hidden;
29
+ overscroll-behavior-x: contain;
30
+ scrollbar-width: none;
31
+ font-size: var(--font-size-sm);
32
+ }
33
+
34
+ .crewlet-breadcrumb::-webkit-scrollbar {
35
+ display: none;
36
+ }
37
+
38
+ /* A trail that overflows is a tab stop (see Breadcrumb.tsx), and shows it. */
39
+ .crewlet-breadcrumb:focus-visible {
40
+ outline: 2px solid var(--color-focus);
41
+ outline-offset: var(--size-focus-ring-inset-offset);
42
+ }
43
+
44
+ .crewlet-breadcrumb__part {
45
+ display: inline-flex;
46
+ align-items: center;
47
+ gap: 2px;
48
+ min-width: 0;
49
+ }
50
+
51
+ /*
52
+ * THE OBJECT'S OWN NAME TAKES THE CUT, never the way back out. The reader
53
+ * knows which object they are on, because the page below says so; what they
54
+ * cannot reconstruct is the links out of it. So the ancestors keep their width
55
+ * and the last part is the one that ellipsises.
56
+ */
57
+ .crewlet-breadcrumb__part:not(:last-child) {
58
+ flex: 0 0 auto;
59
+ }
60
+
61
+ svg.crewlet-breadcrumb__separator {
62
+ flex: none;
63
+ margin: 0 var(--spacing-1);
64
+ color: var(--color-text-muted);
65
+ }
66
+
67
+ .crewlet-breadcrumb__crumb,
68
+ .crewlet-breadcrumb__here {
69
+ display: inline-flex;
70
+ align-items: center;
71
+ gap: calc(var(--spacing-1) * 1.5);
72
+ min-width: 0;
73
+ white-space: nowrap;
74
+ overflow: hidden;
75
+ text-overflow: ellipsis;
76
+ }
77
+
78
+ /*
79
+ * The ancestors in the quiet ink: a link lifts to the primary ink under the
80
+ * pointer, and one with nothing to press stays where it is. NO UNDERLINE: a
81
+ * crumb is a piece of chrome, not a word in a sentence, and the step between
82
+ * two measured text rungs is its hover in both themes.
83
+ */
84
+ .crewlet-breadcrumb__crumb {
85
+ border-radius: var(--radius-xs);
86
+ color: var(--color-text-tertiary);
87
+ text-decoration: none;
88
+ transition: color var(--motion-duration-fast) var(--motion-easing-default);
89
+ }
90
+
91
+ a.crewlet-breadcrumb__crumb:hover {
92
+ color: var(--color-text-primary);
93
+ text-decoration: none;
94
+ }
95
+
96
+ a.crewlet-breadcrumb__crumb:focus-visible {
97
+ outline: 2px solid var(--color-focus);
98
+ outline-offset: var(--size-focus-ring-inset-offset);
99
+ }
100
+
101
+ /* FORCED COLOURS paints a link LinkText hovered or not, so the ink step cannot
102
+ be seen there: the rule under the words is the hover in that mode. */
103
+ @media (forced-colors: active) {
104
+ a.crewlet-breadcrumb__crumb:hover {
105
+ text-decoration: underline;
106
+ }
107
+ }
108
+
109
+ /* A glyph sits a step further from its word than a chip does from its name. */
110
+ .crewlet-breadcrumb__icon {
111
+ display: inline-flex;
112
+ flex: none;
113
+ margin-right: calc(var(--spacing-1) / 2);
114
+ }
115
+
116
+ .crewlet-breadcrumb__badge,
117
+ .crewlet-breadcrumb__key {
118
+ flex: none;
119
+ }
120
+
121
+ /*
122
+ * THE LABEL IS THE BOX THAT ELLIPSISES. The crumb around it is a flex
123
+ * container, and `text-overflow` never reaches the anonymous item text inside
124
+ * one makes; a span is a real flex item, and its `min-width: 0` is what lets
125
+ * it shrink below its text at all.
126
+ */
127
+ .crewlet-breadcrumb__text {
128
+ min-width: 0;
129
+ overflow: hidden;
130
+ white-space: nowrap;
131
+ text-overflow: ellipsis;
132
+ }
133
+
134
+ /* THE LAST CRUMB IS THE PAGE'S `h1`, so the heading's own margins and size are
135
+ the bar's, not the document's. */
136
+ .crewlet-breadcrumb__here {
137
+ margin: 0;
138
+ color: var(--color-text-primary);
139
+ font-size: inherit;
140
+ font-weight: var(--font-weight-medium);
141
+ line-height: inherit;
142
+ }
143
+
144
+ /*
145
+ * AND IT NEVER COLLAPSES TO NOTHING: a floor of a few characters keeps an
146
+ * ellipsised stub, which is what tells a reader the trail has a last part at
147
+ * all. The floor is the last part's alone; an ancestor never shrinks.
148
+ */
149
+ .crewlet-breadcrumb__part:last-child > .crewlet-breadcrumb__here {
150
+ min-width: 5ch;
151
+ }
152
+
153
+ /*
154
+ * A CONTROL AS THE HEADING'S WORDS draws its own edges: its hover fill and its
155
+ * focus ring run to its own box, so the heading around it clips nothing.
156
+ */
157
+ .crewlet-breadcrumb__here--control {
158
+ overflow: visible;
159
+ }
160
+
161
+ /* An identifier still waiting for its name, in the mono face a step down. */
162
+ .crewlet-breadcrumb__crumb.is-mono,
163
+ .crewlet-breadcrumb__here.is-mono {
164
+ font-family: var(--font-family-mono);
165
+ font-size: var(--font-size-compact);
166
+ }
167
+
168
+ /*
169
+ * ON A PHONE AN ANCESTOR WITH A GLYPH IS ITS GLYPH. The workspace's mark is
170
+ * the one sign of where the reader is that survives a sidebar folded into a
171
+ * drawer, and the line it shares with the drawer toggle and the page's primary
172
+ * action has no room for its word as well. The word stays, out of sight, so
173
+ * the crumb is still named.
174
+ */
175
+ @media (width < 640px) {
176
+ .crewlet-breadcrumb__part:not(:last-child) > .crewlet-breadcrumb__crumb:has(> .crewlet-breadcrumb__icon) > .crewlet-breadcrumb__text {
177
+ position: absolute;
178
+ width: 1px;
179
+ height: 1px;
180
+ margin: -1px;
181
+ padding: 0;
182
+ border: 0;
183
+ overflow: hidden;
184
+ clip-path: inset(50%);
185
+ white-space: nowrap;
186
+ }
187
+ }
188
+
189
+ @media (prefers-reduced-motion: reduce) {
190
+ .crewlet-breadcrumb__crumb {
191
+ transition: none;
192
+ }
193
+ }
@@ -0,0 +1,43 @@
1
+ import { type HTMLAttributes, type ReactNode } from 'react';
2
+ export interface ChoiceButtonOption {
3
+ value: string;
4
+ label: ReactNode;
5
+ /** A line under the label: what choosing it means. */
6
+ detail?: ReactNode;
7
+ }
8
+ export interface ChoiceButtonsProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
9
+ /** Names the group: "Answers". */
10
+ label: string;
11
+ options: readonly ChoiceButtonOption[];
12
+ /** The option the asker recommends: drawn first, filled, and marked. */
13
+ recommended?: string | null | undefined;
14
+ /** The mark on the recommended option, read as part of its name. */
15
+ recommendedLabel?: string | undefined;
16
+ /** Sends one option. */
17
+ onChoose: (value: string) => void;
18
+ /**
19
+ * The option being sent. It is announced as busy, and every option holds
20
+ * still until the answer lands, so a second press cannot send a second
21
+ * answer. What landed is the caller's to say, where the options were.
22
+ */
23
+ pending?: string | null | undefined;
24
+ /**
25
+ * Why nothing can be chosen now. Every option keeps its focus and reads the
26
+ * reason after its name, as a disabled `Button` does.
27
+ */
28
+ disabledReason?: string | undefined;
29
+ }
30
+ /**
31
+ * One answer from a short list, sent with ONE PRESS: each option is a button
32
+ * as tall as a touch target, the recommended one first and filled.
33
+ *
34
+ * WHEN IT IS RIGHT. Where the question is the whole screen and the options are
35
+ * the answer, a reader should not have to select and then submit. Where an
36
+ * answer is chosen among other edits, or needs a field of its own under it,
37
+ * `ChoiceList` is the part: a radio group with a footer that sends.
38
+ *
39
+ * NOT THE NATIVE `disabled`, for the reason `Button` gives: it takes focus off
40
+ * the control the reader just pressed and drops it on the page body.
41
+ * `aria-disabled` and `aria-busy` say the same and leave focus where it was.
42
+ */
43
+ export declare function ChoiceButtons({ label, options, recommended, recommendedLabel, onChoose, pending, disabledReason, className, ...rest }: ChoiceButtonsProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import './ChoiceButtons.css';
2
+ export { ChoiceButtons } from './ChoiceButtons.js';
3
+ export type { ChoiceButtonOption, ChoiceButtonsProps } from './ChoiceButtons.js';
@@ -0,0 +1,3 @@
1
+ export { ChoiceButtons } from '../chunk-YSJC7AZC.js';
2
+ import '../chunk-BZFWDIV6.js';
3
+ import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,127 @@
1
+ /*
2
+ * One answer, sent with one press: a column of buttons as tall as a touch
3
+ * target, the recommended one first and filled with the accent.
4
+ *
5
+ * The plain option is the secondary button's recipe (the card's own ground
6
+ * inside the strong hairline), and the recommended one the primary's, so the
7
+ * pair reads as the screen's one primary action and its alternatives.
8
+ */
9
+
10
+ .crewlet-choice-buttons {
11
+ display: grid;
12
+ grid-template-columns: minmax(0, 1fr);
13
+ gap: var(--spacing-2);
14
+ }
15
+
16
+ .crewlet-choice-buttons__option {
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: space-between;
20
+ gap: var(--spacing-3);
21
+ min-width: 0;
22
+ min-height: var(--size-control-lg);
23
+ padding: calc(var(--spacing-2) + var(--spacing-1) / 2) calc(var(--spacing-3) + var(--spacing-1) / 2);
24
+ border: 1px solid var(--color-border-strong);
25
+ border-radius: var(--radius-md);
26
+ background: var(--color-surface-subtle);
27
+ color: var(--color-text-primary);
28
+ font: inherit;
29
+ font-size: var(--font-size-sm);
30
+ font-weight: var(--font-weight-medium);
31
+ line-height: var(--font-line-height-snug);
32
+ text-align: left;
33
+ cursor: pointer;
34
+ transition:
35
+ background var(--motion-duration-fast) var(--motion-easing-default),
36
+ border-color var(--motion-duration-fast) var(--motion-easing-default);
37
+ }
38
+
39
+ .crewlet-choice-buttons__option:hover:not([aria-disabled='true']) {
40
+ background: var(--color-surface-elevated);
41
+ border-color: var(--color-border-strong-hover);
42
+ }
43
+
44
+ .crewlet-choice-buttons__option:active:not([aria-disabled='true']) {
45
+ background: var(--color-surface-pressed);
46
+ }
47
+
48
+ /*
49
+ * The plain option draws the strong hairline, so its ring is drawn over that
50
+ * edge rather than a gap away from it, which would read as a second outline.
51
+ */
52
+ .crewlet-choice-buttons__option:focus-visible {
53
+ outline: 2px solid var(--color-focus);
54
+ outline-offset: var(--size-focus-ring-inset-offset);
55
+ }
56
+
57
+ .crewlet-choice-buttons__option[aria-disabled='true'] {
58
+ cursor: not-allowed;
59
+ }
60
+
61
+ /* The option on its way: still this option, mid-action. */
62
+ .crewlet-choice-buttons__option[aria-busy='true'] {
63
+ cursor: progress;
64
+ }
65
+
66
+ .crewlet-choice-buttons__option--recommended {
67
+ background: var(--color-brand-accent);
68
+ border-color: transparent;
69
+ color: var(--color-text-on-accent);
70
+ }
71
+
72
+ .crewlet-choice-buttons__option--recommended:hover:not([aria-disabled='true']) {
73
+ background: var(--color-brand-accent-hover);
74
+ border-color: transparent;
75
+ }
76
+
77
+ .crewlet-choice-buttons__option--recommended:active:not([aria-disabled='true']) {
78
+ background: var(--color-brand-accent-active);
79
+ }
80
+
81
+ /*
82
+ * The accent fill draws no edge, and an inset ring on it would be drawn in
83
+ * nearly the fill's own colour, so its ring stands off it as a primary
84
+ * button's does.
85
+ */
86
+ .crewlet-choice-buttons__option--recommended:focus-visible {
87
+ outline-offset: 2px;
88
+ }
89
+
90
+ .crewlet-choice-buttons__text {
91
+ display: grid;
92
+ gap: 2px;
93
+ min-width: 0;
94
+ overflow-wrap: anywhere;
95
+ }
96
+
97
+ .crewlet-choice-buttons__detail {
98
+ font-size: var(--font-size-xs);
99
+ font-weight: var(--font-weight-regular);
100
+ color: var(--color-text-tertiary);
101
+ }
102
+
103
+ /*
104
+ * On the accent fill the detail takes the label's own ink. The tertiary step
105
+ * is measured on the surfaces, not on the accent, where it does not clear.
106
+ */
107
+ .crewlet-choice-buttons__option--recommended .crewlet-choice-buttons__detail {
108
+ color: var(--color-text-on-accent);
109
+ }
110
+
111
+ /*
112
+ * The mark, in the micro-label register. Full ink rather than a faded one:
113
+ * eleven pixels of white on the accent clears 4.5:1 only at full strength.
114
+ */
115
+ .crewlet-choice-buttons__note {
116
+ flex: none;
117
+ font-size: var(--font-size-2xs);
118
+ font-weight: var(--font-weight-medium);
119
+ letter-spacing: var(--font-letter-spacing-wide);
120
+ text-transform: uppercase;
121
+ }
122
+
123
+ @media (prefers-reduced-motion: reduce) {
124
+ .crewlet-choice-buttons__option {
125
+ transition: none;
126
+ }
127
+ }
@@ -1,5 +1,5 @@
1
- export { Combobox } from '../chunk-GXYNEDNA.js';
2
- import '../chunk-6MU7HNB4.js';
1
+ export { Combobox } from '../chunk-4Y7WKXAW.js';
2
+ import '../chunk-F73MC7EZ.js';
3
3
  import '../chunk-FTEJPHBI.js';
4
4
  import '../chunk-PQADV6P6.js';
5
5
  import '../chunk-4FQT6ZAY.js';