@lmvz-ds/components 0.36.0 → 0.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/cjs/aria.constants-DPK5mQEL.js +65 -0
  3. package/cjs/{stencil.util-TjS8_DoU.js → async.util-Dnl6c82X.js} +1 -13
  4. package/cjs/{directional-focus-controller-3uDoin45.js → directional-focus-controller-8Xd_7MHT.js} +1 -1
  5. package/cjs/{element-activation-controller-BWtUKzu2.js → element-activation-controller-DzTUocEO.js} +1 -1
  6. package/cjs/{element.util-CctmBvUY.js → element.util-BlhcwmfR.js} +2 -62
  7. package/cjs/lmvz-action-list.cjs.entry.js +3 -2
  8. package/cjs/lmvz-button-group.cjs.entry.js +2 -1
  9. package/cjs/lmvz-button_2.cjs.entry.js +3 -2
  10. package/cjs/lmvz-checkbox.cjs.entry.js +38 -4
  11. package/cjs/lmvz-components.cjs.js +1 -1
  12. package/cjs/lmvz-datepicker.cjs.entry.js +10 -3
  13. package/cjs/lmvz-header_2.cjs.entry.js +4 -3
  14. package/cjs/lmvz-input.cjs.entry.js +242 -0
  15. package/cjs/lmvz-menuitem.cjs.entry.js +3 -2
  16. package/cjs/lmvz-modal.cjs.entry.js +2 -1
  17. package/cjs/{lmvz-input_2.cjs.entry.js → lmvz-popover.cjs.entry.js} +24 -228
  18. package/cjs/lmvz-radio.cjs.entry.js +61 -3
  19. package/cjs/lmvz-select.cjs.entry.js +423 -16
  20. package/cjs/lmvz-snackbar.cjs.entry.js +1 -1
  21. package/cjs/lmvz-spinner.cjs.entry.js +1 -1
  22. package/cjs/lmvz-tab.cjs.entry.js +1 -1
  23. package/cjs/lmvz-tabs.cjs.entry.js +4 -3
  24. package/cjs/lmvz-toggle.cjs.entry.js +7 -3
  25. package/cjs/loader.cjs.js +1 -1
  26. package/cjs/stencil.util-CBrPLd-F.js +17 -0
  27. package/collection/components/lmvz-checkbox/lmvz-checkbox.css +4 -2
  28. package/collection/components/lmvz-checkbox/lmvz-checkbox.js +68 -8
  29. package/collection/components/lmvz-datepicker/lmvz-datepicker.css +0 -5
  30. package/collection/components/lmvz-datepicker/lmvz-datepicker.js +17 -3
  31. package/collection/components/lmvz-input/lmvz-input.css +1 -1
  32. package/collection/components/lmvz-input/lmvz-input.js +47 -12
  33. package/collection/components/lmvz-popover/lmvz-popover.css +1 -1
  34. package/collection/components/lmvz-popover/lmvz-popover.js +45 -5
  35. package/collection/components/lmvz-radio/lmvz-radio.js +77 -9
  36. package/collection/components/lmvz-select/lmvz-select.css +96 -34
  37. package/collection/components/lmvz-select/lmvz-select.js +412 -21
  38. package/collection/components/lmvz-snackbar/lmvz-snackbar.js +1 -1
  39. package/collection/components/lmvz-spinner/lmvz-spinner.js +1 -1
  40. package/collection/components/lmvz-tab/lmvz-tab.js +1 -1
  41. package/collection/components/lmvz-tabs/lmvz-tabs.js +1 -1
  42. package/collection/components/lmvz-toggle/lmvz-toggle.js +21 -1
  43. package/collection/integration/header-integration/header-integration.js +1 -1
  44. package/collection/styles/fragments/_focus-within.css +2 -2
  45. package/collection/utils/aria/listbox-controller.js +154 -0
  46. package/collection/utils/radio/radio-group-controller.js +17 -0
  47. package/components/lmvz-checkbox.js +1 -1
  48. package/components/lmvz-datepicker.js +1 -1
  49. package/components/lmvz-input.js +1 -1
  50. package/components/lmvz-popover.js +1 -1
  51. package/components/lmvz-radio.js +1 -1
  52. package/components/lmvz-select.js +1 -1
  53. package/components/lmvz-snackbar.js +1 -1
  54. package/components/lmvz-spinner.js +1 -1
  55. package/components/lmvz-tab.js +1 -1
  56. package/components/lmvz-tabs.js +1 -1
  57. package/components/lmvz-toggle.js +1 -1
  58. package/components/p-BApwCp0R.js +1 -0
  59. package/components/p-C5gRiLRR.js +1 -0
  60. package/esm/aria.constants-DIyiticz.js +62 -0
  61. package/esm/{stencil.util-C3QOYC8E.js → async.util-DZj20TV1.js} +1 -13
  62. package/esm/{directional-focus-controller-Bhu7RqYE.js → directional-focus-controller-QukTTWBE.js} +1 -1
  63. package/esm/{element-activation-controller-BuwyVq4A.js → element-activation-controller-dxJZKMag.js} +1 -1
  64. package/esm/{element.util-Bd9JyKJZ.js → element.util-3ydVsxUW.js} +2 -61
  65. package/esm/lmvz-action-list.entry.js +3 -2
  66. package/esm/lmvz-button-group.entry.js +2 -1
  67. package/esm/lmvz-button_2.entry.js +3 -2
  68. package/esm/lmvz-checkbox.entry.js +38 -4
  69. package/esm/lmvz-components.js +1 -1
  70. package/esm/lmvz-datepicker.entry.js +10 -3
  71. package/esm/lmvz-header_2.entry.js +4 -3
  72. package/esm/lmvz-input.entry.js +240 -0
  73. package/esm/lmvz-menuitem.entry.js +3 -2
  74. package/esm/lmvz-modal.entry.js +2 -1
  75. package/esm/{lmvz-input_2.entry.js → lmvz-popover.entry.js} +23 -226
  76. package/esm/lmvz-radio.entry.js +61 -3
  77. package/esm/lmvz-select.entry.js +423 -16
  78. package/esm/lmvz-snackbar.entry.js +1 -1
  79. package/esm/lmvz-spinner.entry.js +1 -1
  80. package/esm/lmvz-tab.entry.js +1 -1
  81. package/esm/lmvz-tabs.entry.js +4 -3
  82. package/esm/lmvz-toggle.entry.js +7 -3
  83. package/esm/loader.js +1 -1
  84. package/esm/stencil.util-D2-5rsN3.js +15 -0
  85. package/hydrate/index.js +625 -68
  86. package/hydrate/index.mjs +625 -68
  87. package/lmvz-components/lmvz-components.esm.js +1 -1
  88. package/lmvz-components/{p-544e3ccb.entry.js → p-0f65d001.entry.js} +1 -1
  89. package/lmvz-components/p-137b1fe1.entry.js +1 -0
  90. package/lmvz-components/p-26dd9684.entry.js +1 -0
  91. package/lmvz-components/p-36f44108.entry.js +1 -0
  92. package/lmvz-components/p-398b5cf4.entry.js +1 -0
  93. package/lmvz-components/p-3a396fc8.entry.js +1 -0
  94. package/lmvz-components/p-40716828.entry.js +1 -0
  95. package/lmvz-components/p-70f1e31a.entry.js +1 -0
  96. package/lmvz-components/p-71d06d1d.entry.js +1 -0
  97. package/lmvz-components/{p-d280d21a.entry.js → p-88661c50.entry.js} +1 -1
  98. package/lmvz-components/p-91eb71bb.entry.js +1 -0
  99. package/lmvz-components/p-BA3wizqg.js +1 -0
  100. package/lmvz-components/{p-GGkwaiT6.js → p-CHxiS3ja.js} +1 -1
  101. package/lmvz-components/{p-UhNgD4L3.js → p-CPSyM_e-.js} +1 -1
  102. package/lmvz-components/p-DIyiticz.js +1 -0
  103. package/lmvz-components/p-DZj20TV1.js +1 -0
  104. package/lmvz-components/p-a680a7b2.entry.js +1 -0
  105. package/lmvz-components/{p-d48203f9.entry.js → p-a6fe9b61.entry.js} +1 -1
  106. package/lmvz-components/{p-dbfbcb74.entry.js → p-a99acb02.entry.js} +1 -1
  107. package/lmvz-components/p-bh9cItsP.js +1 -0
  108. package/lmvz-components/p-f16a19fc.entry.js +1 -0
  109. package/lmvz-components/p-f3f0ed49.entry.js +1 -0
  110. package/lmvz-components/p-f516a949.entry.js +1 -0
  111. package/manifest.json +366 -30
  112. package/package.json +1 -1
  113. package/types/api/ds.types.d.ts +1 -0
  114. package/types/components/lmvz-checkbox/lmvz-checkbox.d.ts +9 -1
  115. package/types/components/lmvz-datepicker/lmvz-datepicker.d.ts +2 -0
  116. package/types/components/lmvz-input/lmvz-input.d.ts +3 -0
  117. package/types/components/lmvz-popover/lmvz-popover.d.ts +2 -0
  118. package/types/components/lmvz-radio/lmvz-radio.d.ts +12 -2
  119. package/types/components/lmvz-select/lmvz-select.d.ts +53 -4
  120. package/types/components/lmvz-toggle/lmvz-toggle.d.ts +1 -0
  121. package/types/components.d.ts +96 -12
  122. package/types/utils/aria/listbox-controller.d.ts +42 -0
  123. package/types/utils/radio/radio-group-controller.d.ts +3 -0
  124. package/components/p-CPuI0_r_.js +0 -1
  125. package/components/p-CWpl0WaZ.js +0 -1
  126. package/lmvz-components/p-63f6a596.entry.js +0 -1
  127. package/lmvz-components/p-8022c6dc.entry.js +0 -1
  128. package/lmvz-components/p-Bd9JyKJZ.js +0 -1
  129. package/lmvz-components/p-C3QOYC8E.js +0 -1
  130. package/lmvz-components/p-a611c110.entry.js +0 -1
  131. package/lmvz-components/p-b1bb4393.entry.js +0 -1
  132. package/lmvz-components/p-b50b1cb5.entry.js +0 -1
  133. package/lmvz-components/p-b932cf91.entry.js +0 -1
  134. package/lmvz-components/p-bff115d4.entry.js +0 -1
  135. package/lmvz-components/p-d9aede81.entry.js +0 -1
  136. package/lmvz-components/p-f0e28f3a.entry.js +0 -1
  137. package/lmvz-components/p-f853faae.entry.js +0 -1
  138. package/lmvz-components/p-fa2efc42.entry.js +0 -1
  139. package/lmvz-components/p-fe6f5bdc.entry.js +0 -1
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-07-08T09:00:08",
2
+ "timestamp": "2026-07-10T08:26:43",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.43.5",
@@ -857,7 +857,7 @@
857
857
  "filePath": "src/components/lmvz-checkbox/lmvz-checkbox.tsx",
858
858
  "encapsulation": "scoped",
859
859
  "tag": "lmvz-checkbox",
