@aceshooting/lyra-ui 7.0.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +12 -4
  3. package/custom-elements.json +146 -8
  4. package/dist/components/forms/button/button.class.d.ts +10 -4
  5. package/dist/components/forms/button/button.class.d.ts.map +1 -1
  6. package/dist/components/forms/button/button.class.js +21 -5
  7. package/dist/components/forms/button/button.class.js.map +1 -1
  8. package/dist/components/forms/checkbox/checkbox.class.d.ts +5 -3
  9. package/dist/components/forms/checkbox/checkbox.class.d.ts.map +1 -1
  10. package/dist/components/forms/checkbox/checkbox.class.js +13 -3
  11. package/dist/components/forms/checkbox/checkbox.class.js.map +1 -1
  12. package/dist/components/forms/icon-button/icon-button.class.d.ts +11 -4
  13. package/dist/components/forms/icon-button/icon-button.class.d.ts.map +1 -1
  14. package/dist/components/forms/icon-button/icon-button.class.js +21 -5
  15. package/dist/components/forms/icon-button/icon-button.class.js.map +1 -1
  16. package/dist/components/forms/icon-button/icon-button.styles.js +1 -1
  17. package/dist/components/layout/menu/menu.class.d.ts +4 -1
  18. package/dist/components/layout/menu/menu.class.d.ts.map +1 -1
  19. package/dist/components/layout/menu/menu.class.js +4 -1
  20. package/dist/components/layout/menu/menu.class.js.map +1 -1
  21. package/dist/components/media/map/map-loader.d.ts +2 -2
  22. package/dist/components/media/map/map-loader.js +2 -2
  23. package/dist/components/media/map/map.class.d.ts +2 -2
  24. package/dist/components/media/map/map.class.js +2 -2
  25. package/dist/components/overlays/overlay/popover.class.d.ts +7 -0
  26. package/dist/components/overlays/overlay/popover.class.d.ts.map +1 -1
  27. package/dist/components/overlays/overlay/popover.class.js +13 -2
  28. package/dist/components/overlays/overlay/popover.class.js.map +1 -1
  29. package/dist/components/overlays/overlay/tooltip.class.d.ts +9 -0
  30. package/dist/components/overlays/overlay/tooltip.class.d.ts.map +1 -1
  31. package/dist/components/overlays/overlay/tooltip.class.js +43 -3
  32. package/dist/components/overlays/overlay/tooltip.class.js.map +1 -1
  33. package/dist/internal/aria-controls.d.ts +11 -2
  34. package/dist/internal/aria-controls.d.ts.map +1 -1
  35. package/dist/internal/aria-controls.js +35 -11
  36. package/dist/internal/aria-controls.js.map +1 -1
  37. package/dist/localization.d.ts +9 -0
  38. package/dist/localization.d.ts.map +1 -0
  39. package/dist/localization.js +8 -0
  40. package/dist/localization.js.map +1 -0
  41. package/dist/lyra.d.ts +2 -2
  42. package/dist/lyra.d.ts.map +1 -1
  43. package/dist/lyra.js +1 -1
  44. package/dist/lyra.js.map +1 -1
  45. package/llms/components/lr-button.md +7 -4
  46. package/llms/components/lr-checkbox.md +6 -3
  47. package/llms/components/lr-dropdown-item.md +10 -1
  48. package/llms/components/lr-dropdown.md +2 -0
  49. package/llms/components/lr-geojson-view.md +4 -3
  50. package/llms/components/lr-icon-button.md +11 -6
  51. package/llms/components/lr-map.md +5 -4
  52. package/llms/components/lr-menu-item.md +10 -1
  53. package/llms/components/lr-menu.md +10 -1
  54. package/llms/components/lr-popover.md +7 -0
  55. package/llms/components/lr-tooltip.md +8 -0
  56. package/llms/peers.md +1 -1
  57. package/llms/shared.md +15 -8
  58. package/llms-full.txt +74 -28
  59. package/package.json +4 -3
  60. package/vscode-css-data.json +7 -0
  61. package/vscode-html-data.json +21 -7
  62. package/web-types.json +20 -8
@@ -1829,7 +1829,7 @@
1829
1829
  "name": "lr-button",
1830
1830
  "description": {
1831
1831
  "kind": "markdown",
1832
- "value": "`<lr-button>` — a generic action-button primitive. Renders an internal native\n`<button part=\"base\">`. `type=\"submit\"`/`type=\"reset\"`\nare handled by this component itself via the host's own `closest('form')` — a shadow-internal\nnative `<button type=\"submit\">` does not participate in an ancestor light-DOM form's submission\non its own, since form-submitter semantics don't cross the shadow boundary.\n\nWhen `href` is set to a safe link URL (`http:`/`https:`/`blob:`/`mailto:`/relative — see\n`safeLinkHref`, or `safeDownloadHref` which drops `mailto:` when `download` is set) the root\nrenders as a real `<a part=\"base\" href=…>` instead — for a link styled\nas a button (e.g. a CTA). Native navigation is then the anchor's own activation, so the\nsubmit/reset click handler and `type` (submit/reset) have no effect in that mode. When the\nbutton is disabled (its own `disabled` or an ancestor `<fieldset disabled>`) the anchor renders\nwith `aria-disabled=\"true\"` and **no `href`** — an href-less anchor is not focusable or\nnavigable, so a disabled link button genuinely cannot be activated (unlike a bare\n`aria-disabled` on a still-navigable link). An unsafe/unparseable `href` falls back to the\nnative `<button>`.\n\n`accessibleLabel` (attribute `aria-label`) is forwarded reactively to the internal button/anchor\nas a literal string (for an icon-only button with no visible label); external\n`aria-labelledby`/`aria-describedby` idrefs are not copied across the shadow boundary.\nHost `aria-haspopup` and `aria-expanded` values are likewise forwarded to the internal semantic\ncontrol. When host `aria-controls` names elements in the host's own root, the controls\nrelationship is resolved onto the internal control through the reflected element-reference API\nso it remains valid across this component's shadow boundary; browsers without that API retain\nthe forwarded string attribute as a best-effort fallback.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Default slot: the button's label content. |\n| `end` | Trailing icon/content, rendered after the label. |\n| `start` | Leading icon/content, rendered before the label. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The internal native `<button>` (or an `<a>` when `href` resolves to a safe link). |\n| `end` | The `end` slot wrapper. |\n| `label` | The default-slot label wrapper. |\n| `spinner` | The loading spinner, present only while `loading` is `true`. |\n| `start` | The `start` slot wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-button-width` (default: `100%`) — Inline size of the internal button. The host defaults it to `100%` so the native button follows the host's own width; override to `auto` (or any other value) for a compact inline composition.\n- `--lr-button-hover-brightness` (default: `1.08`) — `filter: brightness()` multiplier applied while hovering a non-disabled button.\n- `--lr-button-active-scale` (default: `0.9875`) — `transform: scale()` factor applied while a non-disabled button is pressed.\n- `--lr-button-spinner-duration` (default: `var(--lr-transition-ambient)`) — Timing of the `loading` spinner.\n- `--lr-button-accent` (default: `var(--lr-color-text)`) — Text/glyph color for the chrome-less appearances (`outlined`, `plain`, `link`). Swapped per `variant` to that variant's semantic color.\n- `--lr-button-fill` (default: `var(--lr-color-surface)`) — Background of `appearance=\"filled\"`. Swapped per `variant` to that variant's semantic color.\n- `--lr-button-on-fill` (default: `var(--lr-color-text)`) — Text color on top of `--lr-button-fill`. Swapped per `variant` to that variant's `on-*` color.\n- `--lr-button-accent-fill` (default: `var(--lr-color-neutral)`) — Background of `appearance=\"accent\"` (and its border color). Swapped per `variant` to that variant's semantic color.\n- `--lr-button-accent-on-fill` (default: `var(--lr-color-on-neutral)`) — Text color on top of `--lr-button-accent-fill`. Swapped per `variant` to that variant's `on-*` color.\n- `--lr-button-border` (default: `var(--lr-color-border)`) — Border color of the internal button. Swapped per `variant` to that variant's semantic color.\n- `--lr-button-outlined-border` (default: `var(--lr-color-border-strong)`) — Border color of `appearance=\"outlined\"`, which overrides `--lr-button-border`.\n- `--lr-button-outlined-fill` (default: `transparent`) — Background of `appearance=\"outlined\"`. Transparent by default; set it to tint the button (e.g. a faint surface wash behind the outline) without a `::part(base)` rule. Like `--lr-button-quiet-*`, it is deliberately *not* swapped per `variant`. Note that the `:hover` `filter: brightness()` applies to whatever fill is set, so a tinted outlined button now visibly brightens on hover where a transparent one did not.\n- `--lr-button-quiet-border` (default: `var(--lr-color-border)`) — Border color of `appearance=\"quiet\"`.\n- `--lr-button-quiet-text` (default: `var(--lr-color-text-quiet)`) — Text color of `appearance=\"quiet\"`.\n- `--lr-button-size-2xs` (default: `var(--lr-size-1-25rem)`) — `min-block-size` at `size=\"2xs\"`.\n- `--lr-button-size-xs` (default: `var(--lr-size-1-5rem)`) — `min-block-size` at `size=\"xs\"`.\n- `--lr-button-size-s` (default: `var(--lr-size-1-875rem)`) — `min-block-size` at `size=\"s\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"s\"` control height.\n- `--lr-button-size-m` (default: `var(--lr-size-2-5rem)`) — `min-block-size` at `size=\"m\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own default control height, so a default-size button sitting next to a default-size input/select/combobox in the same row lines up.\n- `--lr-button-size-l` (default: `var(--lr-size-3rem)`) — `min-block-size` at `size=\"l\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"l\"` control height.\n- `--lr-button-size-xl` (default: `var(--lr-size-3-5rem)`) — `min-block-size` at `size=\"xl\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"xl\"` control height.\n- `--lr-button-padding-block` (default: `var(--lr-space-xs)`) — Block padding of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). Override it to retune a tier without a `::part(base)` rule; `appearance=\"link\"` ignores it (it renders with zero padding).\n- `--lr-button-padding-inline` (default: `var(--lr-space-m)`) — Inline padding of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). `appearance=\"link\"` ignores it.\n- `--lr-button-font-size` (default: `var(--lr-font-size-m)`) — Font size of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). `appearance=\"link\"` ignores it and inherits the ambient font instead.\n- `--lr-button-min-height` (default: `var(--lr-button-size-m)`) — The active tier's `min-block-size` floor. Re-assigned per `size` tier to that tier's own `--lr-button-size-*` token, and used as the fallback when `--lr-button-height` is unset.\n- `--lr-button-height` — Exact height of the internal button. **Undeclared by default** — so the button keeps the active tier's `min-block-size` floor and an `auto` height, exactly as before. Set it (e.g. to pin the button to a fixed toolbar row) to both floor *and* cap the height. Never declare it as `auto`: a declared value wins over the `var()` fallback arm and would make every tier's floor dead code. `appearance=\"link\"` ignores it.\n- `--lr-button-gap` (default: `var(--lr-space-2xs)`) — Gap between the icon/label and any slotted content in the internal button. Unlike the size knobs above it does not vary by `size` tier. Override it to retune without a `::part(base)` rule.\n- `--lr-button-radius` (default: `var(--lr-radius)`) — Corner radius of the internal button. Does not vary by `size` tier. `appearance=\"link\"` ignores it (it renders with zero radius).\n- `--lr-button-shadow` — Box shadow of the internal button. **Undeclared by default**, so `box-shadow` falls back to `none` — byte-identical to before this property existed. Set it (e.g. an elevated/floating action button) without a `::part(base)` rule."
1832
+ "value": "`<lr-button>` — a generic action-button primitive. Renders an internal native\n`<button part=\"base\">`. `type=\"submit\"`/`type=\"reset\"`\nare handled by this component itself via the host's own `closest('form')` — a shadow-internal\nnative `<button type=\"submit\">` does not participate in an ancestor light-DOM form's submission\non its own, since form-submitter semantics don't cross the shadow boundary.\n\nWhen `href` is set to a safe link URL (`http:`/`https:`/`blob:`/`mailto:`/relative — see\n`safeLinkHref`, or `safeDownloadHref` which drops `mailto:` when `download` is set) the root\nrenders as a real `<a part=\"base\" href=…>` instead — for a link styled\nas a button (e.g. a CTA). Native navigation is then the anchor's own activation, so the\nsubmit/reset click handler and `type` (submit/reset) have no effect in that mode. When the\nbutton is disabled (its own `disabled` or an ancestor `<fieldset disabled>`) the anchor renders\nwith `aria-disabled=\"true\"` and **no `href`** — an href-less anchor is not focusable or\nnavigable, so a disabled link button genuinely cannot be activated (unlike a bare\n`aria-disabled` on a still-navigable link). An unsafe/unparseable `href` falls back to the\nnative `<button>`.\n\n`accessibleLabel` (attribute `aria-label`) is forwarded reactively to the internal button/anchor\nas a literal string (for an icon-only button with no visible label). Host `aria-describedby`\nIDREFs are resolved through `ariaDescribedByElements`; external `aria-labelledby` is not copied\nacross the shadow boundary.\nHost `aria-haspopup` and `aria-expanded` values are likewise forwarded to the internal semantic\ncontrol. When host `aria-controls` names elements in the host's own root, the controls\nrelationship is resolved onto the internal control through the reflected element-reference API\nso it remains valid across this component's shadow boundary. Assigning that relationship\nintentionally clears the serialized `aria-controls` value; read `ariaControlsElements` in a\nsupporting browser. Browsers without that API retain the forwarded string attribute as a\nbest-effort fallback.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Default slot: the button's label content. |\n| `end` | Trailing icon/content, rendered after the label. |\n| `start` | Leading icon/content, rendered before the label. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The internal native `<button>` (or an `<a>` when `href` resolves to a safe link). |\n| `end` | The `end` slot wrapper. |\n| `label` | The default-slot label wrapper. |\n| `spinner` | The loading spinner, present only while `loading` is `true`. |\n| `start` | The `start` slot wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-button-width` (default: `100%`) — Inline size of the internal button. The host defaults it to `100%` so the native button follows the host's own width; override to `auto` (or any other value) for a compact inline composition.\n- `--lr-button-hover-brightness` (default: `1.08`) — `filter: brightness()` multiplier applied while hovering a non-disabled button.\n- `--lr-button-active-scale` (default: `0.9875`) — `transform: scale()` factor applied while a non-disabled button is pressed.\n- `--lr-button-spinner-duration` (default: `var(--lr-transition-ambient)`) — Timing of the `loading` spinner.\n- `--lr-button-accent` (default: `var(--lr-color-text)`) — Text/glyph color for the chrome-less appearances (`outlined`, `plain`, `link`). Swapped per `variant` to that variant's semantic color.\n- `--lr-button-fill` (default: `var(--lr-color-surface)`) — Background of `appearance=\"filled\"`. Swapped per `variant` to that variant's semantic color.\n- `--lr-button-on-fill` (default: `var(--lr-color-text)`) — Text color on top of `--lr-button-fill`. Swapped per `variant` to that variant's `on-*` color.\n- `--lr-button-accent-fill` (default: `var(--lr-color-neutral)`) — Background of `appearance=\"accent\"` (and its border color). Swapped per `variant` to that variant's semantic color.\n- `--lr-button-accent-on-fill` (default: `var(--lr-color-on-neutral)`) — Text color on top of `--lr-button-accent-fill`. Swapped per `variant` to that variant's `on-*` color.\n- `--lr-button-border` (default: `var(--lr-color-border)`) — Border color of the internal button. Swapped per `variant` to that variant's semantic color.\n- `--lr-button-outlined-border` (default: `var(--lr-color-border-strong)`) — Border color of `appearance=\"outlined\"`, which overrides `--lr-button-border`.\n- `--lr-button-outlined-fill` (default: `transparent`) — Background of `appearance=\"outlined\"`. Transparent by default; set it to tint the button (e.g. a faint surface wash behind the outline) without a `::part(base)` rule. Like `--lr-button-quiet-*`, it is deliberately *not* swapped per `variant`. Note that the `:hover` `filter: brightness()` applies to whatever fill is set, so a tinted outlined button now visibly brightens on hover where a transparent one did not.\n- `--lr-button-quiet-border` (default: `var(--lr-color-border)`) — Border color of `appearance=\"quiet\"`.\n- `--lr-button-quiet-text` (default: `var(--lr-color-text-quiet)`) — Text color of `appearance=\"quiet\"`.\n- `--lr-button-size-2xs` (default: `var(--lr-size-1-25rem)`) — `min-block-size` at `size=\"2xs\"`.\n- `--lr-button-size-xs` (default: `var(--lr-size-1-5rem)`) — `min-block-size` at `size=\"xs\"`.\n- `--lr-button-size-s` (default: `var(--lr-size-1-875rem)`) — `min-block-size` at `size=\"s\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"s\"` control height.\n- `--lr-button-size-m` (default: `var(--lr-size-2-5rem)`) — `min-block-size` at `size=\"m\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own default control height, so a default-size button sitting next to a default-size input/select/combobox in the same row lines up.\n- `--lr-button-size-l` (default: `var(--lr-size-3rem)`) — `min-block-size` at `size=\"l\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"l\"` control height.\n- `--lr-button-size-xl` (default: `var(--lr-size-3-5rem)`) — `min-block-size` at `size=\"xl\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"xl\"` control height.\n- `--lr-button-padding-block` (default: `var(--lr-space-xs)`) — Block padding of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). Override it to retune a tier without a `::part(base)` rule; `appearance=\"link\"` ignores it (it renders with zero padding).\n- `--lr-button-padding-inline` (default: `var(--lr-space-m)`) — Inline padding of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). `appearance=\"link\"` ignores it.\n- `--lr-button-font-size` (default: `var(--lr-font-size-m)`) — Font size of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). `appearance=\"link\"` ignores it and inherits the ambient font instead.\n- `--lr-button-min-height` (default: `var(--lr-button-size-m)`) — The active tier's `min-block-size` floor. Re-assigned per `size` tier to that tier's own `--lr-button-size-*` token, and used as the fallback when `--lr-button-height` is unset.\n- `--lr-button-height` — Exact height of the internal button. **Undeclared by default** — so the button keeps the active tier's `min-block-size` floor and an `auto` height, exactly as before. Set it (e.g. to pin the button to a fixed toolbar row) to both floor *and* cap the height. Never declare it as `auto`: a declared value wins over the `var()` fallback arm and would make every tier's floor dead code. `appearance=\"link\"` ignores it.\n- `--lr-button-gap` (default: `var(--lr-space-2xs)`) — Gap between the icon/label and any slotted content in the internal button. Unlike the size knobs above it does not vary by `size` tier. Override it to retune without a `::part(base)` rule.\n- `--lr-button-radius` (default: `var(--lr-radius)`) — Corner radius of the internal button. Does not vary by `size` tier. `appearance=\"link\"` ignores it (it renders with zero radius).\n- `--lr-button-shadow` — Box shadow of the internal button. **Undeclared by default**, so `box-shadow` falls back to `none` — byte-identical to before this property existed. Set it (e.g. an elevated/floating action button) without a `::part(base)` rule."
1833
1833
  },
