@lumiose/ui 0.1.1 → 0.1.3

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 (70) hide show
  1. package/README.md +2 -2
  2. package/dist/accordion/accordion.tsx +6 -4
  3. package/dist/autocomplete/autocomplete.tsx +6 -5
  4. package/dist/avatar/avatar.tsx +4 -3
  5. package/dist/batched.ts +11 -0
  6. package/dist/checkbox/checkbox.tsx +4 -3
  7. package/dist/checkbox-group/checkbox-group.tsx +3 -2
  8. package/dist/collapsible/collapsible.tsx +5 -3
  9. package/dist/collapsible/panel.ts +3 -1
  10. package/dist/combobox/combobox.tsx +8 -6
  11. package/dist/combobox/context.ts +2 -1
  12. package/dist/context-menu/context-menu.tsx +6 -4
  13. package/dist/dialog/dialog.tsx +4 -2
  14. package/dist/drawer/drawer.tsx +2 -1
  15. package/dist/field/field.tsx +13 -11
  16. package/dist/form/reset.ts +4 -2
  17. package/dist/input/input.tsx +4 -1
  18. package/dist/menu/menu.tsx +13 -11
  19. package/dist/meter/meter.tsx +2 -1
  20. package/dist/navigation-menu/navigation-menu.tsx +6 -4
  21. package/dist/not-inside.ts +13 -0
  22. package/dist/number-field/number-field.tsx +6 -4
  23. package/dist/otp-field/otp-field.tsx +11 -9
  24. package/dist/popover/hover.ts +2 -1
  25. package/dist/popover/popover-hover.script.ts +27 -0
  26. package/dist/popover/popover.script.ts +19 -25
  27. package/dist/popover/popover.tsx +18 -9
  28. package/dist/preview-card/preview-card.tsx +4 -2
  29. package/dist/progress/progress.tsx +2 -1
  30. package/dist/radio-group/radio-group.tsx +5 -3
  31. package/dist/select/select.script.ts +26 -0
  32. package/dist/select/select.tsx +43 -11
  33. package/dist/slider/slider.tsx +6 -4
  34. package/dist/switch/switch.tsx +4 -3
  35. package/dist/tabs/tabs.tsx +5 -3
  36. package/dist/toast/toast.tsx +5 -3
  37. package/dist/toggle/toggle.tsx +6 -5
  38. package/dist/toggle-group/toggle-group.script.ts +12 -4
  39. package/dist/toggle-group/toggle-group.tsx +16 -4
  40. package/dist/toolbar/toolbar.tsx +2 -1
  41. package/dist/tooltip/tooltip.tsx +6 -2
  42. package/docs/README.md +16 -7
  43. package/docs/accordion.md +3 -2
  44. package/docs/autocomplete.md +2 -2
  45. package/docs/avatar.md +2 -2
  46. package/docs/checkbox-group.md +1 -1
  47. package/docs/checkbox.md +1 -1
  48. package/docs/collapsible.md +4 -3
  49. package/docs/combobox.md +4 -3
  50. package/docs/context-menu.md +3 -3
  51. package/docs/dialog.md +1 -1
  52. package/docs/drawer.md +3 -2
  53. package/docs/field.md +2 -2
  54. package/docs/installation.md +3 -3
  55. package/docs/llms-full.txt +114 -73
  56. package/docs/llms.txt +14 -14
  57. package/docs/menu.md +4 -3
  58. package/docs/menubar.md +3 -3
  59. package/docs/navigation-menu.md +4 -3
  60. package/docs/otp-field.md +2 -2
  61. package/docs/popover.md +7 -4
  62. package/docs/preview-card.md +4 -3
  63. package/docs/radio-group.md +1 -1
  64. package/docs/select.md +25 -10
  65. package/docs/tabs.md +1 -1
  66. package/docs/toast.md +2 -2
  67. package/docs/toggle-group.md +10 -4
  68. package/docs/toolbar.md +2 -2
  69. package/docs/tooltip.md +4 -3
  70. package/package.json +5 -5
@@ -13,9 +13,8 @@ props, events and styling hooks. The Lumiose docs: `node_modules/lumiose/docs/ll
13
13
 
14
14
  > `@lumiose/ui`: accessible, unstyled UI primitives built on native elements (`<dialog>`, popover, invoker commands, CSS anchor positioning), server-rendered without hydration. Style them with your own CSS or the optional default theme.
15
15
 
16
- **Status:** in development. `@lumiose/ui` keeps its own 0.x versions, apart from the version all
17
- other Lumiose packages share, and is not on npm yet: today it is used from this repository's
18
- workspace. The goal for its 1.0 is parity with [Base UI](https://base-ui.com)'s components; the
16
+ **Status:** in development. `@lumiose/ui` is on npm and keeps its own 0.x versions, apart from
17
+ the version all other Lumiose packages share. The goal for its 1.0 is parity with [Base UI](https://base-ui.com)'s components; the
19
18
  table below lists them as they land.
20
19
 
21
20
  These pages ship in the package, in `node_modules/@lumiose/ui/docs`, so they match the installed
@@ -43,12 +42,19 @@ Anatomy, API, Example, Styling, Keyboard and focus, Without JavaScript, Browser
43
42
  runtime.
44
43
  - **Parts.** Each component is a set of parts named as in Base UI (`Dialog.Root`,
45
44
  `Dialog.Trigger`, `Dialog.Popup`, …). Every part that renders an element takes that element's
46
- props (`class`, `style`, `aria-*`, events) and passes them through.
45
+ props (`class`, `style`, `aria-*`, events) and passes them through. A part finds its `Root`
46
+ through context, so a `Root` and its parts render on the same side of an island boundary, even
47
+ where the JSX nests them. A part the page passes to an island as a slot (children or a JSX prop)
48
+ renders in the page, outside the `Root` the island renders, and the server throws "Combobox
49
+ parts must be inside <Combobox.Root>"; an island inside a `Root` the page renders does not see
50
+ that `Root` either. Pass the data as props and render the `Root` and its parts inside the
51
+ island ([Islands](https://itsjavi.com/lumiose/docs/concepts/islands/)).
47
52
  - **Signals.** A component's state is a prop and a callback: `open` with `onOpenChange`, `checked`
48
53
  with `onCheckedChange`, `value` with `onValueChange`. Pass a signal's value and write it back in
49
54
  the callback to keep both in sync, or leave both out and the element keeps its own state. Custom
50
55
  elements report changes with lowercase DOM events, like native ones: `openchange`,
51
- `checkedchange` and `change`.
56
+ `checkedchange` and `change`. The callbacks run inside `batch()`, as event handlers do: what
57
+ they write renders together, and the DOM is current as soon as the event returns.
52
58
  - **Keyboard contracts.** Every component follows its WAI-ARIA Authoring Practices pattern, and
53
59
  its end-to-end tests check the keyboard paths.
54
60
  - **WCAG 2.2 AA.** Every component meets it: the components own semantics, keyboard and
@@ -81,7 +87,7 @@ custom properties, named as in shadcn/ui, are listed in [Theming](theming.md#the
81
87
  | [Tooltip](tooltip.md) | Available: native hint popover, opened by its script |
82
88
  | [Menu](menu.md) | Available: popover menu, opens without JavaScript |
83
89
  | [Combobox](combobox.md) | Available: `<lu-combobox>`, a form control that filters its options |
84
- | [Select](select.md) | Available: native `<select>`, customizable where supported, no script |
90
+ | [Select](select.md) | Available: native `<select>`, customizable where supported, no script but for `label`s |
85
91
  | [Tabs](tabs.md) | Available: server HTML with the full state, `<lu-tabs>` adds the keys |
86
92
  | [Field](field.md) | Available: label, description, errors; native validation in the field |
87
93
  | [Fieldset](fieldset.md) | Available: native `<fieldset>` and `<legend>`, no script |
@@ -176,7 +182,10 @@ errors are yours.
176
182
  `@lumiose/ui` is opt-in, so its size is reported apart from the runtime's caps:
177
183
  `pnpm --filter @lumiose/ui size` prints, per component, the JavaScript a server-rendered page loads
178
184
  for its custom elements, the JavaScript an island that uses it loads, and its CSS (minified, gzip
179
- and brotli).
185
+ and brotli). The scripts load as separate modules, in islands too: each component's script, and
186
+ the modules several scripts share (the dismissal model, the placement fallback, the hover timing),
187
+ each a file of its own, as an app's build splits them. The sizes on the component pages are those
188
+ files' brotli sizes added up.
180
189
 
181
190
  ## Related
182
191
 
@@ -190,13 +199,13 @@ and brotli).
190
199
 
191
200
  > Add `@lumiose/ui` to a Lumiose app and import its stylesheets, and the default theme if you want its look. The components then render in server HTML, without an island, and a page loads only the small scripts of the components it renders.
192
201
 
193
- **Status:** `@lumiose/ui` is not on npm yet. In this repository's workspace, an app adds it as
194
- `"@lumiose/ui": "workspace:*"` in its `dependencies`.
202
+ **Status:** in development, on npm with its own 0.x versions, apart from the version the other
203
+ Lumiose packages share.
195
204
 
196
205
  ## Add the package
197
206
 
198
207
  `@lumiose/ui` needs a Lumiose app: it ships its components as TSX, which the app's build compiles
199
- with the app's own code, and `lumiose` is its peer dependency. Once it is on npm:
208
+ with the app's own code, and `lumiose` is its peer dependency. Install it from npm:
200
209
 
201
210
  ```sh
