@lumiose/ui 0.1.1 → 0.1.2

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 (62) hide show
  1. package/README.md +2 -2
  2. package/dist/accordion/accordion.tsx +3 -2
  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 +3 -2
  9. package/dist/combobox/combobox.tsx +6 -5
  10. package/dist/context-menu/context-menu.tsx +4 -3
  11. package/dist/dialog/dialog.tsx +2 -1
  12. package/dist/field/field.tsx +11 -10
  13. package/dist/form/reset.ts +4 -2
  14. package/dist/input/input.tsx +4 -1
  15. package/dist/menu/menu.tsx +9 -8
  16. package/dist/navigation-menu/navigation-menu.tsx +4 -3
  17. package/dist/number-field/number-field.tsx +4 -3
  18. package/dist/otp-field/otp-field.tsx +9 -8
  19. package/dist/popover/popover-hover.script.ts +27 -0
  20. package/dist/popover/popover.script.ts +19 -25
  21. package/dist/popover/popover.tsx +8 -3
  22. package/dist/preview-card/preview-card.tsx +2 -1
  23. package/dist/radio-group/radio-group.tsx +3 -2
  24. package/dist/select/select.script.ts +26 -0
  25. package/dist/select/select.tsx +36 -10
  26. package/dist/slider/slider.tsx +4 -3
  27. package/dist/switch/switch.tsx +4 -3
  28. package/dist/tabs/tabs.tsx +3 -2
  29. package/dist/toast/toast.tsx +3 -2
  30. package/dist/toggle/toggle.tsx +6 -5
  31. package/dist/toggle-group/toggle-group.script.ts +12 -4
  32. package/dist/toggle-group/toggle-group.tsx +16 -4
  33. package/dist/tooltip/tooltip.tsx +4 -1
  34. package/docs/README.md +9 -6
  35. package/docs/accordion.md +3 -2
  36. package/docs/autocomplete.md +2 -2
  37. package/docs/avatar.md +2 -2
  38. package/docs/checkbox-group.md +1 -1
  39. package/docs/checkbox.md +1 -1
  40. package/docs/collapsible.md +4 -3
  41. package/docs/combobox.md +4 -3
  42. package/docs/context-menu.md +3 -3
  43. package/docs/dialog.md +1 -1
  44. package/docs/drawer.md +3 -2
  45. package/docs/field.md +2 -2
  46. package/docs/installation.md +3 -3
  47. package/docs/llms-full.txt +107 -72
  48. package/docs/llms.txt +14 -14
  49. package/docs/menu.md +4 -3
  50. package/docs/menubar.md +3 -3
  51. package/docs/navigation-menu.md +4 -3
  52. package/docs/otp-field.md +2 -2
  53. package/docs/popover.md +7 -4
  54. package/docs/preview-card.md +4 -3
  55. package/docs/radio-group.md +1 -1
  56. package/docs/select.md +25 -10
  57. package/docs/tabs.md +1 -1
  58. package/docs/toast.md +2 -2
  59. package/docs/toggle-group.md +10 -4
  60. package/docs/toolbar.md +2 -2
  61. package/docs/tooltip.md +4 -3
  62. package/package.json +1 -1
@@ -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
@@ -48,7 +47,8 @@ Anatomy, API, Example, Styling, Keyboard and focus, Without JavaScript, Browser
48
47
  with `onCheckedChange`, `value` with `onValueChange`. Pass a signal's value and write it back in
49
48
  the callback to keep both in sync, or leave both out and the element keeps its own state. Custom
50
49
  elements report changes with lowercase DOM events, like native ones: `openchange`,
51
- `checkedchange` and `change`.
50
+ `checkedchange` and `change`. The callbacks run inside `batch()`, as event handlers do: what
51
+ they write renders together, and the DOM is current as soon as the event returns.
52
52
  - **Keyboard contracts.** Every component follows its WAI-ARIA Authoring Practices pattern, and
53
53
  its end-to-end tests check the keyboard paths.
54
54
  - **WCAG 2.2 AA.** Every component meets it: the components own semantics, keyboard and