860
- "readme": "# lmvz-checkbox\n\nAccessible, form-associated checkbox built as a Stencil scoped web component. Participates in native HTML forms via `ElementInternals` and emits a `lmvzChange` event on toggle.\n\n## Usage\n\n### Basic\n\n```html\n<lmvz-checkbox label=\"Accept terms and conditions\"></lmvz-checkbox>\n\n<!-- With helper text -->\n<lmvz-checkbox\n label=\"Subscribe to newsletter\"\n helper-text=\"You can unsubscribe at any time.\"\n></lmvz-checkbox>\n\n<!-- Required with error state -->\n<lmvz-checkbox\n label=\"I agree to the privacy policy\"\n required\n error\n error-message=\"You must accept the privacy policy to continue.\"\n></lmvz-checkbox>\n```\n\n### Form integration\n\n`lmvz-checkbox` participates in native form submission via `ElementInternals`. Use `name` to set the field name and `value` to control the submitted value (defaults to `'on'`).\n\n```html\n<form id=\"signup-form\">\n <lmvz-checkbox\n name=\"terms\"\n label=\"Accept terms and conditions\"\n value=\"accepted\"\n required\n ></lmvz-checkbox>\n <button type=\"submit\">Submit</button>\n</form>\n```\n\n```js\ndocument.getElementById('signup-form').addEventListener('submit', (event) => {\n event.preventDefault();\n const data = new FormData(event.target);\n // data.get('terms') === 'accepted' when checked, null when unchecked\n console.log(data.get('terms'));\n});\n```\n\nTo associate the checkbox with a form outside its DOM subtree, pass the form element's `id` via the `form` attribute:\n\n```html\n<form id=\"external-form\">...</form>\n<lmvz-checkbox name=\"consent\" label=\"I consent\" form=\"external-form\"></lmvz-checkbox>\n```\n\n## Reflected Attributes\n\nThe following props are reflected as HTML attributes and can be used in CSS attribute selectors:\n\n| Prop | Attribute |\n|------|-----------|\n| `checked` | `checked` |\n| `disabled` | `disabled` |\n| `required` | `required` |\n| `error` | `error` |\n\n## CSS Custom Properties\n\nOverride these on the `:host` element to theme the component. All defaults reference LMVZ semantic or global design tokens.\n\n| Custom Property | Default | Purpose |\n|----------------|---------|---------|\n| `--checkbox-box-size` | `var(--lmvz-global-dimension-s18)` | Width and height of the checkbox box |\n| `--checkbox-border-radius` | `var(--lmvz-global-dimension-s4)` | Border radius of the checkbox box |\n| `--checkbox-border-color` | `var(--lmvz-semantic-color-border-default)` | Default (unchecked) border color |\n| `--checkbox-border-color-hover` | `var(--lmvz-semantic-color-border-hover)` | Border color on hover |\n| `--checkbox-border-color-checked` | `var(--lmvz-semantic-color-border-active)` | Border color when checked |\n| `--checkbox-border-color-error` | `var(--lmvz-semantic-color-status-on-danger)` | Border color in error state |\n| `--checkbox-focus-color` | `var(--lmvz-semantic-color-status-on-active)` | Focus ring color |\n| `--checkbox-checkmark-color` | `var(--lmvz-semantic-color-border-active)` | Checkmark SVG stroke color |\n| `--checkbox-label-color` | `var(--lmvz-semantic-color-on-surface-primary)` | Label text color (default and hover) |\n| `--checkbox-label-color-checked` | `var(--lmvz-semantic-color-status-on-active)` | Label text color when checked |\n| `--checkbox-wrapper-bg-hover` | `var(--lmvz-semantic-color-int-tertiary-hover)` | Pill background on hover |\n| `--checkbox-wrapper-bg-checked` | `var(--lmvz-semantic-color-status-active)` | Pill background when checked |\n| `--checkbox-ripple-bg` | `var(--lmvz-semantic-color-int-secondary-hover)` | Ripple dot color |\n| `--checkbox-helper-color` | `var(--lmvz-semantic-color-on-surface-secondary)` | Helper text color |\n| `--checkbox-error-color` | `var(--lmvz-semantic-color-status-on-danger)` | Error message text color |\n| `--checkbox-disabled-opacity` | `var(--lmvz-component-input-disabled-opacity)` | Opacity when disabled — see caveat below |\n\n## Known Caveats\n\n### Checkbox box size uses a generic dimension token (5.1)\n\n`--checkbox-box-size` defaults to `--lmvz-global-dimension-s18` (18 px). This is a global dimension token; a component-specific token (`component.checkbox.box-size`) is the recommended long-term solution. The effective rendered size is 18 px until that token exists.\n\n### Border-radius uses a generic dimension token (5.2)\n\n`--checkbox-border-radius` defaults to `--lmvz-global-dimension-s4` (4 px). This is a generic dimension token. If `component.checkbox.border-radius` is introduced, this property should be migrated to it.\n\n### Checked label contrast borderline (5.3)\n\nWhen checked, the label text color (`--lmvz-semantic-color-status-on-active`, `#0e7ab4`) against the checked wrapper background (`--lmvz-semantic-color-status-active`, `#f1f9fe`) produces approximately 4.4:1 contrast. This is marginally below the WCAG AA minimum of 4.5:1 for normal text. This was accepted as a design trade-off. Verify contrast against the actual rendered background before each accessibility audit.\n\n### Helper text font-weight inconsistency with `lmvz-input` (5.6)\n\nHelper text renders at `font-weight: 400` per the `body-sm` design token. `lmvz-input` uses `font-weight: 500` for its helper text — a pre-existing inconsistency in the design system. `lmvz-checkbox` intentionally follows the token-specified value. This should be resolved at the design system token level.\n\n### Disabled opacity token export bug (5.7)\n\n`--lmvz-component-input-disabled-opacity` resolves from `global.opacity.opacity-disabled`, which has a known Supernova export bug: the token value is stored as `\"40px\"` (dimension type) instead of `0.4` (unitless number). If disabled checkboxes render at full opacity or at an incorrect size, apply the following workaround at the consuming app level:\n\n```css\nlmvz-checkbox[disabled] {\n --checkbox-disabled-opacity: 0.4;\n}\n```\n\nThis token bug should be tracked and fixed in `packages/design-tokens`.\n",
860
+ "readme": "# lmvz-checkbox\n\nAccessible, form-associated checkbox built as a Stencil scoped web component. Participates in native HTML forms via `ElementInternals` and emits a `lmvzChange` event on toggle.\n\n## Usage\n\n### Basic\n\n```html\n<lmvz-checkbox label=\"Accept terms and conditions\"></lmvz-checkbox>\n\n<!-- With helper text -->\n<lmvz-checkbox\n label=\"Subscribe to newsletter\"\n helper-text=\"You can unsubscribe at any time.\"\n></lmvz-checkbox>\n\n<!-- Required with error state -->\n<lmvz-checkbox\n label=\"I agree to the privacy policy\"\n required\n error\n error-message=\"You must accept the privacy policy to continue.\"\n></lmvz-checkbox>\n```\n\n### Form integration\n\n`lmvz-checkbox` participates in native form submission via `ElementInternals`. Use `name` to set the field name and `value` to control the submitted value (defaults to `'on'`).\n\n```html\n<form id=\"signup-form\">\n <lmvz-checkbox\n name=\"terms\"\n label=\"Accept terms and conditions\"\n value=\"accepted\"\n required\n ></lmvz-checkbox>\n <button type=\"submit\">Submit</button>\n</form>\n```\n\n```js\ndocument.getElementById('signup-form').addEventListener('submit', (event) => {\n event.preventDefault();\n const data = new FormData(event.target);\n // data.get('terms') === 'accepted' when checked, null when unchecked\n console.log(data.get('terms'));\n});\n```\n\nTo associate the checkbox with a form outside its DOM subtree, pass the form element's `id` via the `form` attribute:\n\n```html\n<form id=\"external-form\">...</form>\n<lmvz-checkbox name=\"consent\" label=\"I consent\" form=\"external-form\"></lmvz-checkbox>\n```\n\nUse `checkValidity()`, `reportValidity()`, and `getValidationMessage()` for programmatic validation. The `error` prop can be set manually or will automatically reflect constraint validation state after a validation attempt; correspondingly, `aria-invalid` is set to `true` when `error=true` or when the real constraint validity becomes invalid, and is cleared once resolved.\n\n## Reflected Attributes\n\nThe following props are reflected as HTML attributes and can be used in CSS attribute selectors:\n\n| Prop | Attribute |\n|------|-----------|\n| `checked` | `checked` |\n| `disabled` | `disabled` |\n| `required` | `required` |\n| `error` | `error` |\n\n## CSS Custom Properties\n\nOverride these on the `:host` element to theme the component. All defaults reference LMVZ semantic or global design tokens.\n\n| Custom Property | Default | Purpose |\n|----------------|---------|---------|\n| `--checkbox-box-size` | `var(--lmvz-global-dimension-s18)` | Width and height of the checkbox box |\n| `--checkbox-border-radius` | `var(--lmvz-global-dimension-s4)` | Border radius of the checkbox box |\n| `--checkbox-border-color` | `var(--lmvz-semantic-color-border-default)` | Default (unchecked) border color |\n| `--checkbox-border-color-hover` | `var(--lmvz-semantic-color-border-hover)` | Border color on hover |\n| `--checkbox-border-color-checked` | `var(--lmvz-semantic-color-border-active)` | Border color when checked |\n| `--checkbox-border-color-error` | `var(--lmvz-semantic-color-status-on-danger)` | Border color in error state |\n| `--checkbox-focus-color` | `var(--lmvz-semantic-color-status-on-active)` | Focus ring color |\n| `--checkbox-checkmark-color` | `var(--lmvz-semantic-color-border-active)` | Checkmark SVG stroke color |\n| `--checkbox-label-color` | `var(--lmvz-semantic-color-on-surface-primary)` | Label text color (default and hover) |\n| `--checkbox-label-color-checked` | `var(--lmvz-semantic-color-status-on-active)` | Label text color when checked |\n| `--checkbox-wrapper-bg-hover` | `var(--lmvz-semantic-color-int-tertiary-hover)` | Pill background on hover |\n| `--checkbox-wrapper-bg-checked` | `var(--lmvz-semantic-color-status-active)` | Pill background when checked |\n| `--checkbox-ripple-bg` | `var(--lmvz-semantic-color-int-secondary-hover)` | Ripple dot color |\n| `--checkbox-helper-color` | `var(--lmvz-semantic-color-on-surface-secondary)` | Helper text color |\n| `--checkbox-error-color` | `var(--lmvz-semantic-color-status-on-danger)` | Error message text color |\n| `--checkbox-disabled-opacity` | `var(--lmvz-component-input-disabled-opacity)` | Opacity when disabled — see caveat below |\n\n## Known Caveats\n\n### Checkbox box size uses a generic dimension token (5.1)\n\n`--checkbox-box-size` defaults to `--lmvz-global-dimension-s18` (18 px). This is a global dimension token; a component-specific token (`component.checkbox.box-size`) is the recommended long-term solution. The effective rendered size is 18 px until that token exists.\n\n### Border-radius uses a generic dimension token (5.2)\n\n`--checkbox-border-radius` defaults to `--lmvz-global-dimension-s4` (4 px). This is a generic dimension token. If `component.checkbox.border-radius` is introduced, this property should be migrated to it.\n\n### Checked label contrast borderline (5.3)\n\nWhen checked, the label text color (`--lmvz-semantic-color-status-on-active`, `#0e7ab4`) against the checked wrapper background (`--lmvz-semantic-color-status-active`, `#f1f9fe`) produces approximately 4.4:1 contrast. This is marginally below the WCAG AA minimum of 4.5:1 for normal text. This was accepted as a design trade-off. Verify contrast against the actual rendered background before each accessibility audit.\n\n### Helper text font-weight inconsistency with `lmvz-input` (5.6)\n\nHelper text renders at `font-weight: 400` per the `body-sm` design token. `lmvz-input` uses `font-weight: 500` for its helper text — a pre-existing inconsistency in the design system. `lmvz-checkbox` intentionally follows the token-specified value. This should be resolved at the design system token level.\n\n### Disabled opacity token export bug (5.7)\n\n`--lmvz-component-input-disabled-opacity` resolves from `global.opacity.opacity-disabled`, which has a known Supernova export bug: the token value is stored as `\"40px\"` (dimension type) instead of `0.4` (unitless number). If disabled checkboxes render at full opacity or at an incorrect size, apply the following workaround at the consuming app level:\n\n```css\nlmvz-checkbox[disabled] {\n --checkbox-disabled-opacity: 0.4;\n}\n```\n\nThis token bug should be tracked and fixed in `packages/design-tokens`.\n",
861
861
  "docs": "Checkbox component with form association, ARIA validation, and accessible label support.",