202
211
  pnpm add @lumiose/ui
@@ -578,8 +587,9 @@ closed item are HTML features: an accordion in a server-rendered page works befo
578
587
  loads, and with none. Disabled triggers ignore the pointer through CSS. `value`, `onValueChange`
579
588
  and `bind:value` need the component to run in an island.
580
589
 
581
- A page that renders an `Accordion` loads a 1 kB (brotli) module once, without the Lumiose runtime;
582
- islands bundle it. It defines `<lu-accordion>`, which adds the arrow keys, Home and End, and sets
590
+ A page that renders an `Accordion` loads its script once, with the panel and animation modules it
591
+ shares with Collapsible: 1.3 kB (brotli) without the Lumiose runtime; an island loads the same
592
+ modules. It defines `<lu-accordion>`, which adds the arrow keys, Home and End, and sets
583
593
  the panels' animation hooks (shared with [Collapsible](collapsible.md)); a disabled trigger does
584
594
  not toggle, also from the keyboard.
585
595
 
@@ -602,7 +612,7 @@ not toggle, also from the keyboard.
602
612
 
603
613
  # Autocomplete
604
614
 
605
- > A text input with suggestions, for text that may be anything (a search, a tag, an address): the WAI-ARIA combobox pattern with list autocomplete, after Base UI's Autocomplete. Unlike a [Combobox](combobox.md), the text is the value: the input has the name and submits what is typed, and choosing a suggestion only fills it in. It runs on the Combobox's element (`<lu-combobox free>`, 5.0 kB) and parts, on server-rendered pages and in islands; `bind:value` keeps a signal in sync. Filtering, inline completion, groups, an empty state and a status.
615
+ > A text input with suggestions, for text that may be anything (a search, a tag, an address): the WAI-ARIA combobox pattern with list autocomplete, after Base UI's Autocomplete. Unlike a [Combobox](combobox.md), the text is the value: the input has the name and submits what is typed, and choosing a suggestion only fills it in. It runs on the Combobox's element (`<lu-combobox free>`, 5.8 kB) and parts, on server-rendered pages and in islands; `bind:value` keeps a signal in sync. Filtering, inline completion, groups, an empty state and a status.
606
616
 
607
617
  ```ts
608
618
  import { Autocomplete } from "@lumiose/ui"; // or "@lumiose/ui/autocomplete"
@@ -747,7 +757,7 @@ Focus stays in the input: the highlight moves through `aria-activedescendant`.
747
757
  ## Without JavaScript, and the element's script
748
758
 
749
759
  The input is a native text field with the name: typing, `required`, submission and reset work with
750
- no script; the suggestions need it. `<lu-combobox>`'s script (5.0 kB brotli, shared with Combobox)
760
+ no script; the suggestions need it. `<lu-combobox>`'s script (5.8 kB brotli, shared with Combobox)
751
761
  filters and opens them, moves the highlight, fills in a choice (firing `input` and `change` on the
752
762
  input) and runs inline completion.
753
763
 
@@ -946,8 +956,8 @@ when it fails) and hides its fallback, from sight and from assistive technology,
946
956
  the avatar twice; an avatar without an image shows its fallback.
947
957
  `onLoadingStatusChange` needs the component to run in an island.
948
958
 
949
- A page that renders an `Avatar` loads a 0.5 kB (brotli) script, without the Lumiose runtime;
950
- islands bundle it. `<lu-avatar>` reads the image's state when it connects (a cached image shows
959
+ A page that renders an `Avatar` loads a 0.5 kB (brotli) script, without the Lumiose runtime; an
960
+ island loads the same module. `<lu-avatar>` reads the image's state when it connects (a cached image shows
951
961
  at once), then follows its `load` and `error` events and changes to its `src` and `srcset`. An
952
962
  image that is complete without a size (an SVG without intrinsic dimensions, or a broken image) is
953
963
  told apart with `decode()`.
@@ -1180,7 +1190,7 @@ A native checkbox: toggling, submission, `required`, reset and `disabled` (its o
1180
1190
  work with no script.
1181
1191
 
1182
1192
  A page that renders a Checkbox, Checkbox Group, Radio Group or Switch loads one 1.0 kB (brotli)
1183
- script once, without the Lumiose runtime; islands bundle it. It turns server HTML's
1193
+ script once, without the Lumiose runtime; an island loads the same module. It turns server HTML's
1184
1194
  `data-indeterminate` into the `indeterminate` property (which has no attribute), makes read-only
1185
1195
  inputs ignore clicks, Space and the label, runs the parent checkbox of a group and a [Radio
1186
1196
  Group](radio-group.md)'s arrow keys, and adds an
@@ -1207,7 +1217,7 @@ browser.
1207
1217
  > A group of checkboxes with one value, the list of the checked ones, after Base UI's CheckboxGroup: a native `<fieldset>` whose checkboxes share a name, so the form submits each checked value and a disabled group disables them all, without JavaScript. A parent checkbox checks them all and shows checked, mixed or unchecked as they are; `bind:value` keeps a signal in sync.
1208
1218
 
1209
1219
  ```ts