@@ -81,7 +81,7 @@ custom properties, named as in shadcn/ui, are listed in [Theming](theming.md#the
81
81
  | [Tooltip](tooltip.md) | Available: native hint popover, opened by its script |
82
82
  | [Menu](menu.md) | Available: popover menu, opens without JavaScript |
83
83
  | [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 |
84
+ | [Select](select.md) | Available: native `<select>`, customizable where supported, no script but for `label`s |
85
85
  | [Tabs](tabs.md) | Available: server HTML with the full state, `<lu-tabs>` adds the keys |
86
86
  | [Field](field.md) | Available: label, description, errors; native validation in the field |
87
87
  | [Fieldset](fieldset.md) | Available: native `<fieldset>` and `<legend>`, no script |
@@ -176,7 +176,10 @@ errors are yours.
176
176
  `@lumiose/ui` is opt-in, so its size is reported apart from the runtime's caps:
177
177
  `pnpm --filter @lumiose/ui size` prints, per component, the JavaScript a server-rendered page loads
178
178
  for its custom elements, the JavaScript an island that uses it loads, and its CSS (minified, gzip
179
- and brotli).
179
+ and brotli). The scripts load as separate modules, in islands too: each component's script, and
180
+ the modules several scripts share (the dismissal model, the placement fallback, the hover timing),
181
+ each a file of its own, as an app's build splits them. The sizes on the component pages are those
182
+ files' brotli sizes added up.
180
183
 
181
184
  ## Related
182
185
 
@@ -190,13 +193,13 @@ and brotli).
190
193
 
191
194
  > 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
195
 
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`.
196
+ **Status:** in development, on npm with its own 0.x versions, apart from the version the other
197
+ Lumiose packages share.
195
198
 
196
199
  ## Add the package
197
200
 
198
201
  `@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:
202
+ with the app's own code, and `lumiose` is its peer dependency. Install it from npm:
200
203
 
201
204
  ```sh
202
205
  pnpm add @lumiose/ui
@@ -578,8 +581,9 @@ closed item are HTML features: an accordion in a server-rendered page works befo
578
581
  loads, and with none. Disabled triggers ignore the pointer through CSS. `value`, `onValueChange`
579
582
  and `bind:value` need the component to run in an island.
580
583
 
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
584
+ A page that renders an `Accordion` loads its script once, with the panel and animation modules it
585
+ shares with Collapsible: 1.3 kB (brotli) without the Lumiose runtime; an island loads the same
586
+ modules. It defines `<lu-accordion>`, which adds the arrow keys, Home and End, and sets
583
587
  the panels' animation hooks (shared with [Collapsible](collapsible.md)); a disabled trigger does
584
588
  not toggle, also from the keyboard.
585
589
 
@@ -602,7 +606,7 @@ not toggle, also from the keyboard.
602
606
 
603
607
  # Autocomplete
604
608
 
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.
609
+ > 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
610
 
607
611
  ```ts
608
612
  import { Autocomplete } from "@lumiose/ui"; // or "@lumiose/ui/autocomplete"
@@ -747,7 +751,7 @@ Focus stays in the input: the highlight moves through `aria-activedescendant`.
747
751
  ## Without JavaScript, and the element's script
748
752
 
749
753
  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)
754
+ no script; the suggestions need it. `<lu-combobox>`'s script (5.8 kB brotli, shared with Combobox)
751
755
  filters and opens them, moves the highlight, fills in a choice (firing `input` and `change` on the
752
756
  input) and runs inline completion.
753
757
 
@@ -946,8 +950,8 @@ when it fails) and hides its fallback, from sight and from assistive technology,
946
950
  the avatar twice; an avatar without an image shows its fallback.
947
951
  `onLoadingStatusChange` needs the component to run in an island.
948
952
 
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
953
+ A page that renders an `Avatar` loads a 0.5 kB (brotli) script, without the Lumiose runtime; an
954
+ island loads the same module. `<lu-avatar>` reads the image's state when it connects (a cached image shows
951
955
  at once), then follows its `load` and `error` events and changes to its `src` and `srcset`. An
952
956
  image that is complete without a size (an SVG without intrinsic dimensions, or a broken image) is
953
957
  told apart with `decode()`.