862
862
  "docsTags": [
863
863
  {
@@ -972,7 +972,6 @@
972
972
  "text": "false"
973
973
  }
974
974
  ],
975
- "default": "false",
976
975
  "values": [
977
976
  {
978
977
  "type": "boolean"
@@ -980,8 +979,8 @@
980
979
  ],
981
980
  "optional": false,
982
981
  "required": false,
983
- "getter": false,
984
- "setter": false
982
+ "getter": true,
983
+ "setter": true
985
984
  },
986
985
  {
987
986
  "name": "errorMessage",
@@ -1094,7 +1093,7 @@
1094
1093
  },
1095
1094
  "mutable": false,
1096
1095
  "attr": "name",
1097
- "reflectToAttr": false,
1096
+ "reflectToAttr": true,
1098
1097
  "docs": "Name attribute for form submission via ElementInternals.",
1099
1098
  "docsTags": [],
1100
1099
  "values": [
@@ -1214,6 +1213,28 @@
1214
1213
  "docs": "Sets focus on the native checkbox input.",
1215
1214
  "docsTags": []
1216
1215
  },
1216
+ {
1217
+ "name": "getValidationMessage",
1218
+ "returns": {
1219
+ "type": "Promise<string>",
1220
+ "docs": ""
1221
+ },
1222
+ "complexType": {
1223
+ "signature": "() => Promise<string>",
1224
+ "parameters": [],
1225
+ "references": {
1226
+ "Promise": {
1227
+ "location": "global",
1228
+ "id": "global::Promise"
1229
+ }
1230
+ },
1231
+ "return": "Promise<string>"
1232
+ },
1233
+ "signature": "getValidationMessage() => Promise<string>",
1234
+ "parameters": [],
1235
+ "docs": "Returns the current validation message, if any.",
1236
+ "docsTags": []
1237
+ },
1217
1238
  {
1218
1239
  "name": "reportValidity",
1219
1240
  "returns": {
@@ -1253,7 +1274,13 @@
1253
1274
  "docsTags": []
1254
1275
  }
1255
1276
  ],
1256
- "listeners": [],
1277
+ "listeners": [
1278
+ {
1279
+ "event": "invalid",
1280
+ "capture": false,
1281
+ "passive": false
1282
+ }
1283
+ ],
1257
1284
  "styles": [],
1258
1285
  "slots": [],
1259
1286
  "parts": [],
@@ -1407,7 +1434,7 @@
1407
1434
  "filePath": "src/components/lmvz-datepicker/lmvz-datepicker.tsx",
1408
1435
  "encapsulation": "shadow",
1409
1436
  "tag": "lmvz-datepicker",
1410
- "readme": "# lmvz-datepicker\n\n`lmvz-datepicker` composes an `lmvz-input` text field with a decorative calendar icon and an `lmvz-popover` calendar panel into a single, form-associated date-picker control.\n\n## Breaking Change: Event Payload Migration\n\n**If you are upgrading from an earlier version, your integration code must change.**\n\nThe `lmvzChange` event payload changed from `Temporal.PlainDate` to a plain `IsoDateString` (`YYYY-MM-DD`):\n\n| Before | After |\n|--------|-------|\n| `event.detail.value` is a `Temporal.PlainDate` object | `event.detail.value` is a string like `\"2024-06-15\"` |\n| Consumers called `.toString()` or used Temporal APIs on the payload | Consumers now read `event.detail.value` directly as a plain string |\n| Angular: the value accessor invoked `.value.toString()` on the temporal object | Angular: the value accessor reads the string directly (no extraction needed) |\n\nIf your code uses `.value.toString()` or calls Temporal methods on the payload, update it to treat `value` as a plain ISO date string instead. The `temporal-polyfill` used internally is bundled into the component's build output — consumers do not need to import or provide it.\n\n## Form Association\n\nThe component is form-associated (`formAssociated: true`, `ElementInternals`). It participates in native `<form>` submission under the `name` attribute (submitted value is an `IsoDateString`), honors `required`/`min`/`max` constraints via `ElementInternals.setValidity` (`valueMissing`, `badInput`, `rangeUnderflow`/`rangeOverflow`), and supports `formResetCallback`/`formStateRestoreCallback` for form reset and browser autofill/bfcache restoration. \n\nUse `checkValidity()` and `reportValidity()` to validate or show validation UI (the component delegates `ElementInternals` validation to these methods explicitly — native form submission does not call them automatically).\n\n## Localization\n\nThe popover's accessible dialog label and the default fallback validation message are localized per the `locale` prop for de/en/fr/it, with regional variant fallback (e.g., `de-AT` falls back to `de`).\n\n## Accessibility Note\n\nThe composed `lmvz-input` text field correctly exposes and toggles `aria-expanded`, `aria-haspopup`, and `aria-controls` as the calendar opens and closes, ensuring assistive technology receives proper combobox-pattern semantics.\n",
1437
+ "readme": "# lmvz-datepicker\n\n`lmvz-datepicker` composes an `lmvz-input` text field with a decorative calendar icon and an `lmvz-popover` calendar panel into a single, form-associated date-picker control.\n\n## Breaking Change: Event Payload Migration\n\n**If you are upgrading from an earlier version, your integration code must change.**\n\nThe `lmvzChange` event payload changed from `Temporal.PlainDate` to a plain `IsoDateString` (`YYYY-MM-DD`):\n\n| Before | After |\n|--------|-------|\n| `event.detail.value` is a `Temporal.PlainDate` object | `event.detail.value` is a string like `\"2024-06-15\"` |\n| Consumers called `.toString()` or used Temporal APIs on the payload | Consumers now read `event.detail.value` directly as a plain string |\n| Angular: the value accessor invoked `.value.toString()` on the temporal object | Angular: the value accessor reads the string directly (no extraction needed) |\n\nIf your code uses `.value.toString()` or calls Temporal methods on the payload, update it to treat `value` as a plain ISO date string instead. The `temporal-polyfill` used internally is bundled into the component's build output — consumers do not need to import or provide it.\n\n## Form Association\n\nThe component is form-associated (`formAssociated: true`, `ElementInternals`). It participates in native `<form>` submission under the `name` attribute (submitted value is an `IsoDateString`), honors `required`/`min`/`max` constraints via `ElementInternals.setValidity` (`valueMissing`, `badInput`, `rangeUnderflow`/`rangeOverflow`), and supports `formResetCallback`/`formStateRestoreCallback` for form reset and browser autofill/bfcache restoration. \n\nUse `checkValidity()` and `reportValidity()` to validate or show validation UI (the component delegates `ElementInternals` validation to these methods explicitly — native form submission does not call them automatically). The composed text field's visual error state and `aria-invalid` are intentionally not shown until a real validation attempt occurs (via `checkValidity()`, `reportValidity()`, or a failed form submit), even though `ElementInternals.setValidity` itself is always kept accurate from the current value — the field can be internally invalid before it is visually flagged, by design.\n\n## Localization\n\nThe popover's accessible dialog label and the default fallback validation message are localized per the `locale` prop for de/en/fr/it, with regional variant fallback (e.g., `de-AT` falls back to `de`).\n\n## Accessibility Note\n\nThe composed `lmvz-input` text field correctly exposes and toggles `aria-expanded`, `aria-haspopup`, and `aria-controls` as the calendar opens and closes, ensuring assistive technology receives proper combobox-pattern semantics.\n",
1411
1438
  "docs": "`lmvz-datepicker` composes an `lmvz-input` text field with a decorative calendar icon and an `lmvz-popover` calendar panel into a single, form-associated date-picker control.",
1412
1439
  "docsTags": [
1413
1440
  {
@@ -2070,6 +2097,11 @@
2070
2097
  "event": "click",
2071
2098
  "capture": false,
2072
2099
  "passive": false
2100
+ },
2101
+ {
2102
+ "event": "invalid",
2103
+ "capture": false,
2104
+ "passive": false
2073
2105
  }
2074
2106
  ],
2075
2107
  "styles": [],
@@ -2533,7 +2565,7 @@
2533
2565
  "filePath": "src/components/lmvz-input/lmvz-input.tsx",
2534
2566
  "encapsulation": "scoped",
2535
2567
  "tag": "lmvz-input",
2536
- "readme": "# lmvz-input\n\n## Size Variants\n\nThe `size` prop controls the input's height, padding, and associated typography. Set `size` to one of `'sm'`, `'md'`, or `'lg'`. The default is `'md'`.\n\n| Size | Height | Notes |\n|------|--------|-------|\n| `sm` | ~36px | Compact variant with reduced padding |\n| `md` | ~44px | Default; balanced spacing and readability |\n| `lg` | ~48px | Expanded variant with generous padding |\n\nHeights are fluid and derive from design tokens; exact pixel values depend on user font size settings.\n",
2568
+ "readme": "# lmvz-input\n\n## Size Variants\n\nThe `size` prop controls the input's height, padding, and associated typography. Set `size` to one of `'sm'`, `'md'`, or `'lg'`. The default is `'md'`.\n\n| Size | Height | Notes |\n|------|--------|-------|\n| `sm` | ~36px | Compact variant with reduced padding |\n| `md` | ~44px | Default; balanced spacing and readability |\n| `lg` | ~48px | Expanded variant with generous padding |\n\nHeights are fluid and derive from design tokens; exact pixel values depend on user font size settings.\n\n## Error State and Validation\n\nThe `error` prop reflects native HTML5 validation state: set it manually to indicate an error, or let it automatically reflect the input's constraint validity following a validation attempt (e.g., after a blur with no prior keystroke, or after a failed `submit`, `checkValidity()`, or `reportValidity()` call). Correspondingly, `aria-invalid` is set to `true` when `error=true` or when the real constraint validity becomes invalid, and is cleared once resolved. Use `checkValidity()`, `reportValidity()`, and `getValidationMessage()` for programmatic validation.\n",
2537
2569
  "docs": "Input component with floating label, (form) validation, and slotted content support.",