1210
- import { CheckboxGroup, Checkbox, Fieldset } from "@lumiose/ui"; // or "@lumiose/ui/checkbox-group"
1220
+ import { CheckboxGroup, Checkbox, Fieldset } from "@lumiose/ui"; // or "@lumiose/ui/checkbox-group", "@lumiose/ui/checkbox" and "@lumiose/ui/fieldset"
1211
1221
  import "@lumiose/ui/themes/default.css"; // optional: the default theme
1212
1222
  ```
1213
1223
 
@@ -1319,7 +1329,7 @@ Every supported browser.
1319
1329
 
1320
1330
  # Collapsible
1321
1331
 
1322
- > A panel that its trigger shows and hides: a native `<details>` whose `<summary>` is the trigger, so it works in server HTML with no JavaScript, keeps the browser's keyboard handling, and opens when find-in-page or a link lands inside it. A 0.7 kB script adds Base UI's animation hooks. `bind:open` (or `open` and `onOpenChange`) keeps a signal in sync in islands.
1332
+ > A panel that its trigger shows and hides: a native `<details>` whose `<summary>` is the trigger, so it works in server HTML with no JavaScript, keeps the browser's keyboard handling, and opens when find-in-page or a link lands inside it. A 0.9 kB script adds Base UI's animation hooks. `bind:open` (or `open` and `onOpenChange`) keeps a signal in sync in islands.
1323
1333
 
1324
1334
  ```ts
1325
1335
  import { Collapsible } from "@lumiose/ui"; // or "@lumiose/ui/collapsible"
@@ -1460,8 +1470,9 @@ Collapsible in a server-rendered page works before any script loads, and with no
1460
1470
  trigger ignores the pointer through CSS. `open`, `onOpenChange` and `bind:open` need the component
1461
1471
  to run in an island.
1462
1472
 
1463
- A page that renders a `Collapsible` loads a 0.7 kB (brotli) module once, without the Lumiose
1464
- runtime; islands bundle it. It covers every Collapsible and [Accordion](accordion.md) panel on the
1473
+ A page that renders a `Collapsible` loads 0.9 kB (brotli) of modules once (its script, and the
1474
+ panel and animation modules it shares with Accordion), without the Lumiose runtime; an island
1475
+ loads the same modules. It covers every Collapsible and [Accordion](accordion.md) panel on the
1465
1476
  page:
1466
1477
 