@@ -1180,7 +1184,7 @@ A native checkbox: toggling, submission, `required`, reset and `disabled` (its o
1180
1184
  work with no script.
1181
1185
 
1182
1186
  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
1187
+ script once, without the Lumiose runtime; an island loads the same module. It turns server HTML's
1184
1188
  `data-indeterminate` into the `indeterminate` property (which has no attribute), makes read-only
1185
1189
  inputs ignore clicks, Space and the label, runs the parent checkbox of a group and a [Radio
1186
1190
  Group](radio-group.md)'s arrow keys, and adds an
@@ -1207,7 +1211,7 @@ browser.
1207
1211
  > 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
1212
 
1209
1213
  ```ts
1210
- import { CheckboxGroup, Checkbox, Fieldset } from "@lumiose/ui"; // or "@lumiose/ui/checkbox-group"
1214
+ import { CheckboxGroup, Checkbox, Fieldset } from "@lumiose/ui"; // or "@lumiose/ui/checkbox-group", "@lumiose/ui/checkbox" and "@lumiose/ui/fieldset"
1211
1215
  import "@lumiose/ui/themes/default.css"; // optional: the default theme
1212
1216
  ```
1213
1217
 
@@ -1319,7 +1323,7 @@ Every supported browser.
1319
1323
 
1320
1324
  # Collapsible
1321
1325
 
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.
1326
+ > 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
1327
 
1324
1328
  ```ts
1325
1329
  import { Collapsible } from "@lumiose/ui"; // or "@lumiose/ui/collapsible"
@@ -1460,8 +1464,9 @@ Collapsible in a server-rendered page works before any script loads, and with no
1460
1464
  trigger ignores the pointer through CSS. `open`, `onOpenChange` and `bind:open` need the component
1461
1465
  to run in an island.
1462
1466
 
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
1467
+ A page that renders a `Collapsible` loads 0.9 kB (brotli) of modules once (its script, and the
1468
+ panel and animation modules it shares with Accordion), without the Lumiose runtime; an island
1469
+ loads the same modules. It covers every Collapsible and [Accordion](accordion.md) panel on the
1465
1470
  page:
1466
1471
 
1467
1472
  - the animation hooks above, set on every opening and closing, whatever caused it (the trigger,
@@ -1487,7 +1492,7 @@ page:
1487
1492
 
1488
1493
  # Combobox
1489
1494
 
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.
1495
+ > 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
1496
 
1492
1497
  ```ts
1493
1498
  import { Combobox } from "@lumiose/ui"; // or "@lumiose/ui/combobox"
@@ -1800,8 +1805,9 @@ In the server HTML the input is empty even with a selected value: the input rend
1800
1805
  options, so the server does not know the selected option's label when it writes the input. The
1801
1806
  script puts the label in as it upgrades the element (decision-62).
1802
1807
 
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
1808
+ A page that renders a `Combobox` loads the script once, with the dismissal and placement modules
1809
+ it shares with Menu and Popover: 5.8 kB (brotli) without the Lumiose runtime, and an island loads
1810
+ the same modules. It defines `lu-combobox`, which upgrades every combobox on
1805
1811
  the page in place: it reads the selection the server rendered (`aria-selected="true"`), puts its
1806
1812
  label in the input (text typed before it ran is kept, as a search), renders the chips, and from
1807
1813
  then on owns the value, the filtering, the popup,
@@ -1859,7 +1865,7 @@ The element is also an API of its own, for scripts and other frameworks:
1859
1865
 
1860
1866
  # Context Menu
1861
1867
 
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.
1868
+ > 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
1869
 
1864
1870
  ```ts
1865
1871
  import { ContextMenu } from "@lumiose/ui"; // or "@lumiose/ui/context-menu"
@@ -2034,8 +2040,8 @@ Opening a menu at the pointer needs the script: without JavaScript a right-click
2034
2040
  browser's own menu, and the popup stays hidden. `open`, `onOpenChange` and `bind:open` need the
2035
2041
  component to run in an island.
2036
2042
 
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
2043
+ A page that renders a `ContextMenu` loads Menu's script and a 0.9 kB (brotli) one of its own,
2044
+ 5.4 kB together, without the Lumiose runtime; an island loads the same modules. The element opens the menu on a
2039
2045
  right-click, a long press or the keys, at the pointer, through a zero-size anchor that it moves
2040
2046
  there (CSS anchor positioning places the popup, with Menu's flips; without it, the shared
2041
2047
  placement script does). Menu's script does the rest: keys, items, submenus, and dismissal shared
@@ -2280,7 +2286,7 @@ server-rendered page works before any script loads, and with none. `open`, `onOp
2280
2286
 
2281
2287
  Where the native element falls short of Base UI's dialog, a small script closes the gap: a page
2282
2288
  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:
2289
+ an island loads the same module. It covers every `<dialog>` on the page:
2284
2290
 
2285
2291
  - a right-click or a second finger outside does not close the dialog;
2286
2292
  - `data-nested-dialog-open` and `--lu-nested-dialogs` on a dialog with open dialogs inside;
@@ -2500,8 +2506,9 @@ The trigger, the close buttons, Escape and the click outside are HTML features,
2500
2506
  drawer in a server-rendered page works before any script loads, and with none, against its edge.
2501
2507
  `open`, `onOpenChange` and `bind:open` need the component to run in an island.
2502
2508
 
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
2509
+ A page that renders a `Drawer` loads Dialog's script and a 1.1 kB (brotli) one of its own (2.8 kB
2510
+ together, with the animation module it shares with Toast), without the Lumiose runtime; an island
2511
+ loads the same modules. The drawer's script handles the
2505
2512
  swipe, with the mouse and pen through pointer events and on touch screens through touch events,