2538
2570
  "docsTags": [
2539
2571
  {
@@ -6496,6 +6528,28 @@
6496
6528
  "docs": "Returns the native HTMLInputElement.\n\nPromise resolves when the element is ready and the input is available. If the input is not yet rendered, it waits for the component to be ready.",
6497
6529
  "docsTags": []
6498
6530
  },
6531
+ {
6532
+ "name": "getValidationMessage",
6533
+ "returns": {
6534
+ "type": "Promise<string>",
6535
+ "docs": ""
6536
+ },
6537
+ "complexType": {
6538
+ "signature": "() => Promise<string>",
6539
+ "parameters": [],
6540
+ "references": {
6541
+ "Promise": {
6542
+ "location": "global",
6543
+ "id": "global::Promise"
6544
+ }
6545
+ },
6546
+ "return": "Promise<string>"
6547
+ },
6548
+ "signature": "getValidationMessage() => Promise<string>",
6549
+ "parameters": [],
6550
+ "docs": "Returns the current validation message, if any.",
6551
+ "docsTags": []
6552
+ },
6499
6553
  {
6500
6554
  "name": "reportValidity",
6501
6555
  "returns": {
@@ -7322,7 +7376,7 @@
7322
7376
  "filePath": "src/components/lmvz-popover/lmvz-popover.tsx",
7323
7377
  "encapsulation": "shadow",
7324
7378
  "tag": "lmvz-popover",
7325
- "readme": "# lmvz-popover\n\nA non-modal floating dialog anchored to a trigger element. Positions automatically via `@floating-ui/dom` with collision avoidance (flip and shift middleware). Content-agnostic: accepts any slotted elements.\n\n## Usage\n\nAnchor the popover to a trigger element by setting the `anchor` prop to the trigger's `id` or element reference.\n\nIf no `id` is set on the popover host, one is auto-assigned at load time.\n\n```html\n<button\n id=\"trigger\"\n onclick=\"document.getElementById('my-popover').toggle()\"\n>\n Open menu\n</button>\n<lmvz-popover id=\"my-popover\" anchor=\"trigger\" label=\"Menu options\">\n <p>Popover content goes here</p>\n</lmvz-popover>\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `open` | `boolean` | `false` | Opens or closes the popover. |\n| `anchor` | `string \\| HTMLElement` | – | Anchor element id or reference for positioning. |\n| `placement` | `'top' \\| 'bottom' \\| 'start' \\| 'end' \\| 'top-start' \\| 'top-end' \\| 'bottom-start' \\| 'bottom-end'` | `'bottom-start'` | Preferred placement relative to the anchor. |\n| `size` | `'sm' \\| 'md' \\| 'lg'` | `'sm'` | Size variant of the card surface. |\n| `autoFocus` | `boolean` | `true` | Moves focus to the first focusable element when opened. |\n| `label` | `string` | – | Accessible label for the popover container (reflected on dialog role). |\n\n## Slots\n\n| Slot | Description |\n|---|---|\n| `default` | Popover content. Any elements may be slotted. |\n\n## Events\n\n| Event | Cancelable | Description |\n|---|---|---|\n| `lmvzOpen` | No | Fired after the popover opens. |\n| `lmvzClose` | No | Fired after the popover closes. |\n| `lmvzCancel` | **Yes** | Fired before light-dismiss (click outside) or Escape close. Call `event.preventDefault()` to veto the close and keep the popover open. |\n\n## Methods\n\n| Method | Returns | Description |\n|---|---|---|\n| `show()` | `Promise<void>` | Opens the popover. |\n| `hide()` | `Promise<void>` | Closes the popover after firing `lmvzCancel`. If `lmvzCancel` is prevented, the popover remains open. |\n| `toggle()` | `Promise<void>` | Toggles the open state. |\n\n## CSS Customization\n\n### Shadow Parts\n\n| Part | Description |\n|---|---|\n| `container` | The popover's dialog container. Target to customize positioning, overflow, or appearance. |\n\n### Custom Properties\n\n| Property | Description | Default |\n|---|---|---|\n| `--lmvz-popover-max-width` | Maximum width of the popover surface. | `none` |\n| `--lmvz-popover-max-height` | Maximum height of the popover surface. | `none` |\n| `--lmvz-popover-z-index` | Z-index of the popover surface. | `1` |\n\n## Accessibility\n\n### Trigger Requirements\n\nWhen you set the `anchor` prop, the popover automatically writes three ARIA attributes to the resolved anchor element for screen reader and assistive technology support:\n\n- **`aria-haspopup=\"dialog\"`** — Announces that the trigger opens a dialog.\n- **`aria-expanded`** — Reflects the popover's open state (`\"true\"` when open, `\"false\"` when closed).\n- **`aria-controls`** — Points to the popover's `id` to establish the relationship between trigger and content.\n\nDo not set these attributes manually. Exception: if you pre-set `aria-haspopup` to a different value (e.g. `\"menu\"` for an action-list popover), the component preserves your value.\n\n### Content Semantics\n\nThe component renders a `role=\"dialog\"` container. Provide a `label` prop or set `aria-labelledby` on the host element to satisfy dialog labeling requirements.\n\n### AriaValidation\n\nWhen `@lmvz-ds/aria-validation` is enabled (development only), the component validates:\n\n- **Host element:** Must have an accessible label via the `label` prop or `aria-labelledby` attribute.\n- **Anchor element:** Must be interactive and focusable (e.g., `<button>`, `<a href>`, elements with `tabindex`, or elements with an interactive ARIA role). Non-interactive anchors produce a dev-time warning.\n\nValidation is disabled in production builds and test runtimes.\n\n## Notes\n\n- The popover is non-modal: focus is not trapped, and interaction outside does not require acknowledgment.\n- Positioning is automatic with collision avoidance. Use `placement` to express preference, but the component will flip or shift as needed to keep content visible.\n- Light-dismiss (clicking outside the popover or pressing Escape) fires `lmvzCancel` first, allowing consumers to intercept and prevent close (e.g., to warn unsaved changes).\n\n## Out of Scope\n\nIntegration with sibling components (e.g., action-list, select, date-picker) is intentionally not implemented in the popover itself. These components integrate via the trigger element and slot content, and remain the responsibility of the consuming application.\n",
7379
+ "readme": "# lmvz-popover\n\nA non-modal floating dialog anchored to a trigger element. Positions automatically via `@floating-ui/dom` with collision avoidance (flip and shift middleware). Content-agnostic: accepts any slotted elements.\n\n## Usage\n\nAnchor the popover to a trigger element by setting the `anchor` prop to the trigger's `id` or element reference.\n\nIf no `id` is set on the popover host, one is auto-assigned at load time.\n\n```html\n<button\n id=\"trigger\"\n onclick=\"document.getElementById('my-popover').toggle()\"\n>\n Open menu\n</button>\n<lmvz-popover id=\"my-popover\" anchor=\"trigger\" label=\"Menu options\">\n <p>Popover content goes here</p>\n</lmvz-popover>\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `open` | `boolean` | `false` | Opens or closes the popover. |\n| `anchor` | `string \\| HTMLElement` | – | Anchor element id or reference for positioning. |\n| `placement` | `'top' \\| 'bottom' \\| 'start' \\| 'end' \\| 'top-start' \\| 'top-end' \\| 'bottom-start' \\| 'bottom-end'` | `'bottom-start'` | Preferred placement relative to the anchor. |\n| `size` | `'sm' \\| 'md' \\| 'lg'` | `'sm'` | Size variant of the card surface. |\n| `autoFocus` | `boolean` | `true` | Moves focus to the first focusable element when opened. |\n| `label` | `string` | – | Accessible label for the popover container (reflected on dialog role). |\n\n## Slots\n\n| Slot | Description |\n|---|---|\n| `default` | Popover content. Any elements may be slotted. |\n\n## Events\n\n| Event | Cancelable | Description |\n|---|---|---|\n| `lmvzOpen` | No | Fired after the popover opens. |\n| `lmvzClose` | No | Fired after the popover closes. |\n| `lmvzCancel` | **Yes** | Fired before light-dismiss (click outside) or Escape close. Call `event.preventDefault()` to veto the close and keep the popover open. |\n\n## Methods\n\n| Method | Returns | Description |\n|---|---|---|\n| `show()` | `Promise<void>` | Opens the popover. |\n| `hide()` | `Promise<void>` | Closes the popover after firing `lmvzCancel`. If `lmvzCancel` is prevented, the popover remains open. |\n| `toggle()` | `Promise<void>` | Toggles the open state. |\n\n## CSS Customization\n\n### Shadow Parts\n\n| Part | Description |\n|---|---|\n| `container` | The popover's dialog container. Target to customize positioning, overflow, or appearance. |\n\n### Custom Properties\n\n| Property | Description | Default |\n|---|---|---|\n| `--lmvz-popover-max-width` | Maximum width of the popover surface. | `none` |\n| `--lmvz-popover-max-height` | Maximum height of the popover surface. | `none` |\n| `--lmvz-popover-z-index` | Z-index of the popover surface. | `1` |\n\n## Accessibility\n\n### Trigger Requirements\n\nWhen you set the `anchor` prop, the popover automatically writes three ARIA attributes to the resolved anchor element for screen reader and assistive technology support:\n\n- **`aria-haspopup=\"dialog\"`** — Announces that the trigger opens a dialog.\n- **`aria-expanded`** — Reflects the popover's open state (`\"true\"` when open, `\"false\"` when closed).\n- **`aria-controls`** — Points to the popover's `id` to establish the relationship between trigger and content.\n\nDo not set these attributes manually. Exception: if you pre-set `aria-haspopup` to a different value (e.g. `\"menu\"` for an action-list popover), the component preserves your value.\n\n### Content Semantics\n\nThe component renders a `role=\"dialog\"` container. Provide a `label` prop or set `aria-labelledby` on the host element to satisfy dialog labeling requirements.\n\n### AriaValidation\n\nWhen `@lmvz-ds/aria-validation` is enabled (development only), the component validates:\n\n- **Host element:** Must have an accessible label via the `label` prop or `aria-labelledby` attribute.\n- **Anchor element:** Must be interactive and focusable (e.g., `<button>`, `<a href>`, elements with `tabindex`, or elements with an interactive ARIA role). Non-interactive anchors produce a dev-time warning.\n\nValidation is disabled in production builds and test runtimes.\n\n## Notes\n\n- The popover is non-modal: focus is not trapped, and interaction outside does not require acknowledgment.\n- Positioning is automatic with collision avoidance. Use `placement` to express preference, but the component will flip or shift as needed to keep content visible.\n- Light-dismiss (clicking outside the popover or pressing Escape) fires `lmvzCancel` first, allowing consumers to intercept and prevent close (e.g., to warn unsaved changes).\n\n## Integration Patterns\n\n### Combobox and Listbox (Active Descendant Model)\n\nThe popover supports integration with combobox and listbox components via the `containerRole` prop. When `containerRole` is set to a non-dialog value (e.g. `'listbox'`, `'menu'`, `'tree'`, or `'grid'`):\n\n- The popover container receives the specified ARIA role instead of `role=\"dialog\"`\n- Focus remains on the trigger/anchor element (active descendant pattern), so `autoFocus` has no effect\n- `aria-haspopup` on the anchor is automatically set to match the `containerRole` value\n\nThis enables tight integration with components like `lmvz-select`, which uses `containerRole=\"listbox\"` to implement the WAI-ARIA combobox pattern with a listbox popup. In this mode, the popover acts as a styled container while the trigger manages focus and active descendant state.\n\n## Out of Scope\n\nBroader component integration patterns (e.g., date-picker, action menu with complex internal logic) remain the responsibility of consuming applications. The popover provides the container and positioning mechanism; application-specific behavior flows stay at the consuming component level.\n",
7326
7380
  "docs": "A non-modal floating dialog anchored to a trigger element. Positions automatically via `@floating-ui/dom` with collision avoidance (flip and shift middleware). Content-agnostic: accepts any slotted elements.",
7327
7381
  "docsTags": [
7328
7382
  {
@@ -7377,7 +7431,7 @@
7377
7431
  "mutable": false,
7378
7432
  "attr": "auto-focus",
7379
7433
  "reflectToAttr": false,
7380
- "docs": "Whether to move focus to the first focusable element on open.",
7434
+ "docs": "Whether to move focus to the first focusable element on open.\nIgnored (treated as `false`) when `containerRole` is not `'dialog'`, since\nnon-dialog roles (e.g. `'listbox'` for combobox/activedescendant integrations)\nmust keep focus on the anchor/trigger element.",
7381
7435
  "docsTags": [
7382
7436
  {
7383
7437
  "name": "default",
@@ -7395,6 +7449,59 @@
7395
7449
  "getter": false,
7396
7450
  "setter": false
7397
7451
  },
7452
+ {
7453
+ "name": "containerRole",
7454
+ "type": "\"dialog\" | \"grid\" | \"listbox\" | \"menu\" | \"tree\"",
7455
+ "complexType": {
7456
+ "original": "Popover.ContainerRole",
7457
+ "resolved": "\"dialog\" | \"grid\" | \"listbox\" | \"menu\" | \"tree\"",
7458
+ "references": {
7459
+ "Popover": {
7460
+ "location": "import",
7461
+ "path": "../../api",
7462
+ "id": "src/api/index.d.ts::Popover",
7463
+ "referenceLocation": "Popover"
7464
+ }
7465
+ }
7466
+ },
7467
+ "mutable": false,
7468
+ "attr": "container-role",
7469
+ "reflectToAttr": false,
7470
+ "docs": "ARIA role applied to the popover container.\nUse `'dialog'` (default) for focus-moving dialog-style popovers.\nUse a non-dialog role such as `'listbox'` to integrate with a `role=\"combobox\"`\ntrigger using the activedescendant model, in which case focus is kept on the\nanchor/trigger element instead of being moved into the popover.",
7471
+ "docsTags": [
7472
+ {
7473
+ "name": "default",
7474
+ "text": "'dialog'"
7475
+ }
7476
+ ],
7477
+ "default": "'dialog'",
7478
+ "values": [
7479
+ {
7480
+ "value": "dialog",
7481
+ "type": "string"
7482
+ },
7483
+ {
7484
+ "value": "grid",
7485
+ "type": "string"
7486
+ },
7487
+ {
7488
+ "value": "listbox",
7489
+ "type": "string"
7490
+ },
7491
+ {
7492
+ "value": "menu",
7493
+ "type": "string"
7494
+ },
7495
+ {
7496
+ "value": "tree",
7497
+ "type": "string"
7498
+ }
7499
+ ],
7500
+ "optional": false,
7501
+ "required": false,
7502
+ "getter": false,
7503
+ "setter": false
7504
+ },
7398
7505
  {
7399
7506
  "name": "label",
7400
7507
  "type": "string | undefined",
@@ -7689,12 +7796,16 @@
7689
7796
  ],
7690
7797
  "states": [],
7691
7798
  "dependents": [
7692
- "lmvz-datepicker"
7799
+ "lmvz-datepicker",
7800
+ "lmvz-select"
7693
7801
  ],
7694
7802
  "dependencies": [],
7695
7803
  "dependencyGraph": {
7696
7804
  "lmvz-datepicker": [
7697
7805
  "lmvz-popover"
7806
+ ],
7807
+ "lmvz-select": [
7808
+ "lmvz-popover"
7698
7809
  ]
7699
7810
  }
7700
7811
  },
@@ -7702,7 +7813,7 @@
7702
7813
  "filePath": "src/components/lmvz-radio/lmvz-radio.tsx",
7703
7814
  "encapsulation": "scoped",
7704
7815
  "tag": "lmvz-radio",
7705
- "readme": "# lmvz-radio\n\nAccessible, form-associated radio button built as a Stencil scoped web component. Supports form association, native radio group mutual exclusion via `name` forwarding, roving tabindex, and ARIA APG-compliant keyboard navigation.\n\n## Usage\n\n### Basic\n\n```html\n<lmvz-radio name=\"choice\" value=\"a\" label=\"Option A\"></lmvz-radio>\n<lmvz-radio name=\"choice\" value=\"b\" label=\"Option B\"></lmvz-radio>\n```\n\n## Form Integration\n\n`lmvz-radio` is a FACE (Form-Associated Custom Element). It participates in native HTML forms via `ElementInternals`: the `name` and `value` attributes are submitted to the form when the radio is `checked`.\n\nSetting `required` triggers native form validation — the form will not submit unless a radio with the given `name` is checked.\n\n### Form Reset Behavior\n\nWhen a form is reset, `lmvz-radio` restores its `checked` state to the value it held when the component first mounted (the `initialChecked` value), without emitting `lmvzChange`. This matches native `<input type=\"radio\">` behavior. Internal form state (via `ElementInternals.setFormValue`) is always updated regardless. Browser autofill and session-restore operations (via `formStateRestoreCallback`) also do not emit `lmvzChange`.\n\n> **Note:** `aria-required` is intentionally absent from `lmvz-radio`. When a radio group is required, expose `aria-required` on the `role=\"radiogroup\"` wrapper element via your application code. This is the consumer's responsibility.\n\n## Radio Group Behavior\n\nWhen multiple `lmvz-radio` elements share the same `name` attribute and are within the same form (or document scope if outside any form), they form a native radio group. The `name` is forwarded directly to the native `<input type=\"radio\">`, enabling the browser's built-in mutual-exclusion logic: selecting one radio automatically deselects all others with the same name in the same scope. Radios with the same `name` in different forms are treated as independent groups.\n\n**Roving tabindex:** The component automatically manages keyboard focus navigation via the `RadioGroupController`. When a group of radios shares the same `name`:\n- The checked radio has `tabindex=\"0\"` and is reachable via Tab.\n- All other enabled radios have `tabindex=\"-1\"` and are not directly tabbable.\n- If no radio is checked, the first enabled radio in DOM order receives `tabindex=\"0\"`, ensuring the group is always reachable.\n\n**Keyboard navigation (manual selection pattern):**\n\n| Key | Action |\n|-----|--------|\n| Arrow Down / Arrow Right | Moves focus to the next radio in the group (no selection) |\n| Arrow Up / Arrow Left | Moves focus to the previous radio in the group (no selection) |\n| Home | Moves focus to the first radio in the group (no selection) |\n| End | Moves focus to the last radio in the group (no selection) |\n| Space | Selects the focused radio; emits `lmvzActivation` and (if state changed) `lmvzChange` |\n| Tab | Moves focus out of the radio group |\n\nDisabled radios are skipped during navigation.\n\nThis component implements the WAI-ARIA [manual selection](https://www.w3.org/WAI/ARIA/apg/patterns/radio/) pattern: focus and selection are decoupled. Arrow keys move focus without selecting. The consumer is responsible for wrapping grouped radios in a `role=\"radiogroup\"` element.\n\n**Consumer responsibility — `role=\"radiogroup\"`:**\nWrap a group of related `lmvz-radio` elements in a container element with `role=\"radiogroup\"` and an accessible label via `aria-label` or `aria-labelledby`. The radio component does not add this role itself because a radio button cannot also be a radiogroup.\n\n## Accessibility\n\n- A native `<input type=\"radio\">` is rendered inside the component for full keyboard and screen reader support.\n- `role=\"radiogroup\"` must be added by the consumer on a wrapper element (see \"Radio Group Behavior\" above).\n- `aria-invalid` is set when `error=true`.\n- `aria-describedby` links the input to helper text when `helper-text` is provided.\n- Helper text uses `aria-live=\"polite\"` for dynamic updates.\n- The focus ring is visible and meets WCAG 2.1 AA contrast requirements.\n- Forced Colors (Windows High Contrast Mode) is supported.\n\n## CSS Custom Properties\n\nOverride these on the `:host` element to theme the component. All defaults reference LMVZ semantic or global design tokens.\n\n| Property | Default | Description |\n|---|---|---|\n| `--radio-circle-size` | `var(--lmvz-global-s18)` | Width and height of the radio circle |\n| `--radio-dot-size` | `0.5rem` | Size of the inner selected dot |\n| `--radio-bg` | `var(--lmvz-semantic-color-surface-input-primary)` | Circle background (unchecked) |\n| `--radio-border-color` | `var(--lmvz-semantic-color-border-default)` | Circle border (default) |\n| `--radio-border-color-hover` | `var(--lmvz-semantic-color-border-hover)` | Circle border on hover |\n| `--radio-border-color-checked` | `var(--lmvz-semantic-color-border-active)` | Circle border when checked |\n| `--radio-border-color-error` | `var(--lmvz-semantic-color-status-on-danger)` | Circle border in error state |\n| `--radio-dot-color` | `var(--lmvz-semantic-color-border-active)` | Color of the inner dot |\n| `--radio-wrapper-bg-hover` | `var(--lmvz-semantic-color-int-tertiary-hover)` | Pill background on hover |\n| `--radio-wrapper-bg-active` | `var(--lmvz-semantic-color-int-tertiary-active)` | Pill background when pressed |\n| `--radio-wrapper-bg-checked` | `var(--lmvz-semantic-color-status-active)` | Pill background when checked |\n| `--radio-label-color` | `var(--lmvz-semantic-color-on-surface-primary)` | Label text color (default) |\n| `--radio-label-color-checked` | `var(--lmvz-semantic-color-status-on-active)` | Label text color when checked |\n| `--radio-helper-color` | `var(--lmvz-semantic-color-on-surface-secondary)` | Helper text color |\n| `--radio-focus-color` | `var(--lmvz-semantic-color-status-on-active)` | Focus ring color |\n| `--radio-easing` | `var(--lmvz-global-easing-default, ease)` | Transition easing function |\n| `--radio-duration` | `0.2s` | Transition duration |\n\n## lmvzChange Emission Contract\n\nThe `lmvzChange` event emits only when the radio's checked state changes. The event detail contains the new `checked` boolean value.\n\n**Fires on:**\n- User activation: clicking the radio or pressing Space (only if state changes)\n- Programmatic checked state transitions: setting `checked = true` or `checked = false`\n\n**Does NOT fire on:**\n- Arrow key navigation (Arrow Down, Arrow Up, Home, End). These move focus only, without selection.\n- Browser form lifecycle callbacks (`formResetCallback`, `formStateRestoreCallback`). This matches native `<input type=\"radio\">` behavior, which does not fire `change` on form reset. Internal form state (`ElementInternals.setFormValue`) is always updated regardless.\n\n## Angular Integration\n\n### Automatic Form Integration\n\nUse the `LmvzCheckableValueChangeHelper` directive (included in `@lmvz-ds/angular`) to automatically wire `lmvz-radio` to Angular form controls. When this directive is present on the element, it automatically binds the `lmvzChange` event to the form control's `valueChange` output:\n\n```html\n<!-- With [(ngModel)] -->\n<lmvz-radio\n label=\"Option A\"\n name=\"choice\"\n value=\"a\"\n [(ngModel)]=\"selectedValue\">\n</lmvz-radio>\n\n<!-- With [formControl] -->\n<lmvz-radio\n label=\"Option B\"\n name=\"choice\"\n value=\"b\"\n [formControl]=\"controlRef\">\n</lmvz-radio>\n```\n\nNo additional setup is required — the directive is applied automatically via the selector `lmvz-checkbox, lmvz-radio`.\n\n### Manual Event Binding\n\nIf you need direct event handling, listen to the `lmvzChange` event:\n\n```html\n<lmvz-radio\n label=\"Option\"\n name=\"choice\"\n [checked]=\"isChecked\"\n (lmvzChange)=\"isChecked = $event.target.checked\">\n</lmvz-radio>\n```\n\n## Public Methods\n\n### focusInput()\n\nProgrammatically set focus on the native radio input. Use this method when you need to move keyboard focus to a radio after a user interaction or page state change:\n\n```typescript\nconst radioEl = document.querySelector('lmvz-radio');\nawait radioEl.focusInput();\n```\n\n## Event Reference\n\n### lmvzActivation\n\nThe `lmvzActivation` event fires on every explicit user activation: click or Space on the native input. This includes re-activation of an already-checked radio. Use this event to react directly to user intent, distinct from state transitions.\n\n### lmvzChange vs lmvzActivation\n\n- **`lmvzActivation`:** Fires on every explicit user activation (click or Space), regardless of whether the checked state actually changes.\n- **`lmvzChange`:** Fires only on state transitions (checked ↔ unchecked).\n\nWhen a user selects a different radio in a group, both events fire together. When a user re-activates an already-checked radio, only `lmvzActivation` fires.\n\n## Design Notes / Waivers\n\n### Checked + hover state (OQ-5)\n\nWhen a radio is both checked and hovered, the checked background (`--radio-wrapper-bg-checked`) is held — the hover background does not override it. No Figma spec exists for this combined state; this behavior is an intentional implementation decision.\n\n### Label wrapping (OQ-3)\n\nThe label uses `overflow-wrap: break-word` (normal wrapping). `white-space: nowrap` is intentionally not applied.\n",
7816
+ "readme": "# lmvz-radio\n\nAccessible, form-associated radio button built as a Stencil scoped web component. Supports form association via `ElementInternals`, JS-driven radio group mutual exclusion, roving tabindex, and ARIA APG-compliant keyboard navigation.\n\n## Usage\n\n### Basic\n\n```html\n<lmvz-radio name=\"choice\" value=\"a\" label=\"Option A\"></lmvz-radio>\n<lmvz-radio name=\"choice\" value=\"b\" label=\"Option B\"></lmvz-radio>\n```\n\n## Form Integration\n\n`lmvz-radio` is a FACE (Form-Associated Custom Element). It participates in native HTML forms via `ElementInternals`: the `name` and `value` attributes are submitted to the form when the radio is `checked`.\n\nSetting `required` triggers native form validation — the form will not submit unless a radio with the given `name` is checked. For a required radio group (multiple radios with the same `name`), validity is group-aware: the group becomes invalid only when no radio in the group is checked, and valid as soon as any radio is selected. Use `getValidationMessage()` to retrieve the validation message.\n\n### Form Reset Behavior\n\nWhen a form is reset, `lmvz-radio` restores its `checked` state to the value it held when the component first mounted (the `initialChecked` value), without emitting `lmvzChange`. This matches native `<input type=\"radio\">` behavior. Internal form state (via `ElementInternals.setFormValue`) is always updated regardless. Browser autofill and session-restore operations (via `formStateRestoreCallback`) also do not emit `lmvzChange`.\n\n> **Note:** `aria-required` is intentionally absent from `lmvz-radio`. When a radio group is required, expose `aria-required` on the `role=\"radiogroup\"` wrapper element via your application code. This is the consumer's responsibility.\n\n## Radio Group Behavior\n\nWhen multiple `lmvz-radio` elements share the same `name` attribute and are within the same form (or document scope if outside any form), they form a radio group tracked by `RadioGroupController`. The `name` attribute is NOT forwarded to the native `<input type=\"radio\">` — because this component renders in `scoped` (light DOM) mode, doing so would make the inner `<input>` an independent, genuinely form-associated element of the outer `<form>`, submitting a duplicate value alongside the host's own `ElementInternals` participation. Instead, mutual exclusion is enforced entirely in JavaScript: selecting one radio calls `RadioGroup.select()`, which sets `checked = false` on every other host sharing the same name and scope. Radios with the same `name` in different forms are treated as independent groups.\n\n**Roving tabindex:** The component automatically manages keyboard focus navigation via the `RadioGroupController`. When a group of radios shares the same `name`:\n- The checked radio has `tabindex=\"0\"` and is reachable via Tab.\n- All other enabled radios have `tabindex=\"-1\"` and are not directly tabbable.\n- If no radio is checked, the first enabled radio in DOM order receives `tabindex=\"0\"`, ensuring the group is always reachable.\n\n**Keyboard navigation (manual selection pattern):**\n\n| Key | Action |\n|-----|--------|\n| Arrow Down / Arrow Right | Moves focus to the next radio in the group (no selection) |\n| Arrow Up / Arrow Left | Moves focus to the previous radio in the group (no selection) |\n| Home | Moves focus to the first radio in the group (no selection) |\n| End | Moves focus to the last radio in the group (no selection) |\n| Space | Selects the focused radio; emits `lmvzActivation` and (if state changed) `lmvzChange` |\n| Tab | Moves focus out of the radio group |\n\nDisabled radios are skipped during navigation.\n\nThis component implements the WAI-ARIA [manual selection](https://www.w3.org/WAI/ARIA/apg/patterns/radio/) pattern: focus and selection are decoupled. Arrow keys move focus without selecting. The consumer is responsible for wrapping grouped radios in a `role=\"radiogroup\"` element.\n\n**Consumer responsibility — `role=\"radiogroup\"`:**\nWrap a group of related `lmvz-radio` elements in a container element with `role=\"radiogroup\"` and an accessible label via `aria-label` or `aria-labelledby`. The radio component does not add this role itself because a radio button cannot also be a radiogroup.\n\n## Accessibility\n\n- A native `<input type=\"radio\">` is rendered inside the component for full keyboard and screen reader support.\n- `role=\"radiogroup\"` must be added by the consumer on a wrapper element (see \"Radio Group Behavior\" above).\n- `aria-invalid` is set when `error=true`, and is also automatically set when the radio's real constraint validity becomes invalid following a validation attempt (e.g., after a failed `submit`, `checkValidity()`, or `reportValidity()` call), clearing once resolved.\n- `aria-describedby` links the input to helper text when `helper-text` is provided.\n- Helper text uses `aria-live=\"polite\"` for dynamic updates.\n- The focus ring is visible and meets WCAG 2.1 AA contrast requirements.\n- Forced Colors (Windows High Contrast Mode) is supported.\n\n## CSS Custom Properties\n\nOverride these on the `:host` element to theme the component. All defaults reference LMVZ semantic or global design tokens.\n\n| Property | Default | Description |\n|---|---|---|\n| `--radio-circle-size` | `var(--lmvz-global-s18)` | Width and height of the radio circle |\n| `--radio-dot-size` | `0.5rem` | Size of the inner selected dot |\n| `--radio-bg` | `var(--lmvz-semantic-color-surface-input-primary)` | Circle background (unchecked) |\n| `--radio-border-color` | `var(--lmvz-semantic-color-border-default)` | Circle border (default) |\n| `--radio-border-color-hover` | `var(--lmvz-semantic-color-border-hover)` | Circle border on hover |\n| `--radio-border-color-checked` | `var(--lmvz-semantic-color-border-active)` | Circle border when checked |\n| `--radio-border-color-error` | `var(--lmvz-semantic-color-status-on-danger)` | Circle border in error state |\n| `--radio-dot-color` | `var(--lmvz-semantic-color-border-active)` | Color of the inner dot |\n| `--radio-wrapper-bg-hover` | `var(--lmvz-semantic-color-int-tertiary-hover)` | Pill background on hover |\n| `--radio-wrapper-bg-active` | `var(--lmvz-semantic-color-int-tertiary-active)` | Pill background when pressed |\n| `--radio-wrapper-bg-checked` | `var(--lmvz-semantic-color-status-active)` | Pill background when checked |\n| `--radio-label-color` | `var(--lmvz-semantic-color-on-surface-primary)` | Label text color (default) |\n| `--radio-label-color-checked` | `var(--lmvz-semantic-color-status-on-active)` | Label text color when checked |\n| `--radio-helper-color` | `var(--lmvz-semantic-color-on-surface-secondary)` | Helper text color |\n| `--radio-focus-color` | `var(--lmvz-semantic-color-status-on-active)` | Focus ring color |\n| `--radio-easing` | `var(--lmvz-global-easing-default, ease)` | Transition easing function |\n| `--radio-duration` | `0.2s` | Transition duration |\n\n## lmvzChange Emission Contract\n\nThe `lmvzChange` event emits only when the radio's checked state changes. The event detail contains the new `checked` boolean value.\n\n**Fires on:**\n- User activation: clicking the radio or pressing Space (only if state changes)\n- Programmatic checked state transitions: setting `checked = true` or `checked = false`\n\n**Does NOT fire on:**\n- Arrow key navigation (Arrow Down, Arrow Up, Home, End). These move focus only, without selection.\n- Browser form lifecycle callbacks (`formResetCallback`, `formStateRestoreCallback`). This matches native `<input type=\"radio\">` behavior, which does not fire `change` on form reset. Internal form state (`ElementInternals.setFormValue`) is always updated regardless.\n\n## Angular Integration\n\n### Automatic Form Integration\n\nUse the `LmvzCheckableValueChangeHelper` directive (included in `@lmvz-ds/angular`) to automatically wire `lmvz-radio` to Angular form controls. When this directive is present on the element, it automatically binds the `lmvzChange` event to the form control's `valueChange` output:\n\n```html\n<!-- With [(ngModel)] -->\n<lmvz-radio\n label=\"Option A\"\n name=\"choice\"\n value=\"a\"\n [(ngModel)]=\"selectedValue\">\n</lmvz-radio>\n\n<!-- With [formControl] -->\n<lmvz-radio\n label=\"Option B\"\n name=\"choice\"\n value=\"b\"\n [formControl]=\"controlRef\">\n</lmvz-radio>\n```\n\nNo additional setup is required — the directive is applied automatically via the selector `lmvz-checkbox, lmvz-radio`.\n\n### Manual Event Binding\n\nIf you need direct event handling, listen to the `lmvzChange` event:\n\n```html\n<lmvz-radio\n label=\"Option\"\n name=\"choice\"\n [checked]=\"isChecked\"\n (lmvzChange)=\"isChecked = $event.target.checked\">\n</lmvz-radio>\n```\n\n## Public Methods\n\n### focusInput()\n\nProgrammatically set focus on the native radio input. Use this method when you need to move keyboard focus to a radio after a user interaction or page state change:\n\n```typescript\nconst radioEl = document.querySelector('lmvz-radio');\nawait radioEl.focusInput();\n```\n\n## Event Reference\n\n### lmvzActivation\n\nThe `lmvzActivation` event fires on every explicit user activation: click or Space on the native input. This includes re-activation of an already-checked radio. Use this event to react directly to user intent, distinct from state transitions.\n\n### lmvzChange vs lmvzActivation\n\n- **`lmvzActivation`:** Fires on every explicit user activation (click or Space), regardless of whether the checked state actually changes.\n- **`lmvzChange`:** Fires only on state transitions (checked ↔ unchecked).\n\nWhen a user selects a different radio in a group, both events fire together. When a user re-activates an already-checked radio, only `lmvzActivation` fires.\n\n## Design Notes / Waivers\n\n### Checked + hover state (OQ-5)\n\nWhen a radio is both checked and hovered, the checked background (`--radio-wrapper-bg-checked`) is held — the hover background does not override it. No Figma spec exists for this combined state; this behavior is an intentional implementation decision.\n\n### Label wrapping (OQ-3)\n\nThe label uses `overflow-wrap: break-word` (normal wrapping). `white-space: nowrap` is intentionally not applied.\n",
7706
7817
  "docs": "Radio button component with form association, ARIA validation, accessible label support,\nand ARIA APG-compliant Arrow-key keyboard navigation.\n\n**Consumer responsibility — `role=\"radiogroup\"`:**\nWrap a group of `lmvz-radio` elements in a container element with `role=\"radiogroup\"` and\nan accessible label via `aria-label` or `aria-labelledby`. `lmvz-radio` must not carry\n`role=\"radiogroup\"` itself because a radio cannot also be a radiogroup.\n\n**Arrow-key navigation and roving tabindex:**\nWhen multiple `lmvz-radio` elements share the same `name` attribute, this component\nautomatically handles Arrow Down / Arrow Right (next), Arrow Up / Arrow Left (previous),\nHome (first), and End (last) navigation with wrapping. Disabled radios are skipped.\nThe checked radio has `tabindex=\"0\"`. When no radio is checked, the first enabled radio\nin DOM order receives `tabindex=\"0\"`, ensuring the group is always reachable by Tab.",
7707
7818
  "docsTags": [
7708
7819
  {
@@ -7817,7 +7928,6 @@
7817
7928
  "text": "false"
7818
7929
  }
7819
7930
  ],
7820
- "default": "false",
7821
7931
  "values": [
7822
7932
  {
7823
7933
  "type": "boolean"
@@ -7825,8 +7935,8 @@
7825
7935
  ],
7826
7936
  "optional": false,
7827
7937
  "required": false,
7828
- "getter": false,
7829
- "setter": false
7938
+ "getter": true,
7939
+ "setter": true
7830
7940
  },
7831
7941
  {
7832
7942
  "name": "form",
@@ -7913,12 +8023,12 @@
7913
8023
  },
7914
8024
  "mutable": false,
7915
8025
  "attr": "name",
7916
- "reflectToAttr": false,
7917
- "docs": "Name attribute for form submission via ElementInternals.\n\nWhen multiple `lmvz-radio` elements share the same `name` and form scope,\nthe `name` is forwarded directly to the native `<input type=\"radio\">`,\nenabling browser-native mutual exclusion: selecting one radio automatically\ndeselects all others with the same name in that form. If a radio is outside\nany form, its scope defaults to the document level — same-name radios\noutside forms form a document-scoped group. Radios with the same `name` in\ndifferent forms are independent groups.\n\nThe `RadioGroupController` automatically manages roving tabindex (only the\nchecked radio has `tabindex=\"0\"`) and keyboard navigation (Arrow keys, Home, End)\nwithin the form-scoped group.",
8026
+ "reflectToAttr": true,
8027
+ "docs": "Name attribute for form submission via ElementInternals.\n\nThe `name` is NOT forwarded to the native `<input type=\"radio\">` — this component\nrenders in `scoped` (light DOM) mode, so a native `name` attribute would make the\ninner `<input>` an independent, genuinely form-associated element of the outer\n`<form>`, submitting a duplicate value alongside the host's own `ElementInternals`\nparticipation. Instead, `name` is used purely as a JS-level grouping key: the\n`RadioGroupController` tracks all `lmvz-radio` hosts sharing the same `name` and form\nscope in an internal registry and enforces mutual exclusion itself (via\n`RadioGroup.select()`, which sets `checked = false` on every other host in the group).\nIf a radio is outside any form, its scope defaults to the document level — same-name\nradios outside forms form a document-scoped group. Radios with the same `name` in\ndifferent forms are independent groups.\n\nThe `RadioGroupController` also manages roving tabindex (only the checked radio has\n`tabindex=\"0\"`) and keyboard navigation (Arrow keys, Home, End) within the group.",
7918
8028
  "docsTags": [
7919
8029
  {
7920
8030
  "name": "remarks",
7921
- "text": "A value of `\"\"` (empty string) is forwarded to the native `<input>`, but\n`RadioGroupController` treats it as unnamed — no group coordination occurs between\nradios with `name=\"\"`."
8031
+ "text": "A value of `\"\"` (empty string) is treated as unnamed by `RadioGroupController` —\nno group coordination occurs between radios with `name=\"\"`."
7922
8032
  }
7923
8033
  ],
7924
8034
  "values": [
@@ -8038,6 +8148,28 @@
8038
8148
  "docs": "Sets focus on the native radio input.",
8039
8149
  "docsTags": []
8040
8150
  },
8151
+ {
8152
+ "name": "getValidationMessage",
8153
+ "returns": {
8154
+ "type": "Promise<string>",
8155
+ "docs": ""
8156
+ },
8157
+ "complexType": {
8158
+ "signature": "() => Promise<string>",
8159
+ "parameters": [],
8160
+ "references": {
8161
+ "Promise": {
8162
+ "location": "global",
8163
+ "id": "global::Promise"
8164
+ }
8165
+ },
8166
+ "return": "Promise<string>"
8167
+ },
8168
+ "signature": "getValidationMessage() => Promise<string>",
8169
+ "parameters": [],
8170
+ "docs": "Returns the current validation message, if any.",
8171
+ "docsTags": []
8172
+ },
8041
8173
  {
8042
8174
  "name": "reportValidity",
8043
8175
  "returns": {
@@ -8092,6 +8224,11 @@
8092
8224
  }
8093
8225
  ],
8094
8226
  "listeners": [
8227
+ {
8228
+ "event": "invalid",
8229
+ "capture": false,
8230
+ "passive": false
8231
+ },
8095
8232
  {
8096
8233
  "event": "keydown",
8097
8234
  "capture": false,
@@ -8110,8 +8247,8 @@
8110
8247
  "filePath": "src/components/lmvz-select/lmvz-select.tsx",
8111
8248
  "encapsulation": "scoped",
8112
8249
  "tag": "lmvz-select",
8113
- "readme": "# lmvz-select\n\n## Props\n\n### `size`\n\n- Type: `'small' | 'default' | 'large'`\n- Default: `'default'`\n\nControls the visual height variant of the select field. Use `small` for compact layouts, `default` for standard use, and `large` for emphasis.\n\n### `highlighted`\n\n- Type: `boolean`\n- Default: `false`\n\nIndicates that an active filter or selection applies (e.g., a filter chip is active). When `true`:\n- The host attribute `[highlighted]` is present for CSS styling (`:host([highlighted])`).\n- The host attribute `data-highlighted=\"true\"` is set for assistive-technology consumers.\n\nWhen `false`, `data-highlighted=\"false\"` is set. Note that `aria-pressed` and `aria-selected` are not used here; they conflict with native `<select>` semantics. The `data-highlighted` attribute provides the accessible contract.\n",
8114
- "docs": "Select component with floating label and pill-shaped trigger.\nWraps a native `<select>` element for full keyboard and form support.",
8250
+ "readme": "# lmvz-select\n\nSelect component with floating label and pill-shaped trigger that implements the WAI-ARIA \"combobox with listbox popup\" pattern.\n\n## What changed: Breaking rewrite from native `<select>` to custom WAI-ARIA combobox\n\nIn previous versions, `lmvz-select` wrapped a native `<select>` element and inherited its form behavior and platform-specific UI (including native mobile pickers on iOS/Android). As of this version, it is a fully custom WAI-ARIA combobox implemented using:\n\n- A `role=\"combobox\"` trigger button (your interaction target)\n- A `role=\"listbox\"` popup menu hosted in `lmvz-popover`\n- Focus management via `aria-activedescendant` (trigger keeps focus; the selected item is tracked via an aria attribute rather than DOM focus)\n- Form submission via `ElementInternals` (like `lmvz-input`, `lmvz-checkbox`, etc. in this design system)\n\n**What consumers must know:**\n\n- **No native mobile picker:** On iOS, Android, and other platforms, users see the custom popup instead of the OS-native picker. This is by design, to ensure consistent styling and behavior across platforms.\n- **Slot contract:** You pass native `<option>` and `<optgroup>` elements as children. The component translates them internally to `role=\"listbox\"` and `role=\"option\"` elements.\n- **`<option selected>` is meaningful:** If you do not set the `value` prop, the first `<option selected=\"selected\">` becomes the initial value.\n- **Validation is minimal:** The component uses native HTML form constraint validation (`ElementInternals.setValidity()`). When `required` is true and no value is set, the browser's default validation UI appears on form submit; the component does not provide custom validation message styling beyond `errorMessage` (which you control via props).\n- **Form submission:** Use the `name` attribute (set directly on the `<lmvz-select>` host) and the current `value` to participate in HTML form submission. Form reset correctly restores the initial value.\n\n## Slot contract\n\nThe default slot accepts only native `<option>` and `<optgroup>` elements. The component reads this markup once on mount and translates it into an internal listbox model, re-parsing whenever the slotted content changes structurally (options/optgroups added or removed) or an already-slotted option/optgroup is mutated in place (e.g. `.disabled`, `.selected`, `.value`, `.label`, or its text content), mirroring native `<select>` reactivity. The slotted markup is never rendered; it serves as a template.\n\n```html\n<lmvz-select label=\"Fruit\" name=\"fruit\">\n <option value=\"apple\">Apple</option>\n <optgroup label=\"Citrus\">\n <option value=\"orange\">Orange</option>\n <option value=\"lemon\">Lemon</option>\n </optgroup>\n <option value=\"banana\" selected>Banana</option>\n</lmvz-select>\n```\n\n## Form integration\n\n`lmvz-select` is a form-associated custom element (`formAssociated: true`). Set the `name` attribute on the host element, and the component participates in:\n\n- **Form submission:** The current `value` is submitted under the `name` key.\n- **Form reset:** Restores `value` to its initial state (the prop value at mount time).\n- **Validation:** Set `required` to enforce that a value is present. Use `checkValidity()`, `reportValidity()`, and `getValidationMessage()` methods for programmatic validation.\n\n## Props explained\n\n### `size`\n\n- Type: `'small' | 'default' | 'large'`\n- Default: `'default'`\n\nControls the visual height variant of the select trigger. Use `small` for compact layouts, `default` for standard use, and `large` for emphasis.\n\n### `highlighted`\n\n- Type: `boolean`\n- Default: `false`\n\nIndicates that an active filter or selection applies (e.g., a filter chip is active). When `true`:\n- The host attribute `[highlighted]` is present for CSS styling (`:host([highlighted])`).\n- The host attribute `data-highlighted=\"true\"` is set for assistive-technology consumers.\n\nWhen `false`, `data-highlighted=\"false\"` is set. The `data-highlighted` attribute (rather than `aria-pressed`) provides this accessible contract; `aria-selected` is reserved for the `role=\"option\"` elements in the listbox popup.\n\n### `error`\n\n- Type: `boolean`\n- Default: `false`\n- Reflected as host attribute: yes\n\nWhen `true`:\n- Applies danger-themed border color to the combobox trigger.\n- Sets `aria-invalid=\"true\"` on the trigger element.\n\n`aria-invalid` is also automatically set to `true` when the select's real constraint validity becomes invalid following a validation attempt (e.g., after a failed `submit`, `checkValidity()`, or `reportValidity()` call), and is cleared once the constraint is resolved. This allows automatic reflect of native HTML5 validation state independent of the `error` prop.\n\nTypically set alongside `errorMessage` to provide inline error text.\n\n### `errorMessage`\n\n- Type: `string | undefined`\n- Default: `undefined`\n\nError message text rendered below the select field when `error={true}`. Rendered as an assertive live region (`role=\"alert\"`), linked to the trigger via `aria-describedby` and `aria-errormessage`.\n\nCoexists with `helperText` — both can be displayed simultaneously, with `helperText` rendered first.\n",
8251
+ "docs": "Select component with floating label and pill-shaped trigger.\nImplements the WAI-ARIA APG \"combobox with listbox popup\" pattern: a `role=\"combobox\"`\ntrigger drives an `lmvz-popover`-hosted `role=\"listbox\"`/`role=\"option\"` list via the\nactivedescendant model (the trigger keeps real DOM focus; `aria-activedescendant` tracks\nthe highlighted option).\n\nParticipates in native HTML forms via `ElementInternals` (form-associated custom element):\nthe `name` attribute (set directly on the host element) and the current `value` are submitted,\nand the value resets to its initial value on form reset.",
8115
8252
  "docsTags": [
8116
8253
  {
8117
8254
  "name": "example",
@@ -8123,7 +8260,7 @@
8123
8260
  },
8124
8261
  {
8125
8262
  "name": "slot",
8126
- "text": "default - Native `<option>` or `<optgroup>` elements."
8263
+ "text": "default - Native `<option>` or `<optgroup>` elements. Never rendered directly; read once\nassigned to build the internal `role=\"option\"` listbox, and re-read on structural slot changes\nor in-place mutation (attribute/text/subtree) of already-slotted elements. `<option selected>`\ndetermines the initial value when the `value` prop is not set."
8127
8264
  }
8128
8265
  ],
8129
8266
  "usage": {},
@@ -8157,6 +8294,60 @@
8157
8294
  "getter": false,
8158
8295
  "setter": false
8159
8296
  },
8297
+ {
8298
+ "name": "error",
8299
+ "type": "boolean",
8300
+ "complexType": {
8301
+ "original": "boolean",
8302
+ "resolved": "boolean",
8303
+ "references": {}
8304
+ },
8305
+ "mutable": false,
8306
+ "attr": "error",
8307
+ "reflectToAttr": true,
8308
+ "docs": "Whether the select is in an error state.\nWhen not set manually, this will automatically reflect native HTML5 validation state",
8309
+ "docsTags": [
8310
+ {
8311
+ "name": "default",
8312
+ "text": "false"
8313
+ }
8314
+ ],
8315
+ "values": [
8316
+ {
8317
+ "type": "boolean"
8318
+ }
8319
+ ],
8320
+ "optional": false,
8321
+ "required": false,
8322
+ "getter": true,
8323
+ "setter": true
8324
+ },
8325
+ {
8326
+ "name": "errorMessage",
8327
+ "type": "string | undefined",
8328
+ "complexType": {
8329
+ "original": "string",
8330
+ "resolved": "string | undefined",
8331
+ "references": {}
8332
+ },
8333
+ "mutable": false,
8334
+ "attr": "error-message",
8335
+ "reflectToAttr": false,
8336
+ "docs": "Error message displayed below the select field when error is true.",
8337
+ "docsTags": [],
8338
+ "values": [
8339
+ {
8340
+ "type": "string"
8341
+ },
8342
+ {
8343
+ "type": "undefined"
8344
+ }
8345
+ ],
8346
+ "optional": true,
8347
+ "required": false,
8348
+ "getter": false,
8349
+ "setter": false
8350
+ },
8160
8351
  {
8161
8352
  "name": "helperText",
8162
8353
  "type": "string | undefined",
@@ -8194,7 +8385,7 @@
8194
8385
  "mutable": false,
8195
8386
  "attr": "highlighted",
8196
8387
  "reflectToAttr": true,
8197
- "docs": "Whether the select has an active filter or selection that should be visually\ndistinguished (e.g. a filter chip is applied).\n\nWhen `true`:\n- The host attribute `highlighted` is present (CSS: `:host([highlighted])`).\n- The host attribute `data-highlighted=\"true\"` is set for assistive-technology\n consumers that observe data attributes. When `false`, `data-highlighted` is\n set to `\"false\"`.\n\nNote: `aria-pressed` and `aria-selected` are intentionally not used here.\n`aria-pressed` is reserved for toggle-button roles; `aria-selected` is\nreserved for option/listbox roles. The `data-highlighted` attribute provides\nthe accessible contract without conflicting with native `<select>` semantics.",
8388
+ "docs": "Whether the select has an active filter or selection that should be visually\ndistinguished (e.g. a filter chip is applied).\n\nWhen `true`:\n- The host attribute `highlighted` is present (CSS: `:host([highlighted])`).\n- The host attribute `data-highlighted=\"true\"` is set for assistive-technology\n consumers that observe data attributes. When `false`, `data-highlighted` is\n set to `\"false\"`.\n\nNote: `aria-pressed` is intentionally not used here; it is reserved for toggle-button\nroles. `aria-selected` is reserved for this component's own `role=\"option\"` elements.\nThe `data-highlighted` attribute provides the accessible contract for this state.",
8198
8389
  "docsTags": [
8199
8390
  {
8200
8391
  "name": "default",
@@ -8245,8 +8436,8 @@
8245
8436
  },
8246
8437
  "mutable": false,
8247
8438
  "attr": "name",
8248
- "reflectToAttr": false,
8249
- "docs": "Name attribute passed to the native select for form submission.",
8439
+ "reflectToAttr": true,
8440
+ "docs": "Name attribute for form submission via ElementInternals.",
8250
8441
  "docsTags": [],
8251
8442
  "values": [
8252
8443
  {
@@ -8362,7 +8553,118 @@
8362
8553
  "setter": false
8363
8554
  }
8364
8555
  ],
8365
- "methods": [],
8556
+ "methods": [
8557
+ {
8558
+ "name": "blurTrigger",
8559
+ "returns": {
8560
+ "type": "Promise<void>",
8561
+ "docs": ""
8562
+ },
8563
+ "complexType": {
8564
+ "signature": "() => Promise<void>",
8565
+ "parameters": [],
8566
+ "references": {
8567
+ "Promise": {
8568
+ "location": "global",
8569
+ "id": "global::Promise"
8570
+ }
8571
+ },
8572
+ "return": "Promise<void>"
8573
+ },
8574
+ "signature": "blurTrigger() => Promise<void>",
8575
+ "parameters": [],
8576
+ "docs": "Removes focus from the combobox trigger.",
8577
+ "docsTags": []
8578
+ },
8579
+ {
8580
+ "name": "checkValidity",
8581
+ "returns": {
8582
+ "type": "Promise<boolean>",
8583
+ "docs": ""
8584
+ },
8585
+ "complexType": {
8586
+ "signature": "() => Promise<boolean>",
8587
+ "parameters": [],
8588
+ "references": {
8589
+ "Promise": {
8590
+ "location": "global",
8591
+ "id": "global::Promise"
8592
+ }
8593
+ },
8594
+ "return": "Promise<boolean>"
8595
+ },
8596
+ "signature": "checkValidity() => Promise<boolean>",
8597
+ "parameters": [],
8598
+ "docs": "Returns whether the select satisfies its validation constraints.",
8599
+ "docsTags": []
8600
+ },
8601
+ {
8602
+ "name": "focusTrigger",
8603
+ "returns": {
8604
+ "type": "Promise<void>",
8605
+ "docs": ""
8606
+ },
8607
+ "complexType": {
8608
+ "signature": "() => Promise<void>",
8609
+ "parameters": [],
8610
+ "references": {
8611
+ "Promise": {
8612
+ "location": "global",
8613
+ "id": "global::Promise"
8614
+ }
8615
+ },
8616
+ "return": "Promise<void>"
8617
+ },
8618
+ "signature": "focusTrigger() => Promise<void>",
8619
+ "parameters": [],
8620
+ "docs": "Sets focus on the combobox trigger.",
8621
+ "docsTags": []
8622
+ },
8623
+ {
8624
+ "name": "getValidationMessage",
8625
+ "returns": {
8626
+ "type": "Promise<string>",
8627
+ "docs": ""
8628
+ },
8629
+ "complexType": {
8630
+ "signature": "() => Promise<string>",
8631
+ "parameters": [],
8632
+ "references": {
8633
+ "Promise": {
8634
+ "location": "global",
8635
+ "id": "global::Promise"
8636
+ }
8637
+ },
8638
+ "return": "Promise<string>"
8639
+ },
8640
+ "signature": "getValidationMessage() => Promise<string>",
8641
+ "parameters": [],
8642
+ "docs": "Returns the current validation message, if any.",
8643
+ "docsTags": []
8644
+ },
8645
+ {
8646
+ "name": "reportValidity",
8647
+ "returns": {
8648
+ "type": "Promise<boolean>",
8649
+ "docs": ""
8650
+ },
8651
+ "complexType": {
8652
+ "signature": "() => Promise<boolean>",
8653
+ "parameters": [],
8654
+ "references": {
8655
+ "Promise": {
8656
+ "location": "global",
8657
+ "id": "global::Promise"
8658
+ }
8659
+ },
8660
+ "return": "Promise<boolean>"
8661
+ },
8662
+ "signature": "reportValidity() => Promise<boolean>",
8663
+ "parameters": [],
8664
+ "docs": "Reports validation errors to the user.",
8665
+ "docsTags": []
8666
+ }
8667
+ ],
8366
8668
  "events": [
8367
8669
  {
8368
8670
  "event": "lmvzChange",
@@ -8379,19 +8681,31 @@
8379
8681
  "docsTags": []
8380
8682
  }
8381
8683
  ],
8382
- "listeners": [],
8684
+ "listeners": [
8685
+ {
8686
+ "event": "invalid",
8687
+ "capture": false,
8688
+ "passive": false
8689
+ }
8690
+ ],
8383
8691
  "styles": [],
8384
8692
  "slots": [
8385
8693
  {
8386
8694
  "name": "default",
8387
- "docs": "Native `<option>` or `<optgroup>` elements."
8695
+ "docs": "Native `<option>` or `<optgroup>` elements. Never rendered directly; read once\nassigned to build the internal `role=\"option\"` listbox, and re-read on structural slot changes\nor in-place mutation (attribute/text/subtree) of already-slotted elements. `<option selected>`\ndetermines the initial value when the `value` prop is not set."
8388
8696
  }
8389
8697
  ],
8390
8698
  "parts": [],
8391
8699
  "states": [],
8392
8700
  "dependents": [],
8393
- "dependencies": [],
8394
- "dependencyGraph": {}
8701
+ "dependencies": [
8702
+ "lmvz-popover"
8703
+ ],
8704
+ "dependencyGraph": {
8705
+ "lmvz-select": [
8706
+ "lmvz-popover"
8707
+ ]
8708
+ }
8395
8709
  },
8396
8710
  {
8397
8711
  "filePath": "src/components/lmvz-snackbar/lmvz-snackbar.tsx",
@@ -9463,6 +9777,28 @@
9463
9777
  "docs": "Returns the native HTMLInputElement.\n\nPromise resolves when the element is ready and the input is available.",
9464
9778
  "docsTags": []
9465
9779
  },
9780
+ {
9781
+ "name": "getValidationMessage",
9782
+ "returns": {
9783
+ "type": "Promise<string>",
9784
+ "docs": ""
9785
+ },
9786
+ "complexType": {
9787
+ "signature": "() => Promise<string>",
9788
+ "parameters": [],
9789
+ "references": {
9790
+ "Promise": {
9791
+ "location": "global",
9792
+ "id": "global::Promise"
9793
+ }
9794
+ },
9795
+ "return": "Promise<string>"
9796
+ },
9797
+ "signature": "getValidationMessage() => Promise<string>",
9798
+ "parameters": [],
9799
+ "docs": "Returns the current validation message, if any.",
9800
+ "docsTags": []
9801
+ },
9466
9802
  {
9467
9803
  "name": "reportValidity",
9468
9804
  "returns": {