1467
1478
  - the animation hooks above, set on every opening and closing, whatever caused it (the trigger,
@@ -1487,7 +1498,7 @@ page:
1487
1498
 
1488
1499
  # Combobox
1489
1500
 
1490
- > A text input that filters a list of options, for picking one value or several from many: the WAI-ARIA combobox pattern with list autocomplete, after Base UI's Combobox. The input shows the option's label and the form gets its value. `<lu-combobox>`, a 5.4 kB form-associated light-DOM element, runs it on server-rendered pages and in islands; `bind:value` and `bind:open` keep signals in sync. Groups, an empty state, a status, a clear button, multiple values with chips, validation and async options.
1501
+ > A text input that filters a list of options, for picking one value or several from many: the WAI-ARIA combobox pattern with list autocomplete, after Base UI's Combobox. The input shows the option's label and the form gets its value. `<lu-combobox>`, a 5.8 kB form-associated light-DOM element, runs it on server-rendered pages and in islands; `bind:value` and `bind:open` keep signals in sync. Groups, an empty state, a status, a clear button, multiple values with chips, validation and async options.
1491
1502
 
1492
1503
  ```ts
1493
1504
  import { Combobox } from "@lumiose/ui"; // or "@lumiose/ui/combobox"
@@ -1800,8 +1811,9 @@ In the server HTML the input is empty even with a selected value: the input rend
1800
1811
  options, so the server does not know the selected option's label when it writes the input. The
1801
1812
  script puts the label in as it upgrades the element (decision-62).
1802
1813
 
1803
- A page that renders a `Combobox` loads the script once, as a 5.4 kB (brotli) module without the
1804
- Lumiose runtime, and islands bundle it. It defines `lu-combobox`, which upgrades every combobox on
1814
+ A page that renders a `Combobox` loads the script once, with the dismissal and placement modules
1815
+ it shares with Menu and Popover: 5.8 kB (brotli) without the Lumiose runtime, and an island loads
1816
+ the same modules. It defines `lu-combobox`, which upgrades every combobox on
1805
1817
  the page in place: it reads the selection the server rendered (`aria-selected="true"`), puts its
1806
1818
  label in the input (text typed before it ran is kept, as a search), renders the chips, and from
1807
1819
  then on owns the value, the filtering, the popup,
@@ -1859,7 +1871,7 @@ The element is also an API of its own, for scripts and other frameworks:
1859
1871
 
1860
1872
  # Context Menu
1861
1873
 
1862
- > A menu opened at the pointer by a right-click in its trigger area, a long press on a touch screen, or Shift+F10 and the ContextMenu key: Menu's popup and items, with Menu's keys, typeahead, submenus and dismissal. A 0.6 kB script on top of Menu's (4.6 kB together) opens it; without JavaScript the browser shows its own menu. `bind:open` (or `open` and `onOpenChange`) keeps a signal in sync in islands.
1874
+ > A menu opened at the pointer by a right-click in its trigger area, a long press on a touch screen, or Shift+F10 and the ContextMenu key: Menu's popup and items, with Menu's keys, typeahead, submenus and dismissal. A 0.9 kB script on top of Menu's (5.4 kB together) opens it; without JavaScript the browser shows its own menu. `bind:open` (or `open` and `onOpenChange`) keeps a signal in sync in islands.
1863
1875
 
1864
1876
  ```ts
1865
1877
  import { ContextMenu } from "@lumiose/ui"; // or "@lumiose/ui/context-menu"
@@ -2034,8 +2046,8 @@ Opening a menu at the pointer needs the script: without JavaScript a right-click
2034
2046
  browser's own menu, and the popup stays hidden. `open`, `onOpenChange` and `bind:open` need the
2035
2047
  component to run in an island.
2036
2048
 
2037
- A page that renders a `ContextMenu` loads Menu's script and a 0.6 kB (brotli) one of its own,
2038
- 4.6 kB together, without the Lumiose runtime; islands bundle them. The element opens the menu on a
2049
+ A page that renders a `ContextMenu` loads Menu's script and a 0.9 kB (brotli) one of its own,
2050
+ 5.4 kB together, without the Lumiose runtime; an island loads the same modules. The element opens the menu on a
2039
2051
  right-click, a long press or the keys, at the pointer, through a zero-size anchor that it moves
2040
2052
  there (CSS anchor positioning places the popup, with Menu's flips; without it, the shared
2041
2053
  placement script does). Menu's script does the rest: keys, items, submenus, and dismissal shared
@@ -2280,7 +2292,7 @@ server-rendered page works before any script loads, and with none. `open`, `onOp
2280
2292
 
2281
2293
  Where the native element falls short of Base UI's dialog, a small script closes the gap: a page
2282
2294
  that renders a `Dialog` loads it once, as a 1.5 kB (brotli) module without the Lumiose runtime, and
2283
- islands bundle it. It covers every `<dialog>` on the page:
2295
+ an island loads the same module. It covers every `<dialog>` on the page:
2284
2296
 
2285
2297
  - a right-click or a second finger outside does not close the dialog;
2286
2298
  - `data-nested-dialog-open` and `--lu-nested-dialogs` on a dialog with open dialogs inside;
@@ -2500,8 +2512,9 @@ The trigger, the close buttons, Escape and the click outside are HTML features,
2500
2512
  drawer in a server-rendered page works before any script loads, and with none, against its edge.
2501
2513
  `open`, `onOpenChange` and `bind:open` need the component to run in an island.
2502
2514
 
2503
- A page that renders a `Drawer` loads Dialog's script and a 1 kB (brotli) one of its own (2.5 kB
2504
- together), without the Lumiose runtime; islands bundle them. The drawer's script handles the
2515
+ A page that renders a `Drawer` loads Dialog's script and a 1.1 kB (brotli) one of its own (2.8 kB
2516
+ together, with the animation module it shares with Toast), without the Lumiose runtime; an island
2517
+ loads the same modules. The drawer's script handles the
2505
2518
  swipe, with the mouse and pen through pointer events and on touch screens through touch events,
2506
2519
  taking the gesture from scrolling only once it is a swipe.
2507
2520
 
@@ -2778,8 +2791,8 @@ the message Error's id (`Field.Error` without children or `match`) even when the
2778
2791
  Errors with children or a `match`. Give a field that must read well without JavaScript a message
2779
2792
  Error, and its Descriptions before the control.
2780
2793
 
2781
- A page that renders a `Field` loads a 1.7 kB (brotli) script once, without the Lumiose runtime;
2782
- islands bundle it. On a submit attempt (a submit button, Enter in a field, `requestSubmit()` or
2794
+ A page that renders a `Field` loads a 1.7 kB (brotli) script once, without the Lumiose runtime; an
2795
+ island loads the same module. On a submit attempt (a submit button, Enter in a field, `requestSubmit()` or
2783
2796
  the form's `reportValidity()`; a silent `checkValidity()` changes nothing) it cancels the browser's
2784
2797
  `invalid` bubble for fields that have an Error part for the problem and shows it there instead (the
2785
2798
  browser's message, or one your `validate` set), with the timing above, and focuses the first
@@ -3142,7 +3155,7 @@ Every browser; input types and constraints as the browser supports them.
3142
3155
 
3143
3156
  # Menu
3144
3157
 
3145
- > A menu of commands, links, checkbox and radio items and submenus, opened by a button: the trigger's `popovertarget` opens it and CSS anchor positioning places it, so it works in server HTML with no JavaScript, and a 4.0 kB script adds the APG Menu Button keyboard contract where JavaScript runs. `bind:open`, `bind:checked` and `bind:value` keep signals in sync in islands.
3158
+ > A menu of commands, links, checkbox and radio items and submenus, opened by a button: the trigger's `popovertarget` opens it and CSS anchor positioning places it, so it works in server HTML with no JavaScript, and a 4.4 kB script adds the APG Menu Button keyboard contract where JavaScript runs. `bind:open`, `bind:checked` and `bind:value` keep signals in sync in islands.
3146
3159
 
3147
3160
  ```ts
3148
3161
  import { Menu } from "@lumiose/ui"; // or "@lumiose/ui/menu"
@@ -3463,8 +3476,9 @@ submenu closes it, but the browser leaves focus on the page rather than on the s
3463
3476
  Tab starts over from the top (the script returns focus to the trigger). `open`, `onOpenChange` and
3464
3477
  the `bind:` props need the component to run in the browser, in an island.
3465
3478
 
3466
- The keyboard contract comes from a small script: a page that renders a `Menu` loads it once, as a
3467
- 4.0 kB (brotli) module without the Lumiose runtime, and islands bundle it. It defines `lu-menu`,
3479
+ The keyboard contract comes from a small script: a page that renders a `Menu` loads it once, with
3480
+ the dismissal and placement modules it shares with Popover and Combobox: 4.4 kB (brotli) without
3481
+ the Lumiose runtime, and an island loads the same modules. It defines `lu-menu`,
3468
3482
  which upgrades every menu on the page in place, and adds what the native popover lacks: focus on
3469
3483
  open and its return on close, the arrows, Home, End and typeahead, Escape one level at a time, Tab,
3470
3484
  submenus, checkbox and radio items, disabled items, `data-highlighted`, and the pointer behaviour
@@ -3510,7 +3524,7 @@ The element is also an API of its own, for scripts and other frameworks:
3510
3524
 
3511
3525
  # Menubar
3512
3526
 
3513
- > A bar of menus, such as an app's File, Edit and View, after Base UI's Menubar and the WAI-ARIA menubar pattern: `Menu.Root`s inside `<lu-menubar role="menubar">`, whose triggers are the bar's items. Menu's popups, items, submenus and dismissal; the bar is one tab stop, the arrow keys move between its items and between open menus, and once a menu is open, hovering another item opens its menu instead. A 0.7 kB script on top of Menu's (4.7 kB together); without JavaScript each trigger opens its menu.
3527
+ > A bar of menus, such as an app's File, Edit and View, after Base UI's Menubar and the WAI-ARIA menubar pattern: `Menu.Root`s inside `<lu-menubar role="menubar">`, whose triggers are the bar's items. Menu's popups, items, submenus and dismissal; the bar is one tab stop, the arrow keys move between its items and between open menus, and once a menu is open, hovering another item opens its menu instead. A 1.1 kB script on top of Menu's (5.5 kB together); without JavaScript each trigger opens its menu.
3514
3528
 
3515
3529
  ```ts
3516
3530
  import { Menu, Menubar } from "@lumiose/ui"; // or "@lumiose/ui/menu" and "@lumiose/ui/menubar"
@@ -3679,8 +3693,8 @@ flex row (a column when vertical); each `<lu-menu>` adds no box.
3679
3693
  Each trigger opens its menu through `popovertarget`, and the browser light-dismisses it: without
3680
3694
  JavaScript every trigger is a tab stop, and the menus open with Enter, Space or a click.
3681
3695
 
3682
- A page that renders a `Menubar` loads Menu's script and a 0.7 kB (brotli) one of its own, 4.7 kB
3683
- together, without the Lumiose runtime; islands bundle them. The menubar's script makes the bar one
3696
+ A page that renders a `Menubar` loads Menu's script and a 1.1 kB (brotli) one of its own, 5.5 kB
3697
+ together, without the Lumiose runtime; an island loads the same modules. The menubar's script makes the bar one
3684
3698
  tab stop once the page has loaded, and adds the arrow keys between items and menus and the hover
3685
3699
  switching. Each menu stays Menu's: its keys, items, submenus and dismissal.
3686
3700
 
@@ -3805,7 +3819,7 @@ above. `role="meter"` reaches screen readers in recent versions; older ones read
3805
3819
 
3806
3820
  # Navigation Menu
3807
3821
 
3808
- > A site's navigation whose items open panels of links, after Base UI's NavigationMenu and the WAI-ARIA disclosure navigation pattern: a `<nav>` landmark around a list, each trigger opening its content through `popovertarget` (a native popover placed under it by CSS anchor positioning), so the links work and the panels open on a click, one at a time, in server HTML with no JavaScript. A 3.5 kB script adds hover intent, the arrow keys, closing when focus leaves and the direction a panel comes in from. `bind:value` (or `value` and `onValueChange`) keeps the open item in a signal.
3822
+ > A site's navigation whose items open panels of links, after Base UI's NavigationMenu and the WAI-ARIA disclosure navigation pattern: a `<nav>` landmark around a list, each trigger opening its content through `popovertarget` (a native popover placed under it by CSS anchor positioning), so the links work and the panels open on a click, one at a time, in server HTML with no JavaScript. A 4.0 kB script adds hover intent, the arrow keys, closing when focus leaves and the direction a panel comes in from. `bind:value` (or `value` and `onValueChange`) keeps the open item in a signal.
3809
3823
 
3810
3824
  ```ts
3811
3825
  import { NavigationMenu } from "@lumiose/ui"; // or "@lumiose/ui/navigation-menu"
@@ -4040,8 +4054,9 @@ The `<nav>`, its links and the triggers are HTML: each trigger opens its content
4040
4054
  `popovertarget`, one at a time (an auto popover closes the others), and Escape or a click outside
4041
4055
  closes it. `value`, `onValueChange` and `bind:value` need the component to run in an island.
4042
4056
 
4043
- A page that renders a `NavigationMenu` loads a 3.5 kB (brotli) script (its dismissal module is
4044
- Menu's and Popover's too), without the Lumiose runtime; islands bundle it. `<lu-navigation-menu>`
4057
+ A page that renders a `NavigationMenu` loads its script, with the dismissal and placement modules
4058
+ it shares with Menu and Popover: 4.0 kB (brotli) without the Lumiose runtime; an island loads the
4059
+ same modules. `<lu-navigation-menu>`
4045
4060
  takes over the trigger's click (so a click keeps a content opened by hover open), opens contents on
4046
4061
  hover, adds the arrow keys, closes a content when focus leaves its item, dismisses contents as Menu
4047
4062
  and Popover do (a press of the main button outside, an Escape, another layer opening), keeps
@@ -4249,7 +4264,7 @@ Scrubbing uses pointer capture, without pointer lock.
4249
4264
 
4250
4265
  # OTP Field
4251
4266
 
4252
- > A one-time code input, after Base UI's OTPField: one native `<input>` shown as one slot per character. SMS autofill (`autocomplete="one-time-code"`), paste, Backspace, the arrow keys, `maxlength`, `pattern`, `required` and the form's value are the platform's, without JavaScript too, and assistive technology meets one labelled text field. `<lu-otp-field>`'s 1.5 kB script lays the input over the slots, keeps the code to the allowed characters, and reports completion. `bind:value` keeps a signal in sync.
4267
+ > A one-time code input, after Base UI's OTPField: one native `<input>` shown as one slot per character. SMS autofill (`autocomplete="one-time-code"`), paste, Backspace, the arrow keys, `maxlength`, `pattern`, `required` and the form's value are the platform's, without JavaScript too, and assistive technology meets one labelled text field. `<lu-otp-field>`'s 1.6 kB script lays the input over the slots, keeps the code to the allowed characters, and reports completion. `bind:value` keeps a signal in sync.
4253
4268
 
4254
4269
  ```ts
4255
4270
  import { OTPField } from "@lumiose/ui"; // or "@lumiose/ui/otp-field"
@@ -4388,7 +4403,7 @@ auto-submits.
4388
4403
  ## Without JavaScript, and the element's script
4389
4404
 
4390
4405
  The input is a native text field: autofill, typing, paste, `pattern` and `required` validation and
4391
- submission work with no script, and the slots stay hidden. `<lu-otp-field>`'s script (1.5 kB
4406
+ submission work with no script, and the slots stay hidden. `<lu-otp-field>`'s script (1.6 kB
4392
4407
  brotli) shows the slots, cleans the value as it changes (firing `valueinvalid` for refused input),
4393
4408
  fires `valuecomplete`, auto-submits, and follows a form reset.
4394
4409
 
@@ -4412,7 +4427,7 @@ does not zoom into it on focus.
4412
4427
 
4413
4428
  # Popover
4414
4429
 
4415
- > A popup anchored to its trigger: a native `popover` opened and closed by the trigger's `popovertarget`, placed with CSS anchor positioning, so it works in server HTML with no JavaScript. A 3.3 kB script moves focus into it and back, dismisses it as Base UI does (not on a right-click), keeps `aria-expanded` current and places it in browsers without anchor positioning. `bind:open` (or `open` and `onOpenChange`) keeps a signal in sync in islands. A trigger can open it on hover (`openOnHover`), and a `modal` popover is a modal `<dialog>` that makes the page behind it inert.
4430
+ > A popup anchored to its trigger: a native `popover` opened and closed by the trigger's `popovertarget`, placed with CSS anchor positioning, so it works in server HTML with no JavaScript. A 3.2 kB script moves focus into it and back, dismisses it as Base UI does (not on a right-click), keeps `aria-expanded` current and places it in browsers without anchor positioning. `bind:open` (or `open` and `onOpenChange`) keeps a signal in sync in islands. A trigger can open it on hover (`openOnHover`), and a `modal` popover is a modal `<dialog>` that makes the page behind it inert.
4416
4431
 
4417
4432
  ```ts
4418
4433
  import { Popover } from "@lumiose/ui"; // or "@lumiose/ui/popover"
@@ -4645,11 +4660,14 @@ The trigger, the close buttons, Escape, the click outside and the placement are
4645
4660
  features: a popover in a server-rendered page works before any script loads, and with none. So
4646
4661
  does a modal popover, through invoker commands and `closedby` (where a browser lacks them,
4647
4662
  [Dialog](dialog.md)'s script, which a modal popover loads, stands in). Opening on hover needs the
4648
- script.
4663
+ scripts.
4649
4664
  `open`, `onOpenChange` and `bind:open` need the component to run in an island.
4650
4665
 
4651
- A page that renders a `Popover` loads a 3.3 kB (brotli) module once, without the Lumiose runtime;
4652
- islands bundle it. It covers every Popover on the page:
4666
+ A page that renders a `Popover` loads its script once, with the dismissal and placement modules it
4667
+ shares with Menu, Combobox and Tooltip: 3.2 kB (brotli) without the Lumiose runtime. A trigger
4668
+ with `openOnHover` adds 1.0 kB, the hover timing it shares with
4669
+ [Preview Card](preview-card.md); a page without one goes without it. An island loads the same
4670
+ modules. The scripts cover every Popover on the page:
4653
4671
 
4654
4672
  - focus on open (the first focusable element, or the popup) and its return on close;
4655
4673
  - dismissal, shared with [Menu](menu.md) and [Combobox](combobox.md): a trigger's click makes the
@@ -4686,7 +4704,7 @@ popover's `dialog::backdrop`) styles the layer under it, without an element of i
4686
4704
 
4687
4705
  # Preview Card
4688
4706
 
4689
- > A card that previews a link's destination while the pointer or keyboard focus rests on the link, after Base UI's PreviewCard. The trigger is the link itself, which works as a link without JavaScript; the card is a manual popover placed with [Popover](popover.md)'s placement and opened with its hover timing by a 2.8 kB script. It is hoverable, persistent and dismissible (WCAG 1.4.13). `bind:open` keeps a signal in sync.
4707
+ > A card that previews a link's destination while the pointer or keyboard focus rests on the link, after Base UI's PreviewCard. The trigger is the link itself, which works as a link without JavaScript; the card is a manual popover placed with [Popover](popover.md)'s placement and opened with its hover timing by a 3.4 kB script. It is hoverable, persistent and dismissible (WCAG 1.4.13). `bind:open` keeps a signal in sync.
4690
4708
 
4691
4709
  ```ts
4692
4710
  import { PreviewCard } from "@lumiose/ui"; // or "@lumiose/ui/preview-card"
@@ -4800,8 +4818,9 @@ the link, with a fade (none where motion is reduced).
4800
4818
 
4801
4819
  ## Without JavaScript, and the client script
4802
4820
 
4803
- The link is a plain link and the card never opens. A page that renders a `PreviewCard` loads a
4804
- 2.8 kB (brotli) module once (with the hover timing it shares with Popover): hover and focus with
4821
+ The link is a plain link and the card never opens. A page that renders a `PreviewCard` loads its
4822
+ script once, with the hover timing, dismissal and placement modules it shares with Popover: 3.4 kB
4823
+ (brotli) without the Lumiose runtime; an island loads the same modules. It runs hover and focus with
4805
4824
  their delays, dismissal, `data-popup-open`, and the placement in browsers without CSS anchor
4806
4825
  positioning.
4807
4826
 
@@ -4940,7 +4959,7 @@ above.
4940
4959
  > A group of radios with one value, after Base UI's RadioGroup and Radio: native radios in a `<fieldset role="radiogroup">`. The browser gives the group one tab stop, arrow keys that move and select (skipping disabled radios), `required`, submission and reset, in server HTML and without JavaScript; a legend names the group. `bind:value` keeps a signal in sync.
4941
4960
 
4942
4961
  ```ts
4943
- import { Radio, RadioGroup, Fieldset } from "@lumiose/ui"; // or "@lumiose/ui/radio-group"
4962
+ import { Radio, RadioGroup, Fieldset } from "@lumiose/ui"; // or "@lumiose/ui/radio-group" and "@lumiose/ui/fieldset"
4944
4963
  import "@lumiose/ui/themes/default.css"; // optional: the default theme
4945
4964
  ```
4946
4965
 
@@ -5217,7 +5236,7 @@ supported browser.
5217
5236
 
5218
5237
  # Select
5219
5238
 
5220
- > A native `<select>` for picking one value from a list: it works with no JavaScript, submits with its form and keeps the platform's keyboard, typeahead and mobile pickers. Where the browser has customizable select (`appearance: base-select`, Chrome 135, Safari 26), `Select.Trigger` and `Select.Value` shape the button, the picker is styleable and options may hold more than text; elsewhere it is the classic select. No script; `bind:value` keeps a signal in sync in islands.
5239
+ > A native `<select>` for picking one value from a list: it works with no JavaScript, submits with its form and keeps the platform's keyboard, typeahead and mobile pickers. Where the browser has customizable select (`appearance: base-select`, Chrome 135, Safari 26), `Select.Trigger` and `Select.Value` shape the button, the picker is styleable and options may hold more than text; elsewhere it is the classic select. No script, but a tiny one for items with a `label`; `bind:value` keeps a signal in sync in islands.
5221
5240
 
5222
5241
  ```ts
5223
5242
  import { Select } from "@lumiose/ui"; // or "@lumiose/ui/select"
@@ -5273,11 +5292,17 @@ handlers. Name it with a `<label for>` pointing at its `id`, or `aria-label`, or
5273
5292
 
5274
5293
  ### Select.Item
5275
5294
 
5276
- | Prop | Type | Default | |
5277
- | ---------- | --------- | ------- | --------------------------------------------------------------------------------------------------------------------- |
5278
- | `value` | `string` | | Required: the value the select takes, and submits, when selected. |
5279
- | `disabled` | `boolean` | `false` | The option cannot be selected; the arrow keys skip it. |
5280
- | `label` | `string` | | The option's text for typeahead and the classic select, when its content starts with an icon or holds more than text. |
5295
+ | Prop | Type | Default | |
5296
+ | ---------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
5297
+ | `value` | `string` | | Required: the value the select takes, and submits, when selected. |
5298
+ | `disabled` | `boolean` | `false` | The option cannot be selected; the arrow keys skip it. |
5299
+ | `label` | `string` | | The option's name, for typeahead, assistive technology and the classic select, when its content starts with an icon or holds more than text. |
5300
+
5301
+ A customizable select's picker shows each item's content, with or without `label`. The classic
5302
+ select, which shows text only, shows the `label`. An item with a `label` renders the `<option>`'s
5303
+ `label` and `aria-label`, and a hidden copy of it first in its content (`<span hidden
5304
+ data-label>`), which typeahead matches: so `option.text`, and `<selectedcontent>`'s
5305
+ `textContent`, start with the label, while the rendered text does not.
5281
5306
 
5282
5307
  ### Select.Group
5283
5308
 
@@ -5292,7 +5317,7 @@ handlers. Name it with a `<label for>` pointing at its `id`, or `aria-label`, or
5292
5317
 
5293
5318
  **Differences from Base UI's Select.** Select is the native control, chosen over the listbox
5294
5319
  pattern because it works without JavaScript, carries forms, autofill, validation and the mobile
5295
- pickers, and ships no script. So it has no `open`, `onOpenChange` or `modal` (the platform fires no
5320
+ pickers, and needs no script. So it has no `open`, `onOpenChange` or `modal` (the platform fires no
5296
5321
  event when the picker opens; style `:open` instead), no `Label` (use `<label for>`), no `multiple`
5297
5322
  (use [Combobox](combobox.md) with `multiple`), no `alignItemWithTrigger` (the picker opens below the
5298
5323
  control and flips), no `Portal`, `Positioner`, `Popup`, `List`, `Icon`, `ItemText`,
@@ -5421,7 +5446,7 @@ A click on a `<label for>` focuses the select without opening it. A click on the
5421
5446
  picker; a click on an option selects it; a click outside closes the picker. Other browsers keep
5422
5447
  their own select keyboard and pickers, which differ by platform.
5423
5448
 
5424
- ## Without JavaScript
5449
+ ## Without JavaScript, and the client script
5425
5450
 
5426
5451
  Select needs none: it is a native control, in server HTML and with JavaScript off, on every
5427
5452
  browser. It submits under its `name`, is validated (`required`), resets with its form, takes the
@@ -5429,6 +5454,15 @@ browser's autofill and is disabled by a disabled `<fieldset>`. `value`, `onValue
5429
5454
  `bind:value` need the component to run in the browser, in an island; there `value` sets the
5430
5455
  control's value when it changes, and its `change` event calls `onValueChange`.
5431
5456
 
5457
+ Items with a `label` are the exception. A customizable select's picker shows an `<option>`'s
5458
+ `label` attribute instead of its content, through the browser's own style, which no CSS
5459
+ overrides. So a page whose selects have such items loads a 0.2 kB (brotli) script once, without
5460
+ the Lumiose runtime (an island loads the same module). When a customizable select is pressed or
5461
+ takes focus, before its picker opens, the script removes the attribute from its items: the picker
5462
+ shows their content, typeahead matches the label's hidden copy, and `aria-label` keeps their name.
5463
+ A classic select keeps the attribute, which it shows. Without the script, the customizable
5464
+ picker shows the labels alone.
5465
+
5432
5466
  ## Browser support
5433
5467
 
5434
5468
  - **Customizable select** (`appearance: base-select`, `::picker(select)`, `<selectedcontent>`, a
@@ -5439,8 +5473,8 @@ control's value when it changes, and its `change` event calls `onValueChange`.
5439
5473
  `Trigger`). Older browsers without customizable select drop them from server HTML, so an
5440
5474
  island's select falls back to rendering on the client there
5441
5475
  ([LUM4006](https://itsjavi.com/lumiose/docs/errors/LUM4006/)).
5442
- - **Typeahead** matches an option's text, or its `label`: give options that start with an icon a
5443
- `label`.
5476
+ - **Typeahead** matches an option's text, or its `label`: give options whose content starts with
5477
+ an icon (an emoji, a symbol) a `label`.
5444
5478
  - **`:open`** comes with customizable select.
5445
5479
 
5446
5480
  ## Related
@@ -6098,7 +6132,7 @@ The [WAI-ARIA tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/):
6098
6132
  The server HTML is complete: the selected tab, its panel and the tab stop are right before any
6099
6133
  script loads, and the panels of the other tabs are `hidden`. Selecting another tab needs
6100
6134
  `<lu-tabs>`' script, a 1.2 kB (brotli) module that a page rendering `Tabs` loads once, without
6101
- the Lumiose runtime; islands bundle it. A `Tabs.Indicator` is placed by CSS alone until then; the
6135
+ the Lumiose runtime; an island loads the same module. A `Tabs.Indicator` is placed by CSS alone until then; the
6102
6136
  script makes it follow a new selection. `value`, `onValueChange` and `bind:value` need the
6103
6137
  component to run in an island.
6104
6138
 
@@ -6120,7 +6154,7 @@ component to run in an island.
6120
6154
 
6121
6155
  # Toast
6122
6156
 
6123
- > Short notices in a region of their own, after Base UI's toast: server HTML renders them (a form action's result), and islands add, update and close them through a toast manager. `<lu-toast-viewport>`, a 2.6 kB light-DOM element, puts them in the top layer, times them (pausing on hover and focus), limits how many show, closes them on a swipe, Escape or their close button, focuses the region on F6, and makes screen readers announce them.
6157
+ > Short notices in a region of their own, after Base UI's toast: server HTML renders them (a form action's result), and islands add, update and close them through a toast manager. `<lu-toast-viewport>`, a 2.8 kB light-DOM element, puts them in the top layer, times them (pausing on hover and focus), limits how many show, closes them on a swipe, Escape or their close button, focuses the region on F6, and makes screen readers announce them.
6124
6158
 
6125
6159
  ```ts
6126
6160
  import { Toast } from "@lumiose/ui"; // or "@lumiose/ui/toast"
@@ -6404,7 +6438,7 @@ and back when it closes or leaves the page.
6404
6438
 
6405
6439
  What is server-rendered and what needs the client:
6406
6440
 
6407
- | Server HTML | The element's script (2.6 kB, loaded once per page) |
6441
+ | Server HTML | The element's script (2.8 kB, loaded once per page, as an island loads it) |
6408
6442
  | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
6409
6443
  | The region and the toasts a page renders, with their text, roles and buttons. | The top layer (a manual popover), so toasts sit above dialogs and survive other popovers' light dismiss. |
6410
6444
  | Toasts show at the window's bottom end (`toast.css`). | Timers, pausing, the limit, closing (button, Escape, swipe), F6, focus when a toast goes. |
@@ -6534,7 +6568,7 @@ group's script presses it on server pages too. For a value a form submits, use a
6534
6568
  > A set of [Toggles](toggle.md) with one value, after Base UI's ToggleGroup: text alignment (one pressed at a time) or text style (several). `<lu-toggle-group role="group">` presses its toggles and gives the group one tab stop that the arrow keys, Home and End move, following the orientation and the direction, on server pages and in islands. `bind:value` keeps a signal in sync.
6535
6569
 
6536
6570
  ```ts
6537
- import { Toggle, ToggleGroup } from "@lumiose/ui"; // or "@lumiose/ui/toggle-group"
6571
+ import { Toggle, ToggleGroup } from "@lumiose/ui"; // or "@lumiose/ui/toggle" and "@lumiose/ui/toggle-group"
6538
6572
  import "@lumiose/ui/themes/default.css"; // optional: the default theme
6539
6573
  ```
6540
6574
 
@@ -6558,7 +6592,7 @@ import "@lumiose/ui/themes/default.css"; // optional: the default theme
6558
6592
 
6559
6593
  | Prop | Type | Default | |
6560
6594
  | --------------- | ------------------------------ | -------------- | -------------------------------------------------------------------------- |
6561
- | `value` | `string[]` | | Presses these toggles when it changes. Pair it with `onValueChange`. |
6595
+ | `value` | `string[]` | | The pressed toggles. Pair it with `onValueChange`, which updates it. |
6562
6596
  | `defaultValue` | `string[]` | `[]` | The toggles pressed at first. |
6563
6597
  | `onValueChange` | `(value: string[]) => void` | | Called after the user presses or releases a toggle. |
6564
6598
  | `bind:value` | `Signal<string[]>` | | Stands in for `value` and `onValueChange`. |
@@ -6570,6 +6604,11 @@ import "@lumiose/ui/themes/default.css"; // optional: the default theme
6570
6604
  Every other prop passes through to `<lu-toggle-group>`: `aria-label`, `aria-labelledby`, `id`,
6571
6605
  `class`. Name the group with one of the first two.
6572
6606
 
6607
+ With `value`, the group is controlled: after the user presses or releases a toggle, the toggles
6608
+ show `value` again, so a press shows only once `onValueChange` has updated `value` with it. One
6609
+ that leaves `value` as it was keeps the toggles as they were: an `onValueChange` that ignores an
6610
+ empty array keeps one toggle pressed. Setting `value` to the toggles already pressed does nothing.
6611
+
6573
6612
  Each toggle's `value` is its identity in the group; its `onPressedChange` still fires. A
6574
6613
  `pressedchange` event (`detail.pressed`) bubbles from a toggle before it changes, and
6575
6614
  `event.preventDefault()` cancels the change.
@@ -6580,7 +6619,8 @@ Each toggle's `value` is its identity in the group; its `onPressedChange` still
6580
6619
  ## Example
6581
6620
 
6582
6621
  ```tsx check
6583
- import { Toggle, ToggleGroup } from "@lumiose/ui";
6622
+ import { Toggle } from "@lumiose/ui/toggle";
6623
+ import { ToggleGroup } from "@lumiose/ui/toggle-group";
6584
6624
  import { signal } from "lumiose";
6585
6625
 
6586
6626
  export function TextAlignment() {
@@ -6633,7 +6673,7 @@ and single tab stop take over: the toggles are toolbar items.
6633
6673
  ## Without JavaScript, and the element's script
6634
6674
 
6635
6675
  The server HTML has each toggle's `aria-pressed` and the tab stop. `<lu-toggle-group>`'s script
6636
- (0.9 kB brotli) presses the toggles and moves focus, on server pages and in islands; when the tab
6676
+ (1.0 kB brotli) presses the toggles and moves focus, on server pages and in islands; when the tab
6637
6677
  stop's toggle goes or is disabled, or a disabled group is enabled, the first pressed (else enabled)
6638
6678
  toggle takes the tab stop. With no script
6639
6679
  at all the toggles show their state but do not change. For values a form submits, use a
@@ -6653,7 +6693,7 @@ Custom elements and `aria-pressed`: every supported browser.
6653
6693
 
6654
6694
  # Toolbar
6655
6695
 
6656
- > A set of controls with one tab stop, after Base UI's Toolbar and the APG toolbar pattern: `<lu-toolbar role="toolbar">` moves focus between its items with the arrow keys, Home and End, following the orientation and the text direction. Its items are native: buttons that stay focusable when disabled, links, inputs, and the toggles of a [Toggle Group](toggle-group.md) inside it. In the server HTML every item is a tab stop, so each is reachable without JavaScript; a 1.1 kB script makes them one tab stop and moves it.
6696
+ > A set of controls with one tab stop, after Base UI's Toolbar and the APG toolbar pattern: `<lu-toolbar role="toolbar">` moves focus between its items with the arrow keys, Home and End, following the orientation and the text direction. Its items are native: buttons that stay focusable when disabled, links, inputs, and the toggles of a [Toggle Group](toggle-group.md) inside it. In the server HTML every item is a tab stop, so each is reachable without JavaScript; a 1.2 kB script makes them one tab stop and moves it.
6657
6697
 
6658
6698
  ```ts
6659
6699
  import { Toolbar } from "@lumiose/ui"; // or "@lumiose/ui/toolbar"
@@ -6775,7 +6815,7 @@ its text selected, as in Base UI. Base UI advises at most one input, last.
6775
6815
  ## Without JavaScript, and the element's script
6776
6816
 
6777
6817
  The server HTML is a labelled toolbar whose items are all tab stops, so Tab reaches each one
6778
- without JavaScript or before the script runs. `<lu-toolbar>`'s script (1.1 kB brotli with Button's)
6818
+ without JavaScript or before the script runs. `<lu-toolbar>`'s script (1.2 kB brotli with Button's)
6779
6819
  makes the toolbar one tab stop, moves focus with the keys and keeps the tab stop on the last focused
6780
6820
  item.
6781
6821
 
@@ -6794,7 +6834,7 @@ Custom elements and `role="toolbar"`: every supported browser and screen reader.
6794
6834
 
6795
6835
  # Tooltip
6796
6836
 
6797
- > A short tip next to its trigger, on hover or keyboard focus: a native `popover="hint"` with `role="tooltip"`, placed with CSS anchor positioning. The trigger is described by the tip in server HTML, so screen readers announce it without hovering; a 2.0 kB script opens and closes it as Base UI's tooltip does. `bind:open` (or `open` and `onOpenChange`) keeps a signal in sync in islands.
6837
+ > A short tip next to its trigger, on hover or keyboard focus: a native `popover="hint"` with `role="tooltip"`, placed with CSS anchor positioning. The trigger is described by the tip in server HTML, so screen readers announce it without hovering; a 2.1 kB script opens and closes it as Base UI's tooltip does. `bind:open` (or `open` and `onOpenChange`) keeps a signal in sync in islands.
6798
6838
 
6799
6839
  ```ts
6800
6840
  import { Tooltip } from "@lumiose/ui"; // or "@lumiose/ui/tooltip"
@@ -6905,8 +6945,9 @@ A tooltip inside a popover keeps the popover open, and one inside a dialog belon
6905
6945
  ## Without JavaScript, and the client script
6906
6946
 
6907
6947
  Without JavaScript the tip never shows, but the trigger keeps its description: screen readers
6908
- announce it. A page that renders a `Tooltip` loads a 2.0 kB (brotli) module once, without the
6909
- Lumiose runtime; islands bundle it. It opens and closes every tooltip on the page, and places them
6948
+ announce it. A page that renders a `Tooltip` loads its script once, with the placement module it
6949
+ shares with Popover and Menu: 2.1 kB (brotli) without the Lumiose runtime; an island loads the
6950
+ same modules. It opens and closes every tooltip on the page, and places them
6910
6951
  in browsers without CSS anchor positioning.
6911
6952
 
6912
6953
  ## Browser support