2506
2513
  taking the gesture from scrolling only once it is a swipe.
2507
2514
 
@@ -2778,8 +2785,8 @@ the message Error's id (`Field.Error` without children or `match`) even when the
2778
2785
  Errors with children or a `match`. Give a field that must read well without JavaScript a message
2779
2786
  Error, and its Descriptions before the control.
2780
2787
 
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
2788
+ A page that renders a `Field` loads a 1.7 kB (brotli) script once, without the Lumiose runtime; an
2789
+ island loads the same module. On a submit attempt (a submit button, Enter in a field, `requestSubmit()` or
2783
2790
  the form's `reportValidity()`; a silent `checkValidity()` changes nothing) it cancels the browser's
2784
2791
  `invalid` bubble for fields that have an Error part for the problem and shows it there instead (the
2785
2792
  browser's message, or one your `validate` set), with the timing above, and focuses the first
@@ -3142,7 +3149,7 @@ Every browser; input types and constraints as the browser supports them.
3142
3149
 
3143
3150
  # Menu
3144
3151
 
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.
3152
+ > 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
3153
 
3147
3154
  ```ts
3148
3155
  import { Menu } from "@lumiose/ui"; // or "@lumiose/ui/menu"
@@ -3463,8 +3470,9 @@ submenu closes it, but the browser leaves focus on the page rather than on the s
3463
3470
  Tab starts over from the top (the script returns focus to the trigger). `open`, `onOpenChange` and
3464
3471
  the `bind:` props need the component to run in the browser, in an island.
3465
3472
 
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`,
3473
+ The keyboard contract comes from a small script: a page that renders a `Menu` loads it once, with
3474
+ the dismissal and placement modules it shares with Popover and Combobox: 4.4 kB (brotli) without
3475
+ the Lumiose runtime, and an island loads the same modules. It defines `lu-menu`,
3468
3476
  which upgrades every menu on the page in place, and adds what the native popover lacks: focus on
3469
3477
  open and its return on close, the arrows, Home, End and typeahead, Escape one level at a time, Tab,
3470
3478
  submenus, checkbox and radio items, disabled items, `data-highlighted`, and the pointer behaviour
@@ -3510,7 +3518,7 @@ The element is also an API of its own, for scripts and other frameworks:
3510
3518
 
3511
3519
  # Menubar
3512
3520
 
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.
3521
+ > 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
3522
 
3515
3523
  ```ts
3516
3524
  import { Menu, Menubar } from "@lumiose/ui"; // or "@lumiose/ui/menu" and "@lumiose/ui/menubar"
@@ -3679,8 +3687,8 @@ flex row (a column when vertical); each `<lu-menu>` adds no box.
3679
3687
  Each trigger opens its menu through `popovertarget`, and the browser light-dismisses it: without
3680
3688
  JavaScript every trigger is a tab stop, and the menus open with Enter, Space or a click.
3681
3689
 
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
3690
+ A page that renders a `Menubar` loads Menu's script and a 1.1 kB (brotli) one of its own, 5.5 kB
3691
+ together, without the Lumiose runtime; an island loads the same modules. The menubar's script makes the bar one
3684
3692
  tab stop once the page has loaded, and adds the arrow keys between items and menus and the hover
3685
3693
  switching. Each menu stays Menu's: its keys, items, submenus and dismissal.
3686
3694
 
@@ -3805,7 +3813,7 @@ above. `role="meter"` reaches screen readers in recent versions; older ones read
3805
3813
 
3806
3814
  # Navigation Menu
3807
3815
 
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.
3816
+ > 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
3817
 
3810
3818
  ```ts
3811
3819
  import { NavigationMenu } from "@lumiose/ui"; // or "@lumiose/ui/navigation-menu"
@@ -4040,8 +4048,9 @@ The `<nav>`, its links and the triggers are HTML: each trigger opens its content
4040
4048
  `popovertarget`, one at a time (an auto popover closes the others), and Escape or a click outside
4041
4049
  closes it. `value`, `onValueChange` and `bind:value` need the component to run in an island.
4042
4050
 
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>`
4051
+ A page that renders a `NavigationMenu` loads its script, with the dismissal and placement modules
4052
+ it shares with Menu and Popover: 4.0 kB (brotli) without the Lumiose runtime; an island loads the
4053
+ same modules. `<lu-navigation-menu>`
4045
4054
  takes over the trigger's click (so a click keeps a content opened by hover open), opens contents on
4046
4055
  hover, adds the arrow keys, closes a content when focus leaves its item, dismisses contents as Menu
4047
4056
  and Popover do (a press of the main button outside, an Escape, another layer opening), keeps
@@ -4249,7 +4258,7 @@ Scrubbing uses pointer capture, without pointer lock.
4249
4258
 
4250
4259
  # OTP Field
4251
4260
 
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.
4261
+ > 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
4262
 
4254
4263
  ```ts
4255
4264
  import { OTPField } from "@lumiose/ui"; // or "@lumiose/ui/otp-field"
@@ -4388,7 +4397,7 @@ auto-submits.
4388
4397
  ## Without JavaScript, and the element's script
4389
4398
 
4390
4399
  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
4400
+ submission work with no script, and the slots stay hidden. `<lu-otp-field>`'s script (1.6 kB
4392
4401
  brotli) shows the slots, cleans the value as it changes (firing `valueinvalid` for refused input),
4393
4402
  fires `valuecomplete`, auto-submits, and follows a form reset.
4394
4403
 
@@ -4412,7 +4421,7 @@ does not zoom into it on focus.
4412
4421
 
4413
4422
  # Popover
4414
4423
 
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.
4424
+ > 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
4425
 
4417
4426
  ```ts
4418
4427
  import { Popover } from "@lumiose/ui"; // or "@lumiose/ui/popover"
@@ -4645,11 +4654,14 @@ The trigger, the close buttons, Escape, the click outside and the placement are
4645
4654
  features: a popover in a server-rendered page works before any script loads, and with none. So
4646
4655
  does a modal popover, through invoker commands and `closedby` (where a browser lacks them,
4647
4656
  [Dialog](dialog.md)'s script, which a modal popover loads, stands in). Opening on hover needs the
4648
- script.
4657
+ scripts.
4649
4658
  `open`, `onOpenChange` and `bind:open` need the component to run in an island.
4650
4659
 
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:
4660
+ A page that renders a `Popover` loads its script once, with the dismissal and placement modules it
4661
+ shares with Menu, Combobox and Tooltip: 3.2 kB (brotli) without the Lumiose runtime. A trigger
4662
+ with `openOnHover` adds 1.0 kB, the hover timing it shares with
4663
+ [Preview Card](preview-card.md); a page without one goes without it. An island loads the same
4664
+ modules. The scripts cover every Popover on the page:
4653
4665
 
4654
4666
  - focus on open (the first focusable element, or the popup) and its return on close;
4655
4667
  - dismissal, shared with [Menu](menu.md) and [Combobox](combobox.md): a trigger's click makes the
@@ -4686,7 +4698,7 @@ popover's `dialog::backdrop`) styles the layer under it, without an element of i
4686
4698
 
4687
4699
  # Preview Card
4688
4700
 
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.
4701
+ > 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
4702
 
4691
4703
  ```ts
4692
4704
  import { PreviewCard } from "@lumiose/ui"; // or "@lumiose/ui/preview-card"
@@ -4800,8 +4812,9 @@ the link, with a fade (none where motion is reduced).
4800
4812
 
4801
4813
  ## Without JavaScript, and the client script
4802
4814
 
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
4815
+ The link is a plain link and the card never opens. A page that renders a `PreviewCard` loads its
4816
+ script once, with the hover timing, dismissal and placement modules it shares with Popover: 3.4 kB
4817
+ (brotli) without the Lumiose runtime; an island loads the same modules. It runs hover and focus with
4805
4818
  their delays, dismissal, `data-popup-open`, and the placement in browsers without CSS anchor
4806
4819
  positioning.
4807
4820
 
@@ -4940,7 +4953,7 @@ above.
4940
4953
  > 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
4954
 
4942
4955
  ```ts
4943
- import { Radio, RadioGroup, Fieldset } from "@lumiose/ui"; // or "@lumiose/ui/radio-group"
4956
+ import { Radio, RadioGroup, Fieldset } from "@lumiose/ui"; // or "@lumiose/ui/radio-group" and "@lumiose/ui/fieldset"
4944
4957
  import "@lumiose/ui/themes/default.css"; // optional: the default theme
4945
4958
  ```
4946
4959
 
@@ -5217,7 +5230,7 @@ supported browser.
5217
5230
 
5218
5231
  # Select
5219
5232
 
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.
5233
+ > 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
5234
 
5222
5235
  ```ts
5223
5236
  import { Select } from "@lumiose/ui"; // or "@lumiose/ui/select"
@@ -5273,11 +5286,17 @@ handlers. Name it with a `<label for>` pointing at its `id`, or `aria-label`, or
5273
5286
 
5274
5287
  ### Select.Item
5275
5288
 
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. |
5289
+ | Prop | Type | Default | |
5290
+ | ---------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
5291
+ | `value` | `string` | | Required: the value the select takes, and submits, when selected. |
5292
+ | `disabled` | `boolean` | `false` | The option cannot be selected; the arrow keys skip it. |
5293
+ | `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. |
5294
+
5295
+ A customizable select's picker shows each item's content, with or without `label`. The classic
5296
+ select, which shows text only, shows the `label`. An item with a `label` renders the `<option>`'s
5297
+ `label` and `aria-label`, and a hidden copy of it first in its content (`<span hidden
5298
+ data-label>`), which typeahead matches: so `option.text`, and `<selectedcontent>`'s
5299
+ `textContent`, start with the label, while the rendered text does not.
5281
5300
 
5282
5301
  ### Select.Group
5283
5302
 
@@ -5292,7 +5311,7 @@ handlers. Name it with a `<label for>` pointing at its `id`, or `aria-label`, or
5292
5311
 
5293
5312
  **Differences from Base UI's Select.** Select is the native control, chosen over the listbox
5294
5313
  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
5314
+ pickers, and needs no script. So it has no `open`, `onOpenChange` or `modal` (the platform fires no
5296
5315
  event when the picker opens; style `:open` instead), no `Label` (use `<label for>`), no `multiple`
5297
5316
  (use [Combobox](combobox.md) with `multiple`), no `alignItemWithTrigger` (the picker opens below the
5298
5317
  control and flips), no `Portal`, `Positioner`, `Popup`, `List`, `Icon`, `ItemText`,
@@ -5421,7 +5440,7 @@ A click on a `<label for>` focuses the select without opening it. A click on the
5421
5440
  picker; a click on an option selects it; a click outside closes the picker. Other browsers keep
5422
5441
  their own select keyboard and pickers, which differ by platform.
5423
5442
 
5424
- ## Without JavaScript
5443
+ ## Without JavaScript, and the client script
5425
5444
 
5426
5445
  Select needs none: it is a native control, in server HTML and with JavaScript off, on every
5427
5446
  browser. It submits under its `name`, is validated (`required`), resets with its form, takes the
@@ -5429,6 +5448,15 @@ browser's autofill and is disabled by a disabled `<fieldset>`. `value`, `onValue
5429
5448
  `bind:value` need the component to run in the browser, in an island; there `value` sets the
5430
5449
  control's value when it changes, and its `change` event calls `onValueChange`.
5431
5450
 
5451
+ Items with a `label` are the exception. A customizable select's picker shows an `<option>`'s
5452
+ `label` attribute instead of its content, through the browser's own style, which no CSS
5453
+ overrides. So a page whose selects have such items loads a 0.2 kB (brotli) script once, without
5454
+ the Lumiose runtime (an island loads the same module). When a customizable select is pressed or
5455
+ takes focus, before its picker opens, the script removes the attribute from its items: the picker
5456
+ shows their content, typeahead matches the label's hidden copy, and `aria-label` keeps their name.
5457
+ A classic select keeps the attribute, which it shows. Without the script, the customizable
5458
+ picker shows the labels alone.
5459
+
5432
5460
  ## Browser support
5433
5461
 
5434
5462
  - **Customizable select** (`appearance: base-select`, `::picker(select)`, `<selectedcontent>`, a
@@ -5439,8 +5467,8 @@ control's value when it changes, and its `change` event calls `onValueChange`.
5439
5467
  `Trigger`). Older browsers without customizable select drop them from server HTML, so an
5440
5468
  island's select falls back to rendering on the client there
5441
5469
  ([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`.
5470
+ - **Typeahead** matches an option's text, or its `label`: give options whose content starts with
5471
+ an icon (an emoji, a symbol) a `label`.
5444
5472
  - **`:open`** comes with customizable select.
5445
5473
 
5446
5474
  ## Related
@@ -6098,7 +6126,7 @@ The [WAI-ARIA tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/):
6098
6126
  The server HTML is complete: the selected tab, its panel and the tab stop are right before any
6099
6127
  script loads, and the panels of the other tabs are `hidden`. Selecting another tab needs
6100
6128
  `<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
6129
+ the Lumiose runtime; an island loads the same module. A `Tabs.Indicator` is placed by CSS alone until then; the
6102
6130
  script makes it follow a new selection. `value`, `onValueChange` and `bind:value` need the
6103
6131
  component to run in an island.
6104
6132
 
@@ -6120,7 +6148,7 @@ component to run in an island.
6120
6148
 
6121
6149
  # Toast
6122
6150
 
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.
6151
+ > 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
6152
 
6125
6153
  ```ts
6126
6154
  import { Toast } from "@lumiose/ui"; // or "@lumiose/ui/toast"
@@ -6404,7 +6432,7 @@ and back when it closes or leaves the page.
6404
6432
 
6405
6433
  What is server-rendered and what needs the client:
6406
6434
 
6407
- | Server HTML | The element's script (2.6 kB, loaded once per page) |
6435
+ | Server HTML | The element's script (2.8 kB, loaded once per page, as an island loads it) |
6408
6436
  | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
6409
6437
  | 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
6438
  | 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 +6562,7 @@ group's script presses it on server pages too. For a value a form submits, use a
6534
6562
  > 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
6563
 
6536
6564
  ```ts
6537
- import { Toggle, ToggleGroup } from "@lumiose/ui"; // or "@lumiose/ui/toggle-group"
6565
+ import { Toggle, ToggleGroup } from "@lumiose/ui"; // or "@lumiose/ui/toggle" and "@lumiose/ui/toggle-group"
6538
6566
  import "@lumiose/ui/themes/default.css"; // optional: the default theme
6539
6567
  ```
6540
6568
 
@@ -6558,7 +6586,7 @@ import "@lumiose/ui/themes/default.css"; // optional: the default theme
6558
6586
 
6559
6587
  | Prop | Type | Default | |
6560
6588
  | --------------- | ------------------------------ | -------------- | -------------------------------------------------------------------------- |
6561
- | `value` | `string[]` | | Presses these toggles when it changes. Pair it with `onValueChange`. |
6589
+ | `value` | `string[]` | | The pressed toggles. Pair it with `onValueChange`, which updates it. |
6562
6590
  | `defaultValue` | `string[]` | `[]` | The toggles pressed at first. |
6563
6591
  | `onValueChange` | `(value: string[]) => void` | | Called after the user presses or releases a toggle. |
6564
6592
  | `bind:value` | `Signal<string[]>` | | Stands in for `value` and `onValueChange`. |
@@ -6570,6 +6598,11 @@ import "@lumiose/ui/themes/default.css"; // optional: the default theme
6570
6598
  Every other prop passes through to `<lu-toggle-group>`: `aria-label`, `aria-labelledby`, `id`,
6571
6599
  `class`. Name the group with one of the first two.
6572
6600
 
6601
+ With `value`, the group is controlled: after the user presses or releases a toggle, the toggles
6602
+ show `value` again, so a press shows only once `onValueChange` has updated `value` with it. One
6603
+ that leaves `value` as it was keeps the toggles as they were: an `onValueChange` that ignores an
6604
+ empty array keeps one toggle pressed. Setting `value` to the toggles already pressed does nothing.
6605
+
6573
6606
  Each toggle's `value` is its identity in the group; its `onPressedChange` still fires. A
6574
6607
  `pressedchange` event (`detail.pressed`) bubbles from a toggle before it changes, and
6575
6608
  `event.preventDefault()` cancels the change.
@@ -6580,7 +6613,8 @@ Each toggle's `value` is its identity in the group; its `onPressedChange` still
6580
6613
  ## Example
6581
6614
 
6582
6615
  ```tsx check
6583
- import { Toggle, ToggleGroup } from "@lumiose/ui";
6616
+ import { Toggle } from "@lumiose/ui/toggle";
6617
+ import { ToggleGroup } from "@lumiose/ui/toggle-group";
6584
6618
  import { signal } from "lumiose";
6585
6619
 
6586
6620
  export function TextAlignment() {
@@ -6633,7 +6667,7 @@ and single tab stop take over: the toggles are toolbar items.
6633
6667
  ## Without JavaScript, and the element's script
6634
6668
 
6635
6669
  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
6670
+ (1.0 kB brotli) presses the toggles and moves focus, on server pages and in islands; when the tab
6637
6671
  stop's toggle goes or is disabled, or a disabled group is enabled, the first pressed (else enabled)
6638
6672
  toggle takes the tab stop. With no script
6639
6673
  at all the toggles show their state but do not change. For values a form submits, use a
@@ -6653,7 +6687,7 @@ Custom elements and `aria-pressed`: every supported browser.
6653
6687
 
6654
6688
  # Toolbar
6655
6689
 
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.
6690
+ > 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
6691
 
6658
6692
  ```ts
6659
6693
  import { Toolbar } from "@lumiose/ui"; // or "@lumiose/ui/toolbar"
@@ -6775,7 +6809,7 @@ its text selected, as in Base UI. Base UI advises at most one input, last.
6775
6809
  ## Without JavaScript, and the element's script
6776
6810
 
6777
6811
  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)
6812
+ without JavaScript or before the script runs. `<lu-toolbar>`'s script (1.2 kB brotli with Button's)
6779
6813
  makes the toolbar one tab stop, moves focus with the keys and keeps the tab stop on the last focused
6780
6814
  item.
6781
6815
 
@@ -6794,7 +6828,7 @@ Custom elements and `role="toolbar"`: every supported browser and screen reader.
6794
6828
 
6795
6829
  # Tooltip
6796
6830
 
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.
6831
+ > 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
6832
 
6799
6833
  ```ts
6800
6834
  import { Tooltip } from "@lumiose/ui"; // or "@lumiose/ui/tooltip"
@@ -6905,8 +6939,9 @@ A tooltip inside a popover keeps the popover open, and one inside a dialog belon
6905
6939
  ## Without JavaScript, and the client script
6906
6940
 
6907
6941
  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
6942
+ announce it. A page that renders a `Tooltip` loads its script once, with the placement module it
6943
+ shares with Popover and Menu: 2.1 kB (brotli) without the Lumiose runtime; an island loads the
6944
+ same modules. It opens and closes every tooltip on the page, and places them
6910
6945
  in browsers without CSS anchor positioning.
6911
6946
 
6912
6947
  ## Browser support