1834
1834
  "attributes": [
1835
1835
  {
@@ -1846,6 +1846,13 @@
1846
1846
  "value": "Type: `string | null` \nDefault: `null`"
1847
1847
  }
1848
1848
  },
1849
+ {
1850
+ "name": "aria-describedby",
1851
+ "description": {
1852
+ "kind": "markdown",
1853
+ "value": "Type: `string | null` \nDefault: `null`"
1854
+ }
1855
+ },
1849
1856
  {
1850
1857
  "name": "aria-expanded",
1851
1858
  "description": {
@@ -2655,7 +2662,7 @@
2655
2662
  "name": "lr-checkbox",
2656
2663
  "description": {
2657
2664
  "kind": "markdown",
2658
- "value": "`<lr-checkbox>` — a boolean form control. Structurally the same idea as\n`<lr-switch>` (form-associated via `ElementInternals`, click and\nSpace both toggle) but with checkbox semantics: `role=\"checkbox\"` +\nan `aria-checked` that can also be `\"mixed\"`, and a visual box/checkmark\ninstead of a track/thumb.\n\n`checked` is not a plain string, so this attaches `ElementInternals`\ndirectly and implements its own `updateValidity()` rather than using the\n`FormAssociated` mixin — see `<lr-combobox>` for the same\ndirect-`ElementInternals` shape with a non-string value.\n\nDeliberately no hint/error chrome of its own -- the default slot already carries real, visible\nlabel text (see `@slot` below), so a labeled-field frame built around `label`/`hint`/`errorText`\nprops has nothing to add here. A consumer needing hint/error messaging composes it in their own\nwrapper (e.g. `<lr-tool-param-form>` folds a boolean field's validation error into adjacent\ndescription text / `aria-label` rather than a `<lr-checkbox>`-owned slot), the same way a\nnative `<input type=\"checkbox\">` plus `<label>` pairs with an externally-owned error node.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Label text, rendered next to the box. Clicking it toggles the checkbox, the same as clicking a native checkbox's associated `<label>`. If left empty, set `aria-label` on the host so the control still has an accessible name. A host `aria-describedby` attribute is forwarded to the internal `role=\"checkbox\"` so externally-owned descriptions can be associated with this individual control. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The whole interactive control (`role=\"checkbox\"`); wraps the box and label. |\n| `box` | The small square that shows the checkmark/indeterminate dash. |\n| `checkmark` | The checkmark (or indeterminate dash) glyph inside the box. |\n| `label` | The wrapper around the default slot. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-checkbox-label-indent` (default: `calc(min(var(--lr-icon-button-size), 1.75rem) + var(--lr-space-s))`) — The inline distance from the control's start edge to the start of the label text, i.e. the box's own floor plus the gap next to it. Published so a consumer composing per-option hint text under the label can align it without re-deriving that formula from the shadow styles, and used as the source of the real gap so the two cannot drift. Setting it on the element (or on `lr-checkbox` in your own stylesheet) moves the label; because custom properties inherit down and not sideways, it is *not* readable from a sibling node in your tree — align a sibling by computing the same formula from `--lr-theme-icon-button-size` and `--lr-theme-space-s`, which you control.\n- `--lr-checkbox-checked-bg` (default: `var(--lr-color-brand)`) — Background of `[part='box']` while `checked` or `indeterminate`. Retint just this control's checked fill without touching the shared `--lr-color-brand` token every other component also reads.\n- `--lr-checkbox-checked-border` (default: `var(--lr-color-brand)`) — Border color of `[part='box']` while `checked` or `indeterminate`."
2665
+ "value": "`<lr-checkbox>` — a boolean form control. Structurally the same idea as\n`<lr-switch>` (form-associated via `ElementInternals`, click and\nSpace both toggle) but with checkbox semantics: `role=\"checkbox\"` +\nan `aria-checked` that can also be `\"mixed\"`, and a visual box/checkmark\ninstead of a track/thumb.\n\n`checked` is not a plain string, so this attaches `ElementInternals`\ndirectly and implements its own `updateValidity()` rather than using the\n`FormAssociated` mixin — see `<lr-combobox>` for the same\ndirect-`ElementInternals` shape with a non-string value.\n\nDeliberately no hint/error chrome of its own -- the default slot already carries real, visible\nlabel text (see `@slot` below), so a labeled-field frame built around `label`/`hint`/`errorText`\nprops has nothing to add here. A consumer needing hint/error messaging composes it in their own\nwrapper (e.g. `<lr-tool-param-form>` folds a boolean field's validation error into adjacent\ndescription text / `aria-label` rather than a `<lr-checkbox>`-owned slot), the same way a\nnative `<input type=\"checkbox\">` plus `<label>` pairs with an externally-owned error node.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Label text, rendered next to the box. Clicking it toggles the checkbox, the same as clicking a native checkbox's associated `<label>`. If left empty, set `aria-label` on the host so the control still has an accessible name. A host `aria-describedby` attribute is resolved onto the internal `role=\"checkbox\"` through `ariaDescribedByElements` so externally-owned descriptions remain valid across the shadow boundary. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The whole interactive control (`role=\"checkbox\"`); wraps the box and label. |\n| `box` | The small square that shows the checkmark/indeterminate dash. |\n| `checkmark` | The checkmark (or indeterminate dash) glyph inside the box. |\n| `label` | The wrapper around the default slot. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-checkbox-label-indent` (default: `calc(min(var(--lr-icon-button-size), 1.75rem) + var(--lr-space-s))`) — The inline distance from the control's start edge to the start of the label text, i.e. the box's own floor plus the gap next to it. Published so a consumer composing per-option hint text under the label can align it without re-deriving that formula from the shadow styles, and used as the source of the real gap so the two cannot drift. Setting it on the element (or on `lr-checkbox` in your own stylesheet) moves the label; because custom properties inherit down and not sideways, it is *not* readable from a sibling node in your tree — align a sibling by computing the same formula from `--lr-theme-icon-button-size` and `--lr-theme-space-s`, which you control.\n- `--lr-checkbox-checked-bg` (default: `var(--lr-color-brand)`) — Background of `[part='box']` while `checked` or `indeterminate`. Retint just this control's checked fill without touching the shared `--lr-color-brand` token every other component also reads.\n- `--lr-checkbox-checked-border` (default: `var(--lr-color-brand)`) — Border color of `[part='box']` while `checked` or `indeterminate`."
2659
2666
  },
2660
2667
  "attributes": [
2661
2668
  {
@@ -7669,7 +7676,7 @@
7669
7676
  "name": "lr-icon-button",
7670
7677
  "description": {
7671
7678
  "kind": "markdown",
7672
- "value": "`<lr-icon-button>` — an accessible icon-only action button.\n\nSet `icon` for one of `<lr-icon>`'s named glyphs, or slot your own content instead. Slotted\ncontent is a **sibling** of the built-in glyph rather than being piped through `<lr-icon>`, so\nany complete element — an `<svg>`, an `<img>`, an `<lr-flag>` — renders at its own natural\naspect ratio instead of being forced into a 1:1 box. Bare SVG geometry (`<path>`, `<circle>`,\nand a handful of other raw primitives) with no enclosing `<svg>` of its own is detected and\ncloned into a real, internal SVG-namespaced element so it still paints — the same fallback\n`<lr-icon>`'s own custom-content slot uses, but narrowly scoped so a custom element (e.g. a\nslotted `<lr-flag>`) is never run through it.\n\nHost `aria-haspopup` and `aria-expanded` values are forwarded reactively to the shadow-internal\nnative button. When host `aria-controls` names elements in the host's own root, the controls\nrelationship is resolved onto that focused control through the reflected element-reference API\nso it remains valid across this component's shadow boundary; browsers without that API retain\nthe forwarded string attribute as a best-effort fallback.\n\nForm-associated (mirroring `<lr-button>`'s identical shape): discoverable through\n`form.elements`, and `type=\"submit\"`/`type=\"reset\"` are handled by this component itself via\nthe host's own `closest('form')` — a shadow-internal native `<button type=\"submit\">` does not\nparticipate in an ancestor light-DOM form's submission on its own, since form-submitter\nsemantics don't cross the shadow boundary.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Optional custom icon content, rendered beside (not inside) the `icon` glyph. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `button` | Native button. |\n| `fallback` | The internal SVG-namespaced clone target for slotted bare geometry. Carries the same `fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"` defaults `<lr-icon>`'s own wrapper svg does, so bare stroke-style geometry (no fill/stroke of its own) renders outlined instead of as a solid shape; an explicit `fill`/`stroke`/etc. already present on the slotted node still wins for that node. Only present in the DOM while at least one top-level slotted element needs it; a complete `<svg>`, `<img>`, or custom element never mounts it. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-icon-button-size` (default: `2.5rem`) — Minimum tappable inline and block size of the native button — a **floor**, not a fixed size: content larger than it grows the button and keeps its own aspect ratio, while a small glyph pads out to it. A library-wide token (declared on `:root` by `tokens.styles.ts`, and the shared minimum tappable size several other components size their icon controls against), so overriding it globally resizes all of them together.\n- `--lr-icon-button-radius` (default: `var(--lr-radius)`) — Corner radius of the native button.\n- `--lr-icon-button-background` (default: `transparent`) — Background fill of the native button.\n- `--lr-icon-button-background-hover` (default: `var(--lr-color-surface)`) — Background fill on hover.\n- `--lr-icon-button-color` (default: `inherit`) — Icon/text color of the native button.\n- `--lr-icon-button-color-hover` (default: `var(--lr-icon-button-color, inherit)`) — Icon/text color on hover.\n- `--lr-icon-button-border` (default: `0`) — Complete border shorthand of the native button."
7679
+ "value": "`<lr-icon-button>` — an accessible icon-only action button.\n\nSet `icon` for one of `<lr-icon>`'s named glyphs, or slot your own content instead. Slotted\ncontent is a **sibling** of the built-in glyph rather than being piped through `<lr-icon>`, so\nany complete element — an `<svg>`, an `<img>`, an `<lr-flag>` — renders at its own natural\naspect ratio instead of being forced into a 1:1 box. Bare SVG geometry (`<path>`, `<circle>`,\nand a handful of other raw primitives) with no enclosing `<svg>` of its own is detected and\ncloned into a real, internal SVG-namespaced element so it still paints — the same fallback\n`<lr-icon>`'s own custom-content slot uses, but narrowly scoped so a custom element (e.g. a\nslotted `<lr-flag>`) is never run through it.\n\nHost `aria-haspopup` and `aria-expanded` values are forwarded reactively to the shadow-internal\nnative button. Host `aria-describedby` IDREFs are resolved through `ariaDescribedByElements`.\nWhen host `aria-controls` names elements in the host's own root, the controls relationship is\nresolved onto that focused control through the reflected element-reference API so it remains\nvalid across this component's shadow boundary. Assigning that relationship intentionally clears\nthe serialized `aria-controls` value; read `ariaControlsElements` in a supporting browser.\nBrowsers without that API retain the forwarded string attribute as a best-effort fallback.\n\nForm-associated (mirroring `<lr-button>`'s identical shape): discoverable through\n`form.elements`, and `type=\"submit\"`/`type=\"reset\"` are handled by this component itself via\nthe host's own `closest('form')` — a shadow-internal native `<button type=\"submit\">` does not\nparticipate in an ancestor light-DOM form's submission on its own, since form-submitter\nsemantics don't cross the shadow boundary.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Optional custom icon content, rendered beside (not inside) the `icon` glyph. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `button` | Native button. |\n| `fallback` | The internal SVG-namespaced clone target for slotted bare geometry. Carries the same `fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"` defaults `<lr-icon>`'s own wrapper svg does, so bare stroke-style geometry (no fill/stroke of its own) renders outlined instead of as a solid shape; an explicit `fill`/`stroke`/etc. already present on the slotted node still wins for that node. Only present in the DOM while at least one top-level slotted element needs it; a complete `<svg>`, `<img>`, or custom element never mounts it. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-icon-button-size` (default: `2.5rem`) — Minimum tappable inline and block size of the native button — a **floor**, not a fixed size: content larger than it grows the button and keeps its own aspect ratio, while a small glyph pads out to it. A library-wide token (declared on `:root` by `tokens.styles.ts`, and the shared minimum tappable size several other components size their icon controls against), so overriding it globally resizes all of them together.\n- `--lr-icon-button-radius` (default: `var(--lr-radius)`) — Corner radius of the native button.\n- `--lr-icon-button-background` (default: `transparent`) — Background fill of the native button.\n- `--lr-icon-button-background-hover` (default: `var(--lr-color-surface)`) — Background fill on hover.\n- `--lr-icon-button-color` (default: `inherit`) — Icon/text color of the native button.\n- `--lr-icon-button-color-hover` (default: `var(--lr-icon-button-color, inherit)`) — Icon/text color on hover.\n- `--lr-icon-button-border` (default: `0`) — Complete border shorthand of the native button.\n- `--lr-icon-button-border-hover` (default: `var(--lr-icon-button-border, 0)`) — Complete border shorthand on hover."
7673
7680
  },
7674
7681
  "attributes": [
7675
7682
  {
@@ -7679,6 +7686,13 @@
7679
7686
  "value": "Type: `string | null` \nDefault: `null`"
7680
7687
  }
7681
7688
  },
7689
+ {
7690
+ "name": "aria-describedby",
7691
+ "description": {
7692
+ "kind": "markdown",
7693
+ "value": "Type: `string | null` \nDefault: `null`"
7694
+ }
7695
+ },
7682
7696
  {
7683
7697
  "name": "aria-expanded",
7684
7698
  "description": {
@@ -8962,7 +8976,7 @@
8962
8976
  "name": "lr-map",
8963
8977
  "description": {
8964
8978
  "kind": "markdown",
8965
- "value": "`<lr-map>` — a maplibre-gl wrapper with a declarative legend, choropleth\nGeoJSON layer, markers, and additive `dataLayers` GeoJSON overlays\n(arbitrary shapes rendered as a source plus fill/line/circle layers,\nindependent of `choropleth`'s field/stops color-interpolation), plus a raw\n`map` escape hatch. Requires the optional peer dep `maplibre-gl`\n(consumers also import its CSS and configure its module-worker URL once).\nMapLibre v6 is ESM-only and requires WebGL2.\n\nThe underlying `maplibregl.Map` — and the WebGL context it opens — isn't\nconstructed until this element is first visible in the viewport (tracked\nvia `IntersectionObserver`), even once the `maplibre-gl` peer dependency\nhas finished loading. Browsers hard-cap concurrent WebGL contexts per\npage, so a grid/dashboard of many `<lr-map>` instances only constructs\nthe ones actually on-screen instead of racing to exhaust that budget the\ninstant each one mounts. `map` stays `undefined` (and `lr-map-load`\ndoesn't fire) until construction actually happens.\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The map wrapper. |\n| `container` | The maplibre container. |\n| `error` | `role=\"alert\"` message shown instead of `container` if the optional `maplibre-gl` peer dependency fails to load (e.g. not installed). ⚠️ The default `mapStyle` (when unset) uses OpenStreetMap's demo tile server, which is not suitable for production traffic — see the `DEFAULT_STYLE` doc comment above. Always pass an explicit `mapStyle` in production. |\n| `legend` | The map legend. |\n| `legend-swatch` | A legend color swatch. |"
8979
+ "value": "`<lr-map>` — a maplibre-gl wrapper with a declarative legend, choropleth\nGeoJSON layer, markers, and additive `dataLayers` GeoJSON overlays\n(arbitrary shapes rendered as a source plus fill/line/circle layers,\nindependent of `choropleth`'s field/stops color-interpolation), plus a raw\n`map` escape hatch. Requires the optional peer dep `maplibre-gl`\nv5 or v6 (consumers also import its CSS). MapLibre v6 is ESM-only, requires WebGL2, and needs\nits module-worker URL configured once; v5's standard build includes its worker.\n\nThe underlying `maplibregl.Map` — and the WebGL context it opens — isn't\nconstructed until this element is first visible in the viewport (tracked\nvia `IntersectionObserver`), even once the `maplibre-gl` peer dependency\nhas finished loading. Browsers hard-cap concurrent WebGL contexts per\npage, so a grid/dashboard of many `<lr-map>` instances only constructs\nthe ones actually on-screen instead of racing to exhaust that budget the\ninstant each one mounts. `map` stays `undefined` (and `lr-map-load`\ndoesn't fire) until construction actually happens.\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The map wrapper. |\n| `container` | The maplibre container. |\n| `error` | `role=\"alert\"` message shown instead of `container` if the optional `maplibre-gl` peer dependency fails to load (e.g. not installed). ⚠️ The default `mapStyle` (when unset) uses OpenStreetMap's demo tile server, which is not suitable for production traffic — see the `DEFAULT_STYLE` doc comment above. Always pass an explicit `mapStyle` in production. |\n| `legend` | The map legend. |\n| `legend-swatch` | A legend color swatch. |"
8966
8980
  },
8967
8981
  "attributes": [
8968
8982
  {
@@ -9389,7 +9403,7 @@
9389
9403
  "name": "lr-menu",
9390
9404
  "description": {
9391
9405
  "kind": "markdown",
9392
- "value": "`<lr-menu>` — an anchored dropdown of `<lr-menu-item>` actions, opened\nfrom a consumer-supplied trigger (typically an icon button). A close, drop-\nin-shaped replacement for reaching outside this library for a third-party\ndropdown to build a gear menu, an avatar menu, or a history row's overflow\nmenu: click the trigger, a positioned menu appears, clicking an item both\nperforms the action *and* closes the menu.\n\n**ARIA pattern — `role=\"menu\"`/`role=\"menuitem\"` with real roving DOM\nfocus, not a listbox.** Two coherent, mutually-exclusive shapes were\navailable here: (a) `role=\"listbox\"`/`role=\"option\"` with\n`aria-activedescendant`, the pattern `<lr-select>`'s trigger-button +\npopup listbox uses, where DOM focus never leaves the trigger; or (b)\n`role=\"menu\"`/`role=\"menuitem\"` with real focus moving between actual\nfocusable rows, the WAI-ARIA \"menu button\" pattern. This picks (b):\n`<lr-menu-item>` rows are real, independently-focusable elements (see\nthat class's own doc), which is the more natural fit for a menu\nspecifically — unlike a listbox's rows, a menu's rows are conventionally\nbutton-/link-shaped, and every well-known native/OS menu (and this\nfamily's own `<lr-tree>`/`<lr-tree-node>` pair, which this component's\nroving-tabindex plumbing directly mirrors) already moves real focus rather\nthan merely a virtual `aria-activedescendant` pointer. `role`/`tabIndex`\nare consistently the menu-button shape throughout — never mixed with\nlistbox/option.\n\nInteraction contract (mirrors the WAI-ARIA APG \"menu button\" pattern):\n- Click the trigger (or Enter/Space on it, via the trigger's own native\n `click` activation) toggles the menu, moving focus to the first\n non-disabled item on open.\n- ArrowDown/ArrowUp on the trigger while closed also open it, focusing the\n first/last non-disabled item respectively.\n- Once open, ArrowDown/ArrowUp move the roving focus among non-disabled\n items (wrapping past either end — the recommended, and more common,\n menu-widget behavior, unlike `<lr-select>`'s clamped listbox nav).\n Home/End jump to the first/last non-disabled item. Enter/Space activate\n the focused item. Escape closes and returns focus to the trigger. A\n printable keypress runs type-ahead: roving focus jumps to the next\n non-disabled item whose text starts with the accumulated buffer, cycling\n from just after the active item (mirrors `<lr-select>`'s identical\n listbox type-ahead). All of the above (except Escape and Tab) only\n respond to keydowns from a real `<lr-menu-item>` target, so a slotted\n non-item control (e.g. a date input) keeps its own full default keyboard\n behavior.\n- Tab never traps focus and never calls `preventDefault()` — the browser's\n own Tab navigation always proceeds untouched. It closes the menu only\n when focus is on its way *out* of the popup: with a focusable in the\n `header`/`footer` region on the far side of the keypress, the menu stays\n open so native Tab can carry focus there instead. With neither region\n filled, Tab closes exactly as it always has. Tabbing past the popup's\n last focusable in either direction closes it too, including from slotted\n non-item content — which previously left the menu open while focus\n walked away.\n- Escape from `header`/`footer` content closes the menu and refocuses the\n trigger unconditionally, mirroring `<lr-popover>`'s handling of arbitrary\n popup content. `closeOnEscapeAnywhere` governs only the *legacy* shape —\n non-item content slotted into the **default** slot — and defaults to\n `false`, so existing consumers keep today's behavior unchanged.\n- A click outside both the trigger and the open popup closes it (mirrors\n `<lr-select>`'s `onDocPointer` exactly) — this does *not* refocus the\n trigger, since the outside click itself already moved focus somewhere\n the user chose; Escape and a committed selection *do* refocus the\n trigger, since those are dismissals with nowhere else for focus to go.\n\n`show(focus?)` and `hide(options?)` are the public imperative pair, for the cases the trigger\ncan't express: a slotted \"Apply\"/\"Done\" button inside the menu, a keyboard shortcut, a parent\nrestoring UI state. `hide({ focusTrigger: true })` is the one that also returns DOM focus to the\ntrigger — use it whenever the interaction that closed the menu hasn't already put focus\nsomewhere the user chose. Writing `open` directly still works and is fully equivalent apart from\nthe focus moves: the roving-tabindex reset is centralized in `updated()`, so `el.open = false`\nnever leaves a stale `tabindex=\"0\"` tab stop on the last active item.\n\nThe trigger element itself is read from the `trigger` slot's assigned element (first one, if\nseveral are assigned) and enhanced imperatively with\n`aria-haspopup=\"menu\"`/`aria-expanded`/`aria-controls` — the same \"reach into a consumer-owned\nlight-DOM element to complete its a11y wiring\" approach `<lr-dialog>` documents for its own\nheading detection. `aria-controls` targets this menu host (which receives a stable generated\n`id` only when the consumer did not supply one), rather than a shadow-private popup id, so the\nrelationship is resolvable from the trigger's root. `<lr-button>` and `<lr-icon-button>` observe\nthose attributes, forward the popup/expanded values to their shadow-internal native controls,\nand resolve the controls element-reference onto that focused control.\n\nThe popup is always rendered (never `display:none`) so `.focus()` calls on\nits content work synchronously the instant it opens — visually hidden via\n`visibility`/`opacity` instead (identical to `<lr-select>`'s own\n`[part=\"listbox\"]`). `visibility` is an inherited CSS property that\npierces the `<slot>` projection boundary, so every closed-state\n`<lr-menu-item>` is automatically excluded from sequential (Tab-key)\nnavigation with no separate JS bookkeeping.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | `<lr-menu-item>` elements, plus optionally plain `<hr>` dividers between groups (native `<hr>` already carries an implicit `separator` role, matching what `role=\"menu\"` expects between item groups). Arbitrary non-item content still renders here for backward compatibility, but the `header`/`footer` slots below are the supported place for it. |\n| `footer` | Same as `header`, rendered below the items — an \"Apply\"/\"Done\" button, a link to a fuller settings page, a count. |\n| `header` | Composed content rendered above the items and *outside* the `role=\"menu\"` list — a filter/search field, a section title, a summary row. Keeps its own full default keyboard behavior, is reachable with Tab from the items, and is ARIA-valid (arbitrary content inside `role=\"menu\"` is not). Collapses to no box at all while unfilled. |\n| `trigger` | The consumer's own trigger element (typically an icon button). Clicking it toggles the menu; it's positioned against via `internal/positioner.js`'s `place()`. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `footer` | The wrapper around the `footer` slot, below the list and outside `role=\"menu\"`. `display: none` while the slot is unfilled. |\n| `header` | The wrapper around the `header` slot, above the list and outside `role=\"menu\"`. `display: none` while the slot is unfilled. |\n| `list` | The `role=\"menu\"` container wrapping the default slot. |\n| `popup` | The positioned floating panel. |\n| `trigger` | The wrapper around the `trigger` slot (the positioning anchor). |"
9406
+ "value": "`<lr-menu>` — an anchored dropdown of `<lr-menu-item>` actions, opened\nfrom a consumer-supplied trigger (typically an icon button). A close, drop-\nin-shaped replacement for reaching outside this library for a third-party\ndropdown to build a gear menu, an avatar menu, or a history row's overflow\nmenu: click the trigger, a positioned menu appears, clicking an item both\nperforms the action *and* closes the menu.\n\n**ARIA pattern — `role=\"menu\"`/`role=\"menuitem\"` with real roving DOM\nfocus, not a listbox.** Two coherent, mutually-exclusive shapes were\navailable here: (a) `role=\"listbox\"`/`role=\"option\"` with\n`aria-activedescendant`, the pattern `<lr-select>`'s trigger-button +\npopup listbox uses, where DOM focus never leaves the trigger; or (b)\n`role=\"menu\"`/`role=\"menuitem\"` with real focus moving between actual\nfocusable rows, the WAI-ARIA \"menu button\" pattern. This picks (b):\n`<lr-menu-item>` rows are real, independently-focusable elements (see\nthat class's own doc), which is the more natural fit for a menu\nspecifically — unlike a listbox's rows, a menu's rows are conventionally\nbutton-/link-shaped, and every well-known native/OS menu (and this\nfamily's own `<lr-tree>`/`<lr-tree-node>` pair, which this component's\nroving-tabindex plumbing directly mirrors) already moves real focus rather\nthan merely a virtual `aria-activedescendant` pointer. `role`/`tabIndex`\nare consistently the menu-button shape throughout — never mixed with\nlistbox/option.\n\nInteraction contract (mirrors the WAI-ARIA APG \"menu button\" pattern):\n- Click the trigger (or Enter/Space on it, via the trigger's own native\n `click` activation) toggles the menu, moving focus to the first\n non-disabled item on open.\n- ArrowDown/ArrowUp on the trigger while closed also open it, focusing the\n first/last non-disabled item respectively.\n- Once open, ArrowDown/ArrowUp move the roving focus among non-disabled\n items (wrapping past either end — the recommended, and more common,\n menu-widget behavior, unlike `<lr-select>`'s clamped listbox nav).\n Home/End jump to the first/last non-disabled item. Enter/Space activate\n the focused item. Escape closes and returns focus to the trigger. A\n printable keypress runs type-ahead: roving focus jumps to the next\n non-disabled item whose text starts with the accumulated buffer, cycling\n from just after the active item (mirrors `<lr-select>`'s identical\n listbox type-ahead). All of the above (except Escape and Tab) only\n respond to keydowns from a real `<lr-menu-item>` target, so a slotted\n non-item control (e.g. a date input) keeps its own full default keyboard\n behavior.\n- Tab never traps focus and never calls `preventDefault()` — the browser's\n own Tab navigation always proceeds untouched. It closes the menu only\n when focus is on its way *out* of the popup: with a focusable in the\n `header`/`footer` region on the far side of the keypress, the menu stays\n open so native Tab can carry focus there instead. With neither region\n filled, Tab closes exactly as it always has. Tabbing past the popup's\n last focusable in either direction closes it too, including from slotted\n non-item content — which previously left the menu open while focus\n walked away.\n- Escape from `header`/`footer` content closes the menu and refocuses the\n trigger unconditionally, mirroring `<lr-popover>`'s handling of arbitrary\n popup content. `closeOnEscapeAnywhere` governs only the *legacy* shape —\n non-item content slotted into the **default** slot — and defaults to\n `false`, so existing consumers keep today's behavior unchanged.\n- A click outside both the trigger and the open popup closes it (mirrors\n `<lr-select>`'s `onDocPointer` exactly) — this does *not* refocus the\n trigger, since the outside click itself already moved focus somewhere\n the user chose; Escape and a committed selection *do* refocus the\n trigger, since those are dismissals with nowhere else for focus to go.\n\n`show(focus?)` and `hide(options?)` are the public imperative pair, for the cases the trigger\ncan't express: a slotted \"Apply\"/\"Done\" button inside the menu, a keyboard shortcut, a parent\nrestoring UI state. `hide({ focusTrigger: true })` is the one that also returns DOM focus to the\ntrigger — use it whenever the interaction that closed the menu hasn't already put focus\nsomewhere the user chose. Writing `open` directly still works and is fully equivalent apart from\nthe focus moves: the roving-tabindex reset is centralized in `updated()`, so `el.open = false`\nnever leaves a stale `tabindex=\"0\"` tab stop on the last active item.\n\nThe trigger element itself is read from the `trigger` slot's assigned element (first one, if\nseveral are assigned) and enhanced imperatively with\n`aria-haspopup=\"menu\"`/`aria-expanded`/`aria-controls` — the same \"reach into a consumer-owned\nlight-DOM element to complete its a11y wiring\" approach `<lr-dialog>` documents for its own\nheading detection. `aria-controls` targets this menu host (which receives a stable generated\n`id` only when the consumer did not supply one), rather than a shadow-private popup id, so the\nrelationship is resolvable from the trigger's root. `<lr-button>` and `<lr-icon-button>` observe\nthose attributes, forward the popup/expanded values to their shadow-internal native controls,\nand resolve the controls element-reference onto that focused control. In supporting browsers,\nassigning the element reference intentionally clears the internal control's serialized\n`aria-controls` value; `ariaControlsElements` is the relationship's source of truth. Browsers\nwithout the reflected element-reference API retain the string as a best-effort fallback.\n\nThe popup is always rendered (never `display:none`) so `.focus()` calls on\nits content work synchronously the instant it opens — visually hidden via\n`visibility`/`opacity` instead (identical to `<lr-select>`'s own\n`[part=\"listbox\"]`). `visibility` is an inherited CSS property that\npierces the `<slot>` projection boundary, so every closed-state\n`<lr-menu-item>` is automatically excluded from sequential (Tab-key)\nnavigation with no separate JS bookkeeping.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | `<lr-menu-item>` elements, plus optionally plain `<hr>` dividers between groups (native `<hr>` already carries an implicit `separator` role, matching what `role=\"menu\"` expects between item groups). Arbitrary non-item content still renders here for backward compatibility, but the `header`/`footer` slots below are the supported place for it. |\n| `footer` | Same as `header`, rendered below the items — an \"Apply\"/\"Done\" button, a link to a fuller settings page, a count. |\n| `header` | Composed content rendered above the items and *outside* the `role=\"menu\"` list — a filter/search field, a section title, a summary row. Keeps its own full default keyboard behavior, is reachable with Tab from the items, and is ARIA-valid (arbitrary content inside `role=\"menu\"` is not). Collapses to no box at all while unfilled. |\n| `trigger` | The consumer's own trigger element (typically an icon button). Clicking it toggles the menu; it's positioned against via `internal/positioner.js`'s `place()`. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `footer` | The wrapper around the `footer` slot, below the list and outside `role=\"menu\"`. `display: none` while the slot is unfilled. |\n| `header` | The wrapper around the `header` slot, above the list and outside `role=\"menu\"`. `display: none` while the slot is unfilled. |\n| `list` | The `role=\"menu\"` container wrapping the default slot. |\n| `popup` | The positioned floating panel. |\n| `trigger` | The wrapper around the `trigger` slot (the positioning anchor). |"
9393
9407
  },
9394
9408
  "attributes": [
9395
9409
  {
@@ -11088,7 +11102,7 @@
11088
11102
  "name": "lr-popover",
11089
11103
  "description": {
11090
11104
  "kind": "markdown",
11091
- "value": "`<lr-popover>` — a click-triggered, light-dismiss floating surface.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Popover content. |\n| `trigger` | The interactive element that toggles the popover. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `content` | The content wrapper. |\n| `popup` | The positioned popup. |\n| `trigger` | The trigger wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-overlay-max-inline-size` — Maximum inline size of the popup (default `--lr-size-20rem`)."
11105
+ "value": "`<lr-popover>` — a click-triggered, light-dismiss floating surface.\n\nThe slotted trigger receives `aria-haspopup`, `aria-expanded`, and `aria-controls`.\n`aria-controls` targets this public host (which receives a stable generated `id` when the\nconsumer did not supply one), not the shadow-private popup. That keeps the relationship\nresolvable for native triggers and lets `<lr-button>`/`<lr-icon-button>` reflect the host onto\ntheir focused shadow-internal controls through `ariaControlsElements`.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Popover content. |\n| `trigger` | The interactive element that toggles the popover. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `content` | The content wrapper. |\n| `popup` | The positioned popup. |\n| `trigger` | The trigger wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-overlay-max-inline-size` — Maximum inline size of the popup (default `--lr-size-20rem`)."
11092
11106
  },
11093
11107
  "attributes": [
11094
11108
  {
@@ -15913,7 +15927,7 @@
15913
15927
  "name": "lr-tooltip",
15914
15928
  "description": {
15915
15929
  "kind": "markdown",
15916
- "value": "`<lr-tooltip>` — a localized, hover/focus tooltip for a consumer-owned trigger.\nPlain content uses tooltip semantics. When the default slot contains an actionable descendant,\nthe popup promotes to a named dialog and remains open while pointer or focus is inside so its\ncontrols can be reached. Prefer `<lr-popover>` when click-to-open ownership is desired.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Tooltip content. |\n| `trigger` | The element that receives hover/focus listeners. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `popup` | The tooltip popup. |\n| `trigger` | The trigger wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-tooltip-max-inline-size` — Maximum inline size of the tooltip (default `--lr-size-20rem`).\n- `--lr-tooltip-background` — Tooltip background color (default `--lr-color-neutral`).\n- `--lr-tooltip-color` — Tooltip text color (default `--lr-color-on-neutral`)."
15930
+ "value": "`<lr-tooltip>` — a localized, hover/focus tooltip for a consumer-owned trigger.\nPlain content uses tooltip semantics. When the default slot contains an actionable descendant,\nthe popup promotes to a named dialog and remains open while pointer or focus is inside so its\ncontrols can be reached. Prefer `<lr-popover>` when click-to-open ownership is desired.\n\nWhile open, the trigger's `aria-describedby` targets a hidden text proxy in this component's\nlight DOM rather than the shadow-private popup. Native triggers can resolve that ID directly;\n`<lr-button>` and `<lr-icon-button>` reflect it onto their focused internal controls through\n`ariaDescribedByElements`, where the serialized internal attribute is intentionally empty.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Tooltip content. |\n| `trigger` | The element that receives hover/focus listeners. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `popup` | The tooltip popup. |\n| `trigger` | The trigger wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-tooltip-max-inline-size` — Maximum inline size of the tooltip (default `--lr-size-20rem`).\n- `--lr-tooltip-background` — Tooltip background color (default `--lr-color-neutral`).\n- `--lr-tooltip-color` — Tooltip text color (default `--lr-color-on-neutral`)."
15917
15931
  },
15918
15932
  "attributes": [
15919
15933
  {
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@aceshooting/lyra-ui",
4
- "version": "7.0.0",
4
+ "version": "7.1.0",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -2103,7 +2103,7 @@
2103
2103
  },
2104
2104
  {
2105
2105
  "name": "lr-button",
2106
- "description": "`<lr-button>` — a generic action-button primitive. Renders an internal native\n`<button part=\"base\">`. `type=\"submit\"`/`type=\"reset\"`\nare handled by this component itself via the host's own `closest('form')` — a shadow-internal\nnative `<button type=\"submit\">` does not participate in an ancestor light-DOM form's submission\non its own, since form-submitter semantics don't cross the shadow boundary.\n\nWhen `href` is set to a safe link URL (`http:`/`https:`/`blob:`/`mailto:`/relative — see\n`safeLinkHref`, or `safeDownloadHref` which drops `mailto:` when `download` is set) the root\nrenders as a real `<a part=\"base\" href=…>` instead — for a link styled\nas a button (e.g. a CTA). Native navigation is then the anchor's own activation, so the\nsubmit/reset click handler and `type` (submit/reset) have no effect in that mode. When the\nbutton is disabled (its own `disabled` or an ancestor `<fieldset disabled>`) the anchor renders\nwith `aria-disabled=\"true\"` and **no `href`** — an href-less anchor is not focusable or\nnavigable, so a disabled link button genuinely cannot be activated (unlike a bare\n`aria-disabled` on a still-navigable link). An unsafe/unparseable `href` falls back to the\nnative `<button>`.\n\n`accessibleLabel` (attribute `aria-label`) is forwarded reactively to the internal button/anchor\nas a literal string (for an icon-only button with no visible label); external\n`aria-labelledby`/`aria-describedby` idrefs are not copied across the shadow boundary.\nHost `aria-haspopup` and `aria-expanded` values are likewise forwarded to the internal semantic\ncontrol. When host `aria-controls` names elements in the host's own root, the controls\nrelationship is resolved onto the internal control through the reflected element-reference API\nso it remains valid across this component's shadow boundary; browsers without that API retain\nthe forwarded string attribute as a best-effort fallback.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Default slot: the button's label content. |\n| `end` | Trailing icon/content, rendered after the label. |\n| `start` | Leading icon/content, rendered before the label. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The internal native `<button>` (or an `<a>` when `href` resolves to a safe link). |\n| `end` | The `end` slot wrapper. |\n| `label` | The default-slot label wrapper. |\n| `spinner` | The loading spinner, present only while `loading` is `true`. |\n| `start` | The `start` slot wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-button-width` (default: `100%`) — Inline size of the internal button. The host defaults it to `100%` so the native button follows the host's own width; override to `auto` (or any other value) for a compact inline composition.\n- `--lr-button-hover-brightness` (default: `1.08`) — `filter: brightness()` multiplier applied while hovering a non-disabled button.\n- `--lr-button-active-scale` (default: `0.9875`) — `transform: scale()` factor applied while a non-disabled button is pressed.\n- `--lr-button-spinner-duration` (default: `var(--lr-transition-ambient)`) — Timing of the `loading` spinner.\n- `--lr-button-accent` (default: `var(--lr-color-text)`) — Text/glyph color for the chrome-less appearances (`outlined`, `plain`, `link`). Swapped per `variant` to that variant's semantic color.\n- `--lr-button-fill` (default: `var(--lr-color-surface)`) — Background of `appearance=\"filled\"`. Swapped per `variant` to that variant's semantic color.\n- `--lr-button-on-fill` (default: `var(--lr-color-text)`) — Text color on top of `--lr-button-fill`. Swapped per `variant` to that variant's `on-*` color.\n- `--lr-button-accent-fill` (default: `var(--lr-color-neutral)`) — Background of `appearance=\"accent\"` (and its border color). Swapped per `variant` to that variant's semantic color.\n- `--lr-button-accent-on-fill` (default: `var(--lr-color-on-neutral)`) — Text color on top of `--lr-button-accent-fill`. Swapped per `variant` to that variant's `on-*` color.\n- `--lr-button-border` (default: `var(--lr-color-border)`) — Border color of the internal button. Swapped per `variant` to that variant's semantic color.\n- `--lr-button-outlined-border` (default: `var(--lr-color-border-strong)`) — Border color of `appearance=\"outlined\"`, which overrides `--lr-button-border`.\n- `--lr-button-outlined-fill` (default: `transparent`) — Background of `appearance=\"outlined\"`. Transparent by default; set it to tint the button (e.g. a faint surface wash behind the outline) without a `::part(base)` rule. Like `--lr-button-quiet-*`, it is deliberately *not* swapped per `variant`. Note that the `:hover` `filter: brightness()` applies to whatever fill is set, so a tinted outlined button now visibly brightens on hover where a transparent one did not.\n- `--lr-button-quiet-border` (default: `var(--lr-color-border)`) — Border color of `appearance=\"quiet\"`.\n- `--lr-button-quiet-text` (default: `var(--lr-color-text-quiet)`) — Text color of `appearance=\"quiet\"`.\n- `--lr-button-size-2xs` (default: `var(--lr-size-1-25rem)`) — `min-block-size` at `size=\"2xs\"`.\n- `--lr-button-size-xs` (default: `var(--lr-size-1-5rem)`) — `min-block-size` at `size=\"xs\"`.\n- `--lr-button-size-s` (default: `var(--lr-size-1-875rem)`) — `min-block-size` at `size=\"s\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"s\"` control height.\n- `--lr-button-size-m` (default: `var(--lr-size-2-5rem)`) — `min-block-size` at `size=\"m\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own default control height, so a default-size button sitting next to a default-size input/select/combobox in the same row lines up.\n- `--lr-button-size-l` (default: `var(--lr-size-3rem)`) — `min-block-size` at `size=\"l\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"l\"` control height.\n- `--lr-button-size-xl` (default: `var(--lr-size-3-5rem)`) — `min-block-size` at `size=\"xl\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"xl\"` control height.\n- `--lr-button-padding-block` (default: `var(--lr-space-xs)`) — Block padding of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). Override it to retune a tier without a `::part(base)` rule; `appearance=\"link\"` ignores it (it renders with zero padding).\n- `--lr-button-padding-inline` (default: `var(--lr-space-m)`) — Inline padding of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). `appearance=\"link\"` ignores it.\n- `--lr-button-font-size` (default: `var(--lr-font-size-m)`) — Font size of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). `appearance=\"link\"` ignores it and inherits the ambient font instead.\n- `--lr-button-min-height` (default: `var(--lr-button-size-m)`) — The active tier's `min-block-size` floor. Re-assigned per `size` tier to that tier's own `--lr-button-size-*` token, and used as the fallback when `--lr-button-height` is unset.\n- `--lr-button-height` — Exact height of the internal button. **Undeclared by default** — so the button keeps the active tier's `min-block-size` floor and an `auto` height, exactly as before. Set it (e.g. to pin the button to a fixed toolbar row) to both floor *and* cap the height. Never declare it as `auto`: a declared value wins over the `var()` fallback arm and would make every tier's floor dead code. `appearance=\"link\"` ignores it.\n- `--lr-button-gap` (default: `var(--lr-space-2xs)`) — Gap between the icon/label and any slotted content in the internal button. Unlike the size knobs above it does not vary by `size` tier. Override it to retune without a `::part(base)` rule.\n- `--lr-button-radius` (default: `var(--lr-radius)`) — Corner radius of the internal button. Does not vary by `size` tier. `appearance=\"link\"` ignores it (it renders with zero radius).\n- `--lr-button-shadow` — Box shadow of the internal button. **Undeclared by default**, so `box-shadow` falls back to `none` — byte-identical to before this property existed. Set it (e.g. an elevated/floating action button) without a `::part(base)` rule.",
2106
+ "description": "`<lr-button>` — a generic action-button primitive. Renders an internal native\n`<button part=\"base\">`. `type=\"submit\"`/`type=\"reset\"`\nare handled by this component itself via the host's own `closest('form')` — a shadow-internal\nnative `<button type=\"submit\">` does not participate in an ancestor light-DOM form's submission\non its own, since form-submitter semantics don't cross the shadow boundary.\n\nWhen `href` is set to a safe link URL (`http:`/`https:`/`blob:`/`mailto:`/relative — see\n`safeLinkHref`, or `safeDownloadHref` which drops `mailto:` when `download` is set) the root\nrenders as a real `<a part=\"base\" href=…>` instead — for a link styled\nas a button (e.g. a CTA). Native navigation is then the anchor's own activation, so the\nsubmit/reset click handler and `type` (submit/reset) have no effect in that mode. When the\nbutton is disabled (its own `disabled` or an ancestor `<fieldset disabled>`) the anchor renders\nwith `aria-disabled=\"true\"` and **no `href`** — an href-less anchor is not focusable or\nnavigable, so a disabled link button genuinely cannot be activated (unlike a bare\n`aria-disabled` on a still-navigable link). An unsafe/unparseable `href` falls back to the\nnative `<button>`.\n\n`accessibleLabel` (attribute `aria-label`) is forwarded reactively to the internal button/anchor\nas a literal string (for an icon-only button with no visible label). Host `aria-describedby`\nIDREFs are resolved through `ariaDescribedByElements`; external `aria-labelledby` is not copied\nacross the shadow boundary.\nHost `aria-haspopup` and `aria-expanded` values are likewise forwarded to the internal semantic\ncontrol. When host `aria-controls` names elements in the host's own root, the controls\nrelationship is resolved onto the internal control through the reflected element-reference API\nso it remains valid across this component's shadow boundary. Assigning that relationship\nintentionally clears the serialized `aria-controls` value; read `ariaControlsElements` in a\nsupporting browser. Browsers without that API retain the forwarded string attribute as a\nbest-effort fallback.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Default slot: the button's label content. |\n| `end` | Trailing icon/content, rendered after the label. |\n| `start` | Leading icon/content, rendered before the label. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The internal native `<button>` (or an `<a>` when `href` resolves to a safe link). |\n| `end` | The `end` slot wrapper. |\n| `label` | The default-slot label wrapper. |\n| `spinner` | The loading spinner, present only while `loading` is `true`. |\n| `start` | The `start` slot wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-button-width` (default: `100%`) — Inline size of the internal button. The host defaults it to `100%` so the native button follows the host's own width; override to `auto` (or any other value) for a compact inline composition.\n- `--lr-button-hover-brightness` (default: `1.08`) — `filter: brightness()` multiplier applied while hovering a non-disabled button.\n- `--lr-button-active-scale` (default: `0.9875`) — `transform: scale()` factor applied while a non-disabled button is pressed.\n- `--lr-button-spinner-duration` (default: `var(--lr-transition-ambient)`) — Timing of the `loading` spinner.\n- `--lr-button-accent` (default: `var(--lr-color-text)`) — Text/glyph color for the chrome-less appearances (`outlined`, `plain`, `link`). Swapped per `variant` to that variant's semantic color.\n- `--lr-button-fill` (default: `var(--lr-color-surface)`) — Background of `appearance=\"filled\"`. Swapped per `variant` to that variant's semantic color.\n- `--lr-button-on-fill` (default: `var(--lr-color-text)`) — Text color on top of `--lr-button-fill`. Swapped per `variant` to that variant's `on-*` color.\n- `--lr-button-accent-fill` (default: `var(--lr-color-neutral)`) — Background of `appearance=\"accent\"` (and its border color). Swapped per `variant` to that variant's semantic color.\n- `--lr-button-accent-on-fill` (default: `var(--lr-color-on-neutral)`) — Text color on top of `--lr-button-accent-fill`. Swapped per `variant` to that variant's `on-*` color.\n- `--lr-button-border` (default: `var(--lr-color-border)`) — Border color of the internal button. Swapped per `variant` to that variant's semantic color.\n- `--lr-button-outlined-border` (default: `var(--lr-color-border-strong)`) — Border color of `appearance=\"outlined\"`, which overrides `--lr-button-border`.\n- `--lr-button-outlined-fill` (default: `transparent`) — Background of `appearance=\"outlined\"`. Transparent by default; set it to tint the button (e.g. a faint surface wash behind the outline) without a `::part(base)` rule. Like `--lr-button-quiet-*`, it is deliberately *not* swapped per `variant`. Note that the `:hover` `filter: brightness()` applies to whatever fill is set, so a tinted outlined button now visibly brightens on hover where a transparent one did not.\n- `--lr-button-quiet-border` (default: `var(--lr-color-border)`) — Border color of `appearance=\"quiet\"`.\n- `--lr-button-quiet-text` (default: `var(--lr-color-text-quiet)`) — Text color of `appearance=\"quiet\"`.\n- `--lr-button-size-2xs` (default: `var(--lr-size-1-25rem)`) — `min-block-size` at `size=\"2xs\"`.\n- `--lr-button-size-xs` (default: `var(--lr-size-1-5rem)`) — `min-block-size` at `size=\"xs\"`.\n- `--lr-button-size-s` (default: `var(--lr-size-1-875rem)`) — `min-block-size` at `size=\"s\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"s\"` control height.\n- `--lr-button-size-m` (default: `var(--lr-size-2-5rem)`) — `min-block-size` at `size=\"m\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own default control height, so a default-size button sitting next to a default-size input/select/combobox in the same row lines up.\n- `--lr-button-size-l` (default: `var(--lr-size-3rem)`) — `min-block-size` at `size=\"l\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"l\"` control height.\n- `--lr-button-size-xl` (default: `var(--lr-size-3-5rem)`) — `min-block-size` at `size=\"xl\"`. Matches `lr-input`/`lr-select`/`lr-combobox`'s own `size=\"xl\"` control height.\n- `--lr-button-padding-block` (default: `var(--lr-space-xs)`) — Block padding of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). Override it to retune a tier without a `::part(base)` rule; `appearance=\"link\"` ignores it (it renders with zero padding).\n- `--lr-button-padding-inline` (default: `var(--lr-space-m)`) — Inline padding of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). `appearance=\"link\"` ignores it.\n- `--lr-button-font-size` (default: `var(--lr-font-size-m)`) — Font size of the internal button, re-assigned per `size` tier (the default is the `m` tier's value). `appearance=\"link\"` ignores it and inherits the ambient font instead.\n- `--lr-button-min-height` (default: `var(--lr-button-size-m)`) — The active tier's `min-block-size` floor. Re-assigned per `size` tier to that tier's own `--lr-button-size-*` token, and used as the fallback when `--lr-button-height` is unset.\n- `--lr-button-height` — Exact height of the internal button. **Undeclared by default** — so the button keeps the active tier's `min-block-size` floor and an `auto` height, exactly as before. Set it (e.g. to pin the button to a fixed toolbar row) to both floor *and* cap the height. Never declare it as `auto`: a declared value wins over the `var()` fallback arm and would make every tier's floor dead code. `appearance=\"link\"` ignores it.\n- `--lr-button-gap` (default: `var(--lr-space-2xs)`) — Gap between the icon/label and any slotted content in the internal button. Unlike the size knobs above it does not vary by `size` tier. Override it to retune without a `::part(base)` rule.\n- `--lr-button-radius` (default: `var(--lr-radius)`) — Corner radius of the internal button. Does not vary by `size` tier. `appearance=\"link\"` ignores it (it renders with zero radius).\n- `--lr-button-shadow` — Box shadow of the internal button. **Undeclared by default**, so `box-shadow` falls back to `none` — byte-identical to before this property existed. Set it (e.g. an elevated/floating action button) without a `::part(base)` rule.",
2107
2107
  "attributes": [
2108
2108
  {
2109
2109
  "name": "appearance",
@@ -2118,6 +2118,10 @@
2118
2118
  "name": "aria-controls",
2119
2119
  "description": "Type: `string | null` \nDefault: `null`"
2120
2120
  },
2121
+ {
2122
+ "name": "aria-describedby",
2123
+ "description": "Type: `string | null` \nDefault: `null`"
2124
+ },
2121
2125
  {
2122
2126
  "name": "aria-expanded",
2123
2127
  "description": "Type: `string | null` \nDefault: `null`"
@@ -3012,7 +3016,7 @@
3012
3016
  },
3013
3017
  {
3014
3018
  "name": "lr-checkbox",
3015
- "description": "`<lr-checkbox>` — a boolean form control. Structurally the same idea as\n`<lr-switch>` (form-associated via `ElementInternals`, click and\nSpace both toggle) but with checkbox semantics: `role=\"checkbox\"` +\nan `aria-checked` that can also be `\"mixed\"`, and a visual box/checkmark\ninstead of a track/thumb.\n\n`checked` is not a plain string, so this attaches `ElementInternals`\ndirectly and implements its own `updateValidity()` rather than using the\n`FormAssociated` mixin — see `<lr-combobox>` for the same\ndirect-`ElementInternals` shape with a non-string value.\n\nDeliberately no hint/error chrome of its own -- the default slot already carries real, visible\nlabel text (see `@slot` below), so a labeled-field frame built around `label`/`hint`/`errorText`\nprops has nothing to add here. A consumer needing hint/error messaging composes it in their own\nwrapper (e.g. `<lr-tool-param-form>` folds a boolean field's validation error into adjacent\ndescription text / `aria-label` rather than a `<lr-checkbox>`-owned slot), the same way a\nnative `<input type=\"checkbox\">` plus `<label>` pairs with an externally-owned error node.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Label text, rendered next to the box. Clicking it toggles the checkbox, the same as clicking a native checkbox's associated `<label>`. If left empty, set `aria-label` on the host so the control still has an accessible name. A host `aria-describedby` attribute is forwarded to the internal `role=\"checkbox\"` so externally-owned descriptions can be associated with this individual control. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The whole interactive control (`role=\"checkbox\"`); wraps the box and label. |\n| `box` | The small square that shows the checkmark/indeterminate dash. |\n| `checkmark` | The checkmark (or indeterminate dash) glyph inside the box. |\n| `label` | The wrapper around the default slot. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-checkbox-label-indent` (default: `calc(min(var(--lr-icon-button-size), 1.75rem) + var(--lr-space-s))`) — The inline distance from the control's start edge to the start of the label text, i.e. the box's own floor plus the gap next to it. Published so a consumer composing per-option hint text under the label can align it without re-deriving that formula from the shadow styles, and used as the source of the real gap so the two cannot drift. Setting it on the element (or on `lr-checkbox` in your own stylesheet) moves the label; because custom properties inherit down and not sideways, it is *not* readable from a sibling node in your tree — align a sibling by computing the same formula from `--lr-theme-icon-button-size` and `--lr-theme-space-s`, which you control.\n- `--lr-checkbox-checked-bg` (default: `var(--lr-color-brand)`) — Background of `[part='box']` while `checked` or `indeterminate`. Retint just this control's checked fill without touching the shared `--lr-color-brand` token every other component also reads.\n- `--lr-checkbox-checked-border` (default: `var(--lr-color-brand)`) — Border color of `[part='box']` while `checked` or `indeterminate`.",
3019
+ "description": "`<lr-checkbox>` — a boolean form control. Structurally the same idea as\n`<lr-switch>` (form-associated via `ElementInternals`, click and\nSpace both toggle) but with checkbox semantics: `role=\"checkbox\"` +\nan `aria-checked` that can also be `\"mixed\"`, and a visual box/checkmark\ninstead of a track/thumb.\n\n`checked` is not a plain string, so this attaches `ElementInternals`\ndirectly and implements its own `updateValidity()` rather than using the\n`FormAssociated` mixin — see `<lr-combobox>` for the same\ndirect-`ElementInternals` shape with a non-string value.\n\nDeliberately no hint/error chrome of its own -- the default slot already carries real, visible\nlabel text (see `@slot` below), so a labeled-field frame built around `label`/`hint`/`errorText`\nprops has nothing to add here. A consumer needing hint/error messaging composes it in their own\nwrapper (e.g. `<lr-tool-param-form>` folds a boolean field's validation error into adjacent\ndescription text / `aria-label` rather than a `<lr-checkbox>`-owned slot), the same way a\nnative `<input type=\"checkbox\">` plus `<label>` pairs with an externally-owned error node.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Label text, rendered next to the box. Clicking it toggles the checkbox, the same as clicking a native checkbox's associated `<label>`. If left empty, set `aria-label` on the host so the control still has an accessible name. A host `aria-describedby` attribute is resolved onto the internal `role=\"checkbox\"` through `ariaDescribedByElements` so externally-owned descriptions remain valid across the shadow boundary. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The whole interactive control (`role=\"checkbox\"`); wraps the box and label. |\n| `box` | The small square that shows the checkmark/indeterminate dash. |\n| `checkmark` | The checkmark (or indeterminate dash) glyph inside the box. |\n| `label` | The wrapper around the default slot. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-checkbox-label-indent` (default: `calc(min(var(--lr-icon-button-size), 1.75rem) + var(--lr-space-s))`) — The inline distance from the control's start edge to the start of the label text, i.e. the box's own floor plus the gap next to it. Published so a consumer composing per-option hint text under the label can align it without re-deriving that formula from the shadow styles, and used as the source of the real gap so the two cannot drift. Setting it on the element (or on `lr-checkbox` in your own stylesheet) moves the label; because custom properties inherit down and not sideways, it is *not* readable from a sibling node in your tree — align a sibling by computing the same formula from `--lr-theme-icon-button-size` and `--lr-theme-space-s`, which you control.\n- `--lr-checkbox-checked-bg` (default: `var(--lr-color-brand)`) — Background of `[part='box']` while `checked` or `indeterminate`. Retint just this control's checked fill without touching the shared `--lr-color-brand` token every other component also reads.\n- `--lr-checkbox-checked-border` (default: `var(--lr-color-brand)`) — Border color of `[part='box']` while `checked` or `indeterminate`.",
3016
3020
  "attributes": [
3017
3021
  {
3018
3022
  "name": "checked",
@@ -8549,12 +8553,16 @@
8549
8553
  },
8550
8554
  {
8551
8555
  "name": "lr-icon-button",
8552
- "description": "`<lr-icon-button>` — an accessible icon-only action button.\n\nSet `icon` for one of `<lr-icon>`'s named glyphs, or slot your own content instead. Slotted\ncontent is a **sibling** of the built-in glyph rather than being piped through `<lr-icon>`, so\nany complete element — an `<svg>`, an `<img>`, an `<lr-flag>` — renders at its own natural\naspect ratio instead of being forced into a 1:1 box. Bare SVG geometry (`<path>`, `<circle>`,\nand a handful of other raw primitives) with no enclosing `<svg>` of its own is detected and\ncloned into a real, internal SVG-namespaced element so it still paints — the same fallback\n`<lr-icon>`'s own custom-content slot uses, but narrowly scoped so a custom element (e.g. a\nslotted `<lr-flag>`) is never run through it.\n\nHost `aria-haspopup` and `aria-expanded` values are forwarded reactively to the shadow-internal\nnative button. When host `aria-controls` names elements in the host's own root, the controls\nrelationship is resolved onto that focused control through the reflected element-reference API\nso it remains valid across this component's shadow boundary; browsers without that API retain\nthe forwarded string attribute as a best-effort fallback.\n\nForm-associated (mirroring `<lr-button>`'s identical shape): discoverable through\n`form.elements`, and `type=\"submit\"`/`type=\"reset\"` are handled by this component itself via\nthe host's own `closest('form')` — a shadow-internal native `<button type=\"submit\">` does not\nparticipate in an ancestor light-DOM form's submission on its own, since form-submitter\nsemantics don't cross the shadow boundary.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Optional custom icon content, rendered beside (not inside) the `icon` glyph. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `button` | Native button. |\n| `fallback` | The internal SVG-namespaced clone target for slotted bare geometry. Carries the same `fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"` defaults `<lr-icon>`'s own wrapper svg does, so bare stroke-style geometry (no fill/stroke of its own) renders outlined instead of as a solid shape; an explicit `fill`/`stroke`/etc. already present on the slotted node still wins for that node. Only present in the DOM while at least one top-level slotted element needs it; a complete `<svg>`, `<img>`, or custom element never mounts it. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-icon-button-size` (default: `2.5rem`) — Minimum tappable inline and block size of the native button — a **floor**, not a fixed size: content larger than it grows the button and keeps its own aspect ratio, while a small glyph pads out to it. A library-wide token (declared on `:root` by `tokens.styles.ts`, and the shared minimum tappable size several other components size their icon controls against), so overriding it globally resizes all of them together.\n- `--lr-icon-button-radius` (default: `var(--lr-radius)`) — Corner radius of the native button.\n- `--lr-icon-button-background` (default: `transparent`) — Background fill of the native button.\n- `--lr-icon-button-background-hover` (default: `var(--lr-color-surface)`) — Background fill on hover.\n- `--lr-icon-button-color` (default: `inherit`) — Icon/text color of the native button.\n- `--lr-icon-button-color-hover` (default: `var(--lr-icon-button-color, inherit)`) — Icon/text color on hover.\n- `--lr-icon-button-border` (default: `0`) — Complete border shorthand of the native button.",
8556
+ "description": "`<lr-icon-button>` — an accessible icon-only action button.\n\nSet `icon` for one of `<lr-icon>`'s named glyphs, or slot your own content instead. Slotted\ncontent is a **sibling** of the built-in glyph rather than being piped through `<lr-icon>`, so\nany complete element — an `<svg>`, an `<img>`, an `<lr-flag>` — renders at its own natural\naspect ratio instead of being forced into a 1:1 box. Bare SVG geometry (`<path>`, `<circle>`,\nand a handful of other raw primitives) with no enclosing `<svg>` of its own is detected and\ncloned into a real, internal SVG-namespaced element so it still paints — the same fallback\n`<lr-icon>`'s own custom-content slot uses, but narrowly scoped so a custom element (e.g. a\nslotted `<lr-flag>`) is never run through it.\n\nHost `aria-haspopup` and `aria-expanded` values are forwarded reactively to the shadow-internal\nnative button. Host `aria-describedby` IDREFs are resolved through `ariaDescribedByElements`.\nWhen host `aria-controls` names elements in the host's own root, the controls relationship is\nresolved onto that focused control through the reflected element-reference API so it remains\nvalid across this component's shadow boundary. Assigning that relationship intentionally clears\nthe serialized `aria-controls` value; read `ariaControlsElements` in a supporting browser.\nBrowsers without that API retain the forwarded string attribute as a best-effort fallback.\n\nForm-associated (mirroring `<lr-button>`'s identical shape): discoverable through\n`form.elements`, and `type=\"submit\"`/`type=\"reset\"` are handled by this component itself via\nthe host's own `closest('form')` — a shadow-internal native `<button type=\"submit\">` does not\nparticipate in an ancestor light-DOM form's submission on its own, since form-submitter\nsemantics don't cross the shadow boundary.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Optional custom icon content, rendered beside (not inside) the `icon` glyph. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `button` | Native button. |\n| `fallback` | The internal SVG-namespaced clone target for slotted bare geometry. Carries the same `fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"` defaults `<lr-icon>`'s own wrapper svg does, so bare stroke-style geometry (no fill/stroke of its own) renders outlined instead of as a solid shape; an explicit `fill`/`stroke`/etc. already present on the slotted node still wins for that node. Only present in the DOM while at least one top-level slotted element needs it; a complete `<svg>`, `<img>`, or custom element never mounts it. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-icon-button-size` (default: `2.5rem`) — Minimum tappable inline and block size of the native button — a **floor**, not a fixed size: content larger than it grows the button and keeps its own aspect ratio, while a small glyph pads out to it. A library-wide token (declared on `:root` by `tokens.styles.ts`, and the shared minimum tappable size several other components size their icon controls against), so overriding it globally resizes all of them together.\n- `--lr-icon-button-radius` (default: `var(--lr-radius)`) — Corner radius of the native button.\n- `--lr-icon-button-background` (default: `transparent`) — Background fill of the native button.\n- `--lr-icon-button-background-hover` (default: `var(--lr-color-surface)`) — Background fill on hover.\n- `--lr-icon-button-color` (default: `inherit`) — Icon/text color of the native button.\n- `--lr-icon-button-color-hover` (default: `var(--lr-icon-button-color, inherit)`) — Icon/text color on hover.\n- `--lr-icon-button-border` (default: `0`) — Complete border shorthand of the native button.\n- `--lr-icon-button-border-hover` (default: `var(--lr-icon-button-border, 0)`) — Complete border shorthand on hover.",
8553
8557
  "attributes": [
8554
8558
  {
8555
8559
  "name": "aria-controls",
8556
8560
  "description": "Type: `string | null` \nDefault: `null`"
8557
8561
  },
8562
+ {
8563
+ "name": "aria-describedby",
8564
+ "description": "Type: `string | null` \nDefault: `null`"
8565
+ },
8558
8566
  {
8559
8567
  "name": "aria-expanded",
8560
8568
  "description": "Type: `string | null` \nDefault: `null`"
@@ -9982,7 +9990,7 @@
9982
9990
  },
9983
9991
  {
9984
9992
  "name": "lr-map",
9985
- "description": "`<lr-map>` — a maplibre-gl wrapper with a declarative legend, choropleth\nGeoJSON layer, markers, and additive `dataLayers` GeoJSON overlays\n(arbitrary shapes rendered as a source plus fill/line/circle layers,\nindependent of `choropleth`'s field/stops color-interpolation), plus a raw\n`map` escape hatch. Requires the optional peer dep `maplibre-gl`\n(consumers also import its CSS and configure its module-worker URL once).\nMapLibre v6 is ESM-only and requires WebGL2.\n\nThe underlying `maplibregl.Map` — and the WebGL context it opens — isn't\nconstructed until this element is first visible in the viewport (tracked\nvia `IntersectionObserver`), even once the `maplibre-gl` peer dependency\nhas finished loading. Browsers hard-cap concurrent WebGL contexts per\npage, so a grid/dashboard of many `<lr-map>` instances only constructs\nthe ones actually on-screen instead of racing to exhaust that budget the\ninstant each one mounts. `map` stays `undefined` (and `lr-map-load`\ndoesn't fire) until construction actually happens.\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The map wrapper. |\n| `container` | The maplibre container. |\n| `error` | `role=\"alert\"` message shown instead of `container` if the optional `maplibre-gl` peer dependency fails to load (e.g. not installed). ⚠️ The default `mapStyle` (when unset) uses OpenStreetMap's demo tile server, which is not suitable for production traffic — see the `DEFAULT_STYLE` doc comment above. Always pass an explicit `mapStyle` in production. |\n| `legend` | The map legend. |\n| `legend-swatch` | A legend color swatch. |",
9993
+ "description": "`<lr-map>` — a maplibre-gl wrapper with a declarative legend, choropleth\nGeoJSON layer, markers, and additive `dataLayers` GeoJSON overlays\n(arbitrary shapes rendered as a source plus fill/line/circle layers,\nindependent of `choropleth`'s field/stops color-interpolation), plus a raw\n`map` escape hatch. Requires the optional peer dep `maplibre-gl`\nv5 or v6 (consumers also import its CSS). MapLibre v6 is ESM-only, requires WebGL2, and needs\nits module-worker URL configured once; v5's standard build includes its worker.\n\nThe underlying `maplibregl.Map` — and the WebGL context it opens — isn't\nconstructed until this element is first visible in the viewport (tracked\nvia `IntersectionObserver`), even once the `maplibre-gl` peer dependency\nhas finished loading. Browsers hard-cap concurrent WebGL contexts per\npage, so a grid/dashboard of many `<lr-map>` instances only constructs\nthe ones actually on-screen instead of racing to exhaust that budget the\ninstant each one mounts. `map` stays `undefined` (and `lr-map-load`\ndoesn't fire) until construction actually happens.\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `base` | The map wrapper. |\n| `container` | The maplibre container. |\n| `error` | `role=\"alert\"` message shown instead of `container` if the optional `maplibre-gl` peer dependency fails to load (e.g. not installed). ⚠️ The default `mapStyle` (when unset) uses OpenStreetMap's demo tile server, which is not suitable for production traffic — see the `DEFAULT_STYLE` doc comment above. Always pass an explicit `mapStyle` in production. |\n| `legend` | The map legend. |\n| `legend-swatch` | A legend color swatch. |",
9986
9994
  "attributes": [
9987
9995
  {
9988
9996
  "name": "center",
@@ -10467,7 +10475,7 @@
10467
10475
  },
10468
10476
  {
10469
10477
  "name": "lr-menu",
10470
- "description": "`<lr-menu>` — an anchored dropdown of `<lr-menu-item>` actions, opened\nfrom a consumer-supplied trigger (typically an icon button). A close, drop-\nin-shaped replacement for reaching outside this library for a third-party\ndropdown to build a gear menu, an avatar menu, or a history row's overflow\nmenu: click the trigger, a positioned menu appears, clicking an item both\nperforms the action *and* closes the menu.\n\n**ARIA pattern — `role=\"menu\"`/`role=\"menuitem\"` with real roving DOM\nfocus, not a listbox.** Two coherent, mutually-exclusive shapes were\navailable here: (a) `role=\"listbox\"`/`role=\"option\"` with\n`aria-activedescendant`, the pattern `<lr-select>`'s trigger-button +\npopup listbox uses, where DOM focus never leaves the trigger; or (b)\n`role=\"menu\"`/`role=\"menuitem\"` with real focus moving between actual\nfocusable rows, the WAI-ARIA \"menu button\" pattern. This picks (b):\n`<lr-menu-item>` rows are real, independently-focusable elements (see\nthat class's own doc), which is the more natural fit for a menu\nspecifically — unlike a listbox's rows, a menu's rows are conventionally\nbutton-/link-shaped, and every well-known native/OS menu (and this\nfamily's own `<lr-tree>`/`<lr-tree-node>` pair, which this component's\nroving-tabindex plumbing directly mirrors) already moves real focus rather\nthan merely a virtual `aria-activedescendant` pointer. `role`/`tabIndex`\nare consistently the menu-button shape throughout — never mixed with\nlistbox/option.\n\nInteraction contract (mirrors the WAI-ARIA APG \"menu button\" pattern):\n- Click the trigger (or Enter/Space on it, via the trigger's own native\n `click` activation) toggles the menu, moving focus to the first\n non-disabled item on open.\n- ArrowDown/ArrowUp on the trigger while closed also open it, focusing the\n first/last non-disabled item respectively.\n- Once open, ArrowDown/ArrowUp move the roving focus among non-disabled\n items (wrapping past either end — the recommended, and more common,\n menu-widget behavior, unlike `<lr-select>`'s clamped listbox nav).\n Home/End jump to the first/last non-disabled item. Enter/Space activate\n the focused item. Escape closes and returns focus to the trigger. A\n printable keypress runs type-ahead: roving focus jumps to the next\n non-disabled item whose text starts with the accumulated buffer, cycling\n from just after the active item (mirrors `<lr-select>`'s identical\n listbox type-ahead). All of the above (except Escape and Tab) only\n respond to keydowns from a real `<lr-menu-item>` target, so a slotted\n non-item control (e.g. a date input) keeps its own full default keyboard\n behavior.\n- Tab never traps focus and never calls `preventDefault()` — the browser's\n own Tab navigation always proceeds untouched. It closes the menu only\n when focus is on its way *out* of the popup: with a focusable in the\n `header`/`footer` region on the far side of the keypress, the menu stays\n open so native Tab can carry focus there instead. With neither region\n filled, Tab closes exactly as it always has. Tabbing past the popup's\n last focusable in either direction closes it too, including from slotted\n non-item content — which previously left the menu open while focus\n walked away.\n- Escape from `header`/`footer` content closes the menu and refocuses the\n trigger unconditionally, mirroring `<lr-popover>`'s handling of arbitrary\n popup content. `closeOnEscapeAnywhere` governs only the *legacy* shape —\n non-item content slotted into the **default** slot — and defaults to\n `false`, so existing consumers keep today's behavior unchanged.\n- A click outside both the trigger and the open popup closes it (mirrors\n `<lr-select>`'s `onDocPointer` exactly) — this does *not* refocus the\n trigger, since the outside click itself already moved focus somewhere\n the user chose; Escape and a committed selection *do* refocus the\n trigger, since those are dismissals with nowhere else for focus to go.\n\n`show(focus?)` and `hide(options?)` are the public imperative pair, for the cases the trigger\ncan't express: a slotted \"Apply\"/\"Done\" button inside the menu, a keyboard shortcut, a parent\nrestoring UI state. `hide({ focusTrigger: true })` is the one that also returns DOM focus to the\ntrigger — use it whenever the interaction that closed the menu hasn't already put focus\nsomewhere the user chose. Writing `open` directly still works and is fully equivalent apart from\nthe focus moves: the roving-tabindex reset is centralized in `updated()`, so `el.open = false`\nnever leaves a stale `tabindex=\"0\"` tab stop on the last active item.\n\nThe trigger element itself is read from the `trigger` slot's assigned element (first one, if\nseveral are assigned) and enhanced imperatively with\n`aria-haspopup=\"menu\"`/`aria-expanded`/`aria-controls` — the same \"reach into a consumer-owned\nlight-DOM element to complete its a11y wiring\" approach `<lr-dialog>` documents for its own\nheading detection. `aria-controls` targets this menu host (which receives a stable generated\n`id` only when the consumer did not supply one), rather than a shadow-private popup id, so the\nrelationship is resolvable from the trigger's root. `<lr-button>` and `<lr-icon-button>` observe\nthose attributes, forward the popup/expanded values to their shadow-internal native controls,\nand resolve the controls element-reference onto that focused control.\n\nThe popup is always rendered (never `display:none`) so `.focus()` calls on\nits content work synchronously the instant it opens — visually hidden via\n`visibility`/`opacity` instead (identical to `<lr-select>`'s own\n`[part=\"listbox\"]`). `visibility` is an inherited CSS property that\npierces the `<slot>` projection boundary, so every closed-state\n`<lr-menu-item>` is automatically excluded from sequential (Tab-key)\nnavigation with no separate JS bookkeeping.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | `<lr-menu-item>` elements, plus optionally plain `<hr>` dividers between groups (native `<hr>` already carries an implicit `separator` role, matching what `role=\"menu\"` expects between item groups). Arbitrary non-item content still renders here for backward compatibility, but the `header`/`footer` slots below are the supported place for it. |\n| `footer` | Same as `header`, rendered below the items — an \"Apply\"/\"Done\" button, a link to a fuller settings page, a count. |\n| `header` | Composed content rendered above the items and *outside* the `role=\"menu\"` list — a filter/search field, a section title, a summary row. Keeps its own full default keyboard behavior, is reachable with Tab from the items, and is ARIA-valid (arbitrary content inside `role=\"menu\"` is not). Collapses to no box at all while unfilled. |\n| `trigger` | The consumer's own trigger element (typically an icon button). Clicking it toggles the menu; it's positioned against via `internal/positioner.js`'s `place()`. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `footer` | The wrapper around the `footer` slot, below the list and outside `role=\"menu\"`. `display: none` while the slot is unfilled. |\n| `header` | The wrapper around the `header` slot, above the list and outside `role=\"menu\"`. `display: none` while the slot is unfilled. |\n| `list` | The `role=\"menu\"` container wrapping the default slot. |\n| `popup` | The positioned floating panel. |\n| `trigger` | The wrapper around the `trigger` slot (the positioning anchor). |",
10478
+ "description": "`<lr-menu>` — an anchored dropdown of `<lr-menu-item>` actions, opened\nfrom a consumer-supplied trigger (typically an icon button). A close, drop-\nin-shaped replacement for reaching outside this library for a third-party\ndropdown to build a gear menu, an avatar menu, or a history row's overflow\nmenu: click the trigger, a positioned menu appears, clicking an item both\nperforms the action *and* closes the menu.\n\n**ARIA pattern — `role=\"menu\"`/`role=\"menuitem\"` with real roving DOM\nfocus, not a listbox.** Two coherent, mutually-exclusive shapes were\navailable here: (a) `role=\"listbox\"`/`role=\"option\"` with\n`aria-activedescendant`, the pattern `<lr-select>`'s trigger-button +\npopup listbox uses, where DOM focus never leaves the trigger; or (b)\n`role=\"menu\"`/`role=\"menuitem\"` with real focus moving between actual\nfocusable rows, the WAI-ARIA \"menu button\" pattern. This picks (b):\n`<lr-menu-item>` rows are real, independently-focusable elements (see\nthat class's own doc), which is the more natural fit for a menu\nspecifically — unlike a listbox's rows, a menu's rows are conventionally\nbutton-/link-shaped, and every well-known native/OS menu (and this\nfamily's own `<lr-tree>`/`<lr-tree-node>` pair, which this component's\nroving-tabindex plumbing directly mirrors) already moves real focus rather\nthan merely a virtual `aria-activedescendant` pointer. `role`/`tabIndex`\nare consistently the menu-button shape throughout — never mixed with\nlistbox/option.\n\nInteraction contract (mirrors the WAI-ARIA APG \"menu button\" pattern):\n- Click the trigger (or Enter/Space on it, via the trigger's own native\n `click` activation) toggles the menu, moving focus to the first\n non-disabled item on open.\n- ArrowDown/ArrowUp on the trigger while closed also open it, focusing the\n first/last non-disabled item respectively.\n- Once open, ArrowDown/ArrowUp move the roving focus among non-disabled\n items (wrapping past either end — the recommended, and more common,\n menu-widget behavior, unlike `<lr-select>`'s clamped listbox nav).\n Home/End jump to the first/last non-disabled item. Enter/Space activate\n the focused item. Escape closes and returns focus to the trigger. A\n printable keypress runs type-ahead: roving focus jumps to the next\n non-disabled item whose text starts with the accumulated buffer, cycling\n from just after the active item (mirrors `<lr-select>`'s identical\n listbox type-ahead). All of the above (except Escape and Tab) only\n respond to keydowns from a real `<lr-menu-item>` target, so a slotted\n non-item control (e.g. a date input) keeps its own full default keyboard\n behavior.\n- Tab never traps focus and never calls `preventDefault()` — the browser's\n own Tab navigation always proceeds untouched. It closes the menu only\n when focus is on its way *out* of the popup: with a focusable in the\n `header`/`footer` region on the far side of the keypress, the menu stays\n open so native Tab can carry focus there instead. With neither region\n filled, Tab closes exactly as it always has. Tabbing past the popup's\n last focusable in either direction closes it too, including from slotted\n non-item content — which previously left the menu open while focus\n walked away.\n- Escape from `header`/`footer` content closes the menu and refocuses the\n trigger unconditionally, mirroring `<lr-popover>`'s handling of arbitrary\n popup content. `closeOnEscapeAnywhere` governs only the *legacy* shape —\n non-item content slotted into the **default** slot — and defaults to\n `false`, so existing consumers keep today's behavior unchanged.\n- A click outside both the trigger and the open popup closes it (mirrors\n `<lr-select>`'s `onDocPointer` exactly) — this does *not* refocus the\n trigger, since the outside click itself already moved focus somewhere\n the user chose; Escape and a committed selection *do* refocus the\n trigger, since those are dismissals with nowhere else for focus to go.\n\n`show(focus?)` and `hide(options?)` are the public imperative pair, for the cases the trigger\ncan't express: a slotted \"Apply\"/\"Done\" button inside the menu, a keyboard shortcut, a parent\nrestoring UI state. `hide({ focusTrigger: true })` is the one that also returns DOM focus to the\ntrigger — use it whenever the interaction that closed the menu hasn't already put focus\nsomewhere the user chose. Writing `open` directly still works and is fully equivalent apart from\nthe focus moves: the roving-tabindex reset is centralized in `updated()`, so `el.open = false`\nnever leaves a stale `tabindex=\"0\"` tab stop on the last active item.\n\nThe trigger element itself is read from the `trigger` slot's assigned element (first one, if\nseveral are assigned) and enhanced imperatively with\n`aria-haspopup=\"menu\"`/`aria-expanded`/`aria-controls` — the same \"reach into a consumer-owned\nlight-DOM element to complete its a11y wiring\" approach `<lr-dialog>` documents for its own\nheading detection. `aria-controls` targets this menu host (which receives a stable generated\n`id` only when the consumer did not supply one), rather than a shadow-private popup id, so the\nrelationship is resolvable from the trigger's root. `<lr-button>` and `<lr-icon-button>` observe\nthose attributes, forward the popup/expanded values to their shadow-internal native controls,\nand resolve the controls element-reference onto that focused control. In supporting browsers,\nassigning the element reference intentionally clears the internal control's serialized\n`aria-controls` value; `ariaControlsElements` is the relationship's source of truth. Browsers\nwithout the reflected element-reference API retain the string as a best-effort fallback.\n\nThe popup is always rendered (never `display:none`) so `.focus()` calls on\nits content work synchronously the instant it opens — visually hidden via\n`visibility`/`opacity` instead (identical to `<lr-select>`'s own\n`[part=\"listbox\"]`). `visibility` is an inherited CSS property that\npierces the `<slot>` projection boundary, so every closed-state\n`<lr-menu-item>` is automatically excluded from sequential (Tab-key)\nnavigation with no separate JS bookkeeping.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | `<lr-menu-item>` elements, plus optionally plain `<hr>` dividers between groups (native `<hr>` already carries an implicit `separator` role, matching what `role=\"menu\"` expects between item groups). Arbitrary non-item content still renders here for backward compatibility, but the `header`/`footer` slots below are the supported place for it. |\n| `footer` | Same as `header`, rendered below the items — an \"Apply\"/\"Done\" button, a link to a fuller settings page, a count. |\n| `header` | Composed content rendered above the items and *outside* the `role=\"menu\"` list — a filter/search field, a section title, a summary row. Keeps its own full default keyboard behavior, is reachable with Tab from the items, and is ARIA-valid (arbitrary content inside `role=\"menu\"` is not). Collapses to no box at all while unfilled. |\n| `trigger` | The consumer's own trigger element (typically an icon button). Clicking it toggles the menu; it's positioned against via `internal/positioner.js`'s `place()`. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `footer` | The wrapper around the `footer` slot, below the list and outside `role=\"menu\"`. `display: none` while the slot is unfilled. |\n| `header` | The wrapper around the `header` slot, above the list and outside `role=\"menu\"`. `display: none` while the slot is unfilled. |\n| `list` | The `role=\"menu\"` container wrapping the default slot. |\n| `popup` | The positioned floating panel. |\n| `trigger` | The wrapper around the `trigger` slot (the positioning anchor). |",
10471
10479
  "attributes": [
10472
10480
  {
10473
10481
  "name": "close-on-escape-anywhere",
@@ -12378,7 +12386,7 @@
12378
12386
  },
12379
12387
  {
12380
12388
  "name": "lr-popover",
12381
- "description": "`<lr-popover>` — a click-triggered, light-dismiss floating surface.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Popover content. |\n| `trigger` | The interactive element that toggles the popover. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `content` | The content wrapper. |\n| `popup` | The positioned popup. |\n| `trigger` | The trigger wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-overlay-max-inline-size` — Maximum inline size of the popup (default `--lr-size-20rem`).",
12389
+ "description": "`<lr-popover>` — a click-triggered, light-dismiss floating surface.\n\nThe slotted trigger receives `aria-haspopup`, `aria-expanded`, and `aria-controls`.\n`aria-controls` targets this public host (which receives a stable generated `id` when the\nconsumer did not supply one), not the shadow-private popup. That keeps the relationship\nresolvable for native triggers and lets `<lr-button>`/`<lr-icon-button>` reflect the host onto\ntheir focused shadow-internal controls through `ariaControlsElements`.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Popover content. |\n| `trigger` | The interactive element that toggles the popover. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `content` | The content wrapper. |\n| `popup` | The positioned popup. |\n| `trigger` | The trigger wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-overlay-max-inline-size` — Maximum inline size of the popup (default `--lr-size-20rem`).",
12382
12390
  "attributes": [
12383
12391
  {
12384
12392
  "name": "aria-label",
@@ -17713,7 +17721,7 @@
17713
17721
  },
17714
17722
  {
17715
17723
  "name": "lr-tooltip",
17716
- "description": "`<lr-tooltip>` — a localized, hover/focus tooltip for a consumer-owned trigger.\nPlain content uses tooltip semantics. When the default slot contains an actionable descendant,\nthe popup promotes to a named dialog and remains open while pointer or focus is inside so its\ncontrols can be reached. Prefer `<lr-popover>` when click-to-open ownership is desired.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Tooltip content. |\n| `trigger` | The element that receives hover/focus listeners. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `popup` | The tooltip popup. |\n| `trigger` | The trigger wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-tooltip-max-inline-size` — Maximum inline size of the tooltip (default `--lr-size-20rem`).\n- `--lr-tooltip-background` — Tooltip background color (default `--lr-color-neutral`).\n- `--lr-tooltip-color` — Tooltip text color (default `--lr-color-on-neutral`).",
17724
+ "description": "`<lr-tooltip>` — a localized, hover/focus tooltip for a consumer-owned trigger.\nPlain content uses tooltip semantics. When the default slot contains an actionable descendant,\nthe popup promotes to a named dialog and remains open while pointer or focus is inside so its\ncontrols can be reached. Prefer `<lr-popover>` when click-to-open ownership is desired.\n\nWhile open, the trigger's `aria-describedby` targets a hidden text proxy in this component's\nlight DOM rather than the shadow-private popup. Native triggers can resolve that ID directly;\n`<lr-button>` and `<lr-icon-button>` reflect it onto their focused internal controls through\n`ariaDescribedByElements`, where the serialized internal attribute is intentionally empty.\n\n---\n\n**Slots**\n\n| Name | Description |\n| --- | --- |\n| (default) | Tooltip content. |\n| `trigger` | The element that receives hover/focus listeners. |\n\n---\n\n**CSS Shadow Parts**\n\n| Name | Description |\n| --- | --- |\n| `popup` | The tooltip popup. |\n| `trigger` | The trigger wrapper. |\n\n---\n\n**CSS Custom Properties**\n\n- `--lr-tooltip-max-inline-size` — Maximum inline size of the tooltip (default `--lr-size-20rem`).\n- `--lr-tooltip-background` — Tooltip background color (default `--lr-color-neutral`).\n- `--lr-tooltip-color` — Tooltip text color (default `--lr-color-on-neutral`).",
17717
17725
  "attributes": [
17718
17726
  {
17719
17727
  "name": "aria-label",
@@ -20056,6 +20064,10 @@
20056
20064
  "name": "--lr-icon-button-border",
20057
20065
  "description": "**`<lr-icon-button>`** (default: `0`) — Complete border shorthand of the native button."
20058
20066
  },
20067
+ {
20068
+ "name": "--lr-icon-button-border-hover",
20069
+ "description": "**`<lr-icon-button>`** (default: `var(--lr-icon-button-border, 0)`) — Complete border shorthand on hover."
20070
+ },
20059
20071
  {
20060
20072
  "name": "--lr-icon-button-color",
20061
20073
  "description": "**`<lr-icon-button>`** (default: `inherit`) — Icon/text color of the native button."