@reliability-design/web 1.4.6 → 1.4.8

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 (148) hide show
  1. package/CHANGELOG.md +246 -49
  2. package/dist/components/rel-accordion/README.md +133 -132
  3. package/dist/components/rel-alert/README.md +145 -141
  4. package/dist/components/rel-autocomplete/README.md +172 -61
  5. package/dist/components/rel-avatar/README.md +118 -130
  6. package/dist/components/rel-badge/README.md +97 -93
  7. package/dist/components/rel-breadcrumb/README.md +96 -102
  8. package/dist/components/rel-button/README.md +190 -130
  9. package/dist/components/rel-calendar/README.md +109 -109
  10. package/dist/components/rel-card/README.md +75 -94
  11. package/dist/components/rel-cardmedia/README.md +100 -127
  12. package/dist/components/rel-carousel/README.md +186 -166
  13. package/dist/components/rel-checkbox/README.md +96 -75
  14. package/dist/components/rel-chip/README.md +138 -121
  15. package/dist/components/rel-container/README.md +107 -99
  16. package/dist/components/rel-date-picker/README.md +200 -289
  17. package/dist/components/rel-divider/README.md +88 -100
  18. package/dist/components/rel-drawer/README.md +187 -200
  19. package/dist/components/rel-kbd/README.md +141 -151
  20. package/dist/components/rel-layout/README.md +34 -0
  21. package/dist/components/rel-menu/README.md +180 -151
  22. package/dist/components/rel-menubar/README.md +218 -329
  23. package/dist/components/rel-otp/README.md +171 -440
  24. package/dist/components/rel-pagination/README.md +100 -424
  25. package/dist/components/rel-popover/README.md +203 -568
  26. package/dist/components/rel-progress/README.md +88 -110
  27. package/dist/components/rel-radio/README.md +131 -138
  28. package/dist/components/rel-range-picker/README.md +165 -0
  29. package/dist/components/rel-resizable/README.md +199 -140
  30. package/dist/components/rel-segmented-button/README.md +156 -141
  31. package/dist/components/rel-select/README.md +156 -93
  32. package/dist/components/rel-sheet/README.md +168 -224
  33. package/dist/components/rel-skeleton/README.md +104 -0
  34. package/dist/components/rel-slider/README.md +131 -135
  35. package/dist/components/rel-snackbar/README.md +168 -134
  36. package/dist/components/rel-stepper/README.md +162 -160
  37. package/dist/components/rel-switch/README.md +117 -102
  38. package/dist/components/rel-tab/README.md +173 -163
  39. package/dist/components/rel-textfield/README.md +176 -69
  40. package/dist/components/rel-time-picker/README.md +122 -157
  41. package/dist/components/rel-toast/README.md +165 -148
  42. package/dist/components/rel-tooltip/README.md +119 -111
  43. package/dist/components/rel-upload/README.md +220 -0
  44. package/dist/index.d.mts +1726 -447
  45. package/dist/index.d.ts +1726 -447
  46. package/dist/index.js +13715 -10870
  47. package/dist/index.js.map +1 -1
  48. package/dist/index.mjs +14018 -11163
  49. package/dist/index.mjs.map +1 -1
  50. package/dist/styles/focus-ring.ts +84 -0
  51. package/dist/styles/vars.css +892 -866
  52. package/dist/{vars-XNMTSRVN.css → vars-DXX6QOAG.css} +892 -866
  53. package/package.json +3 -2
  54. package/dist/components/rel-accordion/rel-accordion.ts +0 -336
  55. package/dist/components/rel-alert/rel-alert.ts +0 -305
  56. package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -720
  57. package/dist/components/rel-avatar/rel-avatar.ts +0 -142
  58. package/dist/components/rel-badge/rel-badge.ts +0 -175
  59. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
  60. package/dist/components/rel-button/rel-button.ts +0 -560
  61. package/dist/components/rel-calendar/index.ts +0 -1
  62. package/dist/components/rel-calendar/rel-calendar.ts +0 -565
  63. package/dist/components/rel-card/rel-card.ts +0 -88
  64. package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
  65. package/dist/components/rel-carousel/index.ts +0 -1
  66. package/dist/components/rel-carousel/rel-carousel.ts +0 -723
  67. package/dist/components/rel-chart/README.md +0 -286
  68. package/dist/components/rel-chart/index.ts +0 -1
  69. package/dist/components/rel-chart/rel-chart.ts +0 -754
  70. package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
  71. package/dist/components/rel-chip/rel-chip.ts +0 -483
  72. package/dist/components/rel-container/rel-container.ts +0 -95
  73. package/dist/components/rel-date-picker/date-format.test.ts +0 -43
  74. package/dist/components/rel-date-picker/date-format.ts +0 -47
  75. package/dist/components/rel-date-picker/date-parse.test.ts +0 -116
  76. package/dist/components/rel-date-picker/date-parse.ts +0 -178
  77. package/dist/components/rel-date-picker/index.ts +0 -1
  78. package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1676
  79. package/dist/components/rel-divider/rel-divider.ts +0 -54
  80. package/dist/components/rel-drawer/index.ts +0 -1
  81. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
  82. package/dist/components/rel-drawer/rel-drawer.ts +0 -607
  83. package/dist/components/rel-kbd/rel-kbd.ts +0 -188
  84. package/dist/components/rel-layout/rel-layout.ts +0 -49
  85. package/dist/components/rel-menu/rel-menu.ts +0 -541
  86. package/dist/components/rel-menubar/index.ts +0 -2
  87. package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
  88. package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
  89. package/dist/components/rel-menubar/rel-menubar.ts +0 -777
  90. package/dist/components/rel-nav-menu/README.md +0 -289
  91. package/dist/components/rel-nav-menu/index.ts +0 -3
  92. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
  93. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
  94. package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
  95. package/dist/components/rel-navbar/README.md +0 -126
  96. package/dist/components/rel-navbar/rel-navbar.ts +0 -105
  97. package/dist/components/rel-otp/index.ts +0 -1
  98. package/dist/components/rel-otp/rel-otp.ts +0 -527
  99. package/dist/components/rel-pagination/index.ts +0 -1
  100. package/dist/components/rel-pagination/rel-pagination.ts +0 -522
  101. package/dist/components/rel-popover/index.ts +0 -2
  102. package/dist/components/rel-popover/rel-popover-content.ts +0 -81
  103. package/dist/components/rel-popover/rel-popover.ts +0 -475
  104. package/dist/components/rel-progress/rel-progress.ts +0 -234
  105. package/dist/components/rel-radio/rel-radio.ts +0 -481
  106. package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1688
  107. package/dist/components/rel-resizable/index.ts +0 -3
  108. package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
  109. package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
  110. package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
  111. package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -217
  112. package/dist/components/rel-select/rel-select.ts +0 -1416
  113. package/dist/components/rel-sheet/rel-sheet.ts +0 -610
  114. package/dist/components/rel-sidebar/README.md +0 -209
  115. package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1191
  116. package/dist/components/rel-skeleton/rel-skeleton.ts +0 -143
  117. package/dist/components/rel-slider/rel-slider.ts +0 -673
  118. package/dist/components/rel-snackbar/rel-snackbar.ts +0 -329
  119. package/dist/components/rel-stepper/rel-stepper.ts +0 -567
  120. package/dist/components/rel-switch/rel-switch.ts +0 -199
  121. package/dist/components/rel-tab/rel-tab.ts +0 -264
  122. package/dist/components/rel-table/README.md +0 -178
  123. package/dist/components/rel-table/rel-table-body.ts +0 -22
  124. package/dist/components/rel-table/rel-table-caption.ts +0 -30
  125. package/dist/components/rel-table/rel-table-cell.ts +0 -43
  126. package/dist/components/rel-table/rel-table-footer.ts +0 -24
  127. package/dist/components/rel-table/rel-table-head.ts +0 -47
  128. package/dist/components/rel-table/rel-table-header.ts +0 -22
  129. package/dist/components/rel-table/rel-table-row.ts +0 -37
  130. package/dist/components/rel-table/rel-table.ts +0 -62
  131. package/dist/components/rel-textfield/rel-textfield.ts +0 -1054
  132. package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1482
  133. package/dist/components/rel-time-picker/time-parse.test.ts +0 -67
  134. package/dist/components/rel-time-picker/time-parse.ts +0 -85
  135. package/dist/components/rel-toast/rel-toast.ts +0 -206
  136. package/dist/components/rel-tooltip/rel-tooltip.ts +0 -193
  137. package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
  138. package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
  139. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
  140. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
  141. package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
  142. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
  143. package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
  144. package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
  145. package/dist/components/rel-topbar/rel-topbar.ts +0 -446
  146. package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
  147. package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
  148. package/dist/components/rel-upload/rel-upload.ts +0 -968
@@ -1,122 +1,185 @@
1
1
  # rel-select
2
2
 
3
- ## Overview
4
- A custom combobox-style select component built on the same textfield visual language (sizes, states, border styles). Renders a typeable text trigger with a custom-styled dropdown listbox (no native `<select>`/`<option>` rendering), so the dropdown is fully themeable in both light and dark mode. Supports icons, multiple states, and sizes, with built-in keyboard navigation, type-to-filter, and an inline "ghost text" autocomplete suggestion.
3
+ `<rel-select>` is the combobox-style dropdown from the Figma "Dropdown" component: a text-field-styled trigger button that opens a custom listbox (never a native `<select>`, so it is fully themeable in light and dark mode). It supports single- and multi-select, an optional in-panel search field, a Select-all row in multi mode, and width-adaptive selection chips that collapse into a "+N" chip instead of wrapping. Use it to pick one or several values from a known list. For free text use `rel-textfield`; for type-ahead against a large or remote list use `rel-autocomplete`; for two or three mutually exclusive options `rel-radio` is usually clearer.
5
4
 
6
5
  ## Usage
6
+
7
7
  ```html
8
- <!-- Basic example -->
9
- <rel-select id="countrySelect" label="Country" selected="USA"></rel-select>
8
+ <!-- Single select -->
9
+ <rel-select id="country" label="Country" placeholder="Select a country"></rel-select>
10
10
  <script>
11
- const select = document.getElementById('countrySelect');
12
- select.options = ['USA', 'Canada', 'Mexico'];
11
+ const country = document.getElementById("country");
12
+ country.options = ["USA", "Canada", "Mexico"];
13
+ country.addEventListener("changed", (e) => console.log(e.detail.value)); // "Canada"
13
14
  </script>
14
15
 
15
- <!-- Example with common options -->
16
+ <!-- Object options: value / label / icon / supportingText (supportingText shows in multi-select rows only) -->
17
+ <script>
18
+ country.options = [
19
+ { value: "air", label: "Air freight", icon: "air" },
20
+ { value: "sea", label: "Sea freight", icon: "ship", supportingText: "4-6 weeks" },
21
+ ];
22
+ </script>
23
+
24
+ <!-- Preselected value, helper text, icons -->
25
+ <rel-select label="Email preference" selected="weekly" supporting-text="How often we email you" left-icon="mail"></rel-select>
26
+ <rel-select label="Custom chevron" right-icon="expand-more"></rel-select>
27
+
28
+ <!-- Sizes, corners, label styles -->
29
+ <rel-select label="Small" size="small"></rel-select>
30
+ <rel-select label="Large" size="large"></rel-select>
31
+ <rel-select label="Sharp corners" border="sharp"></rel-select>
32
+ <rel-select label="Floating label" label-style="floating"></rel-select>
33
+ <rel-select label-style="none" placeholder="Filter by status" field-aria-label="Filter by status"></rel-select>
34
+
35
+ <!-- States -->
36
+ <rel-select label="Region" state="error" supporting-text="Pick a region to continue"></rel-select>
37
+ <rel-select label="Locked" state="disabled" selected="USA"></rel-select>
38
+
39
+ <!-- Short list without a search field (Figma "Single Option" dropdown) -->
40
+ <rel-select label="Priority" no-search></rel-select>
41
+
42
+ <!-- Multi-select with chips, Select-all and a search placeholder -->
16
43
  <rel-select
17
- id="emailPreference"
18
- label="Email Preference"
19
- selected="Weekly"
20
- size="medium"
21
- state="default"
22
- supportingText="Choose how often you want to receive emails"
23
- leftIcon="📧">
44
+ id="tags"
45
+ label="Tags"
46
+ multiple
47
+ selected-values='["urgent","fragile"]'
48
+ select-all-label="Select all tags"
49
+ search-placeholder="Search tags"
50
+ max-visible-chips="3">
24
51
  </rel-select>
25
52
  <script>
26
- const emailPref = document.getElementById('emailPreference');
27
- emailPref.options = ['Daily', 'Weekly', 'Monthly', 'Never'];
53
+ const tags = document.getElementById("tags");
54
+ tags.options = ["urgent", "fragile", "hazmat", "oversize", "refrigerated"];
55
+ tags.addEventListener("changed", (e) => console.log(e.detail.value)); // ["urgent", "fragile", ...]
28
56
  </script>
29
57
  ```
30
58
 
59
+ <!-- readme-gen:start -->
60
+
61
+ <!-- Generated from rel-select.ts — do not edit; run readme-generate.mjs -->
62
+
31
63
  ## API
32
64
 
33
- ### Attributes/Properties
34
- | Name | Type | Default | Description |
35
- |------|------|---------|-------------|
36
- | `label` | `string` | `""` | Label text displayed above the select |
37
- | `label-style` | `"default" \| "floating" \| "none"` | `"default"` | Label presentation. `"default"` sits above the trigger, `"floating"` rests over the placeholder and animates onto the border line, `"none"` renders no label at all |
38
- | `field-aria-label` | `string` | - | Accessible name for the trigger. Required with `label-style="none"` |
39
- | `field-aria-labelledby` | `string` | - | IDREF list of external element(s) naming the trigger. Wins over `field-aria-label` |
40
- | `options` | `(string \| SelectOptionObject)[]` | `[]` | Options to display in the dropdown. Each entry is either a plain string or an object: `{ value, label?, supportingText?, icon? }`. `icon` is a `rel-icon` name rendered as a prefix (leading icon) on the menu item — before the label in single-select rows, and between the checkbox and label in multi-select rows |
41
- | `selected` | `string` | `""` | Currently selected option value |
42
- | `state` | `"default" \| "error" \| "disabled"` | `"default"` | Visual and interaction state of the select |
43
- | `supportingText` | `string` | `""` | Helper or error text displayed below the select |
44
- | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Size variant affecting padding and font size |
45
- | `border` | `"rounded" \| "sharp"` | `"rounded"` | Border radius style |
46
- | `leftIcon` | `string` | `""` | `rel-icon` name displayed on the left side |
47
- | `rightIcon` | `string` | `""` | `rel-icon` name to override the default right-side icon. When unset, a chevron is shown that rotates 180° on open; a custom `rightIcon` replaces the chevron entirely and does not rotate |
48
- | `placeholder` | `string` | `"Select option"` | Placeholder text shown when nothing is selected |
65
+ ### Attributes / Properties
66
+
67
+ | Property | Attribute | Type | Default | Description |
68
+ | --- | --- | --- | --- | --- |
69
+ | `label` | `label` | `string` | `""` | Visible label. Rendered above the trigger (`labelStyle="default"`) or floating inside it; omitted under `labelStyle="none"`. |
70
+ | `labelStyle` | `label-style` | `"default" \| "floating" \| "none"` | `"default"` | Label presentation: `default` static label above the trigger; `floating` label rests in the value area and floats onto the border when focused, open or filled; `none` renders no label (supply `fieldAriaLabel` / `fieldAriaLabelledby`). Reflected. |
71
+ | `fieldAriaLabel` | `field-aria-label` | `string` | `undefined` | Accessible name for the trigger. Required when `labelStyle="none"`, since no `<label>` renders and a placeholder is not a valid naming mechanism. |
72
+ | `fieldAriaLabelledby` | `field-aria-labelledby` | `string` | `undefined` | IDREF list of external element(s) naming the trigger. Wins over `fieldAriaLabel`. |
73
+ | `options` | `options` | `(string \| SelectOptionObject)[]` | `[]` | Options to list. Each entry is a plain string (used as both value and label) or `{ value, label?, supportingText?, icon? }`; `icon` is a `rel-icon` name rendered before the label, `supportingText` renders as a second line in both single- and multi-select rows. NOTE — the panel's search matches the LABEL ONLY. `supportingText` is displayed but not searched, so an option is not findable by its second line here. This differs deliberately from `rel-autocomplete`, whose filter matches the supporting line as well: there the second line carries identifying data (a DOT or MC number) that a user types to find the row, whereas a select's is descriptive ("Read-only", "Can edit, not delete") and matching it would return rows whose visible label has nothing to do with the query. |
74
+ | `multiple` | `multiple` | `boolean` | `false` | Enables multi-select: selected values render as removable chips, options as checkboxes, and a Select-all row appears in the panel. |
75
+ | `selected` | `selected` | `string` | `""` | Selected option value in single-select mode. Ignored when `multiple` is set. |
76
+ | `selectedValues` | `selected-values` | `string[]` | `[]` | Selected option values in multi-select mode. Reset to `[]` when `multiple` is turned off. |
77
+ | `state` | `state` | `"default" \| "error" \| "disabled"` | `"default"` | Visual / interaction state. `error` colours the border and supporting text and clears itself on the next selection; `disabled` blocks interaction and dims the whole field. |
78
+ | `supportingText` | `supporting-text` | `string` | `""` | Helper line below the trigger; coloured as an error while `state="error"`. |
79
+ | `supportingIcon` | `supporting-icon` | `string` | `""` | `rel-icon` name shown before the supporting text (16px, 14px at `small`). Optional: the design pairs the helper line with an icon, but existing fields without one keep their current single-line look. |
80
+ | `size` | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Field height — `small` 32px, `medium` 40px, `large` 48px. The whole component rides this one axis: label and supporting type, trigger padding, icons (20 / 18 / 16), selection chips, and the panel's rows, search field and checkboxes all scale with it. Reflected. |
81
+ | `border` | `border` | `"rounded" \| "sharp"` | `"rounded"` | Trigger corners: `rounded` applies the corner radius, `sharp` renders square corners. |
82
+ | `leftIcon` | `left-icon` | `string` | `""` | `rel-icon` name rendered at the left edge of the trigger. |
83
+ | `rightIcon` | `right-icon` | `string` | `""` | `rel-icon` name replacing the default chevron at the right edge of the trigger. A custom icon does not rotate when the panel opens. |
84
+ | `placeholder` | `placeholder` | `string` | `"Select option"` | Text shown in the trigger while nothing is selected. |
85
+ | `searchPlaceholder` | `search-placeholder` | `string` | `"Search option"` | Placeholder of the in-panel search field (the field is hidden when `noSearch` is set). |
86
+ | `selectAllLabel` | `select-all-label` | `string` | `"Select all"` | Label of the Select-all row shown at the top of the panel in multi-select mode. |
87
+ | `maxVisibleChips` | `max-visible-chips` | `number` | `Infinity` | Optional hard cap on visible chips. By default (no cap) the visible count adapts to the trigger's width: chips collapse into the "+N" badge one by one instead of wrapping to a second line. |
88
+ | `noSearch` | `no-search` | `boolean` | `false` | DELIBERATE DIVERGENCE — do not default this to `!multiple`. Figma's `.Menu Dropdown` shows the search row only on `Type=Multioption`; its `Type=Single Option` panels contain five Select Option items and nothing else. Here the search row renders for single-select too unless this is set. Ruled by the user: our behaviour stands and the DESIGN FILE follows our docs. A long single-select list is unusable without search, and making the default depend on `multiple` would change the panel of every existing single-select. |
49
89
 
50
90
  ## Events
51
- | Event | Detail | Description |
52
- |-------|--------|-------------|
53
- | `changed` | `{ value: string }` | Fired when the selected value changes |
54
91
 
55
- ## Slots
56
- Default slot not used.
57
-
58
- ## CSS Parts
59
- None exposed.
92
+ | Event | Detail | Bubbles / composed | Description |
93
+ | --- | --- | --- | --- |
94
+ | `changed` | `{ value: this.multiple ? this.selectedValues : this.selected }` | bubbles / composed | Fired after any selection change (pick, checkbox toggle, Select-all, chip removal). Detail: `{ value }` — the selected value string in single mode, or the array of selected values when `multiple`. |
60
95
 
61
- ## Tokens / CSS Variables
96
+ ## Slots
62
97
 
63
- ### Semantic Tokens
64
- - `--rel-fonts-font-family` - Font family
65
- - `--rel-body-xs-regular-font-size`, `--rel-body-sm-regular-font-size`, `--rel-body-md-regular-font-size` - Font sizes for different size variants
66
- - `--rel-fonts-weight-body-medium` - Font weight for label
98
+ | Slot | Description |
99
+ | --- | --- |
100
+ | — | This element has no slots. |
67
101
 
68
- ### Spacing Tokens
69
- - `--rel-spacing-3xs`, `--rel-spacing-2xs`, `--rel-spacing-xs` - Internal padding
70
- - `--rel-spacing-sm`, `--rel-spacing-md`, `--rel-spacing-xl`, `--rel-spacing-2xl` - Padding and icon positioning
102
+ ## CSS Parts
71
103
 
72
- ### Color Tokens
73
- - `--rel-text-label` - Label and selected-value text color
74
- - `--rel-text-default` - Icon color, focus border/shadow color
75
- - `--rel-text-placeholder` - Placeholder, supporting text, and ghost-suggestion text color
76
- - `--rel-text-disabled` - Disabled text/icon color
77
- - `--rel-border-default` - Default border color
78
- - `--rel-bg-surface` - Background color (trigger and dropdown panel)
79
- - `--rel-bg-neutral-elevated-1` - Disabled background, hovered/highlighted option background
80
- - `--rel-bg-danger` - Error state border, text, and icon color (light: `#D50000`, dark: `#FF5252`)
81
- - `--rel-shadows-shadow-200` - Dropdown panel shadow
104
+ | Part | Description |
105
+ | --- | --- |
106
+ | `label` | The visible label above the trigger (default label style) |
107
+ | `trigger` | The button that opens the panel; styled like a text field |
108
+ | `value` | The value area inside the trigger (selected label, placeholder or chips) |
109
+ | `helper` | The supporting / error text below the trigger |
110
+ | `panel` | The dropdown listbox container (same node as `list`) |
111
+ | `list` | Alias of `panel` |
112
+ | `input` | The search input inside the panel |
113
+ | `option` | A single-select option row |
114
+ | `option-label` | A single-select row's first line (the option label) |
115
+ | `option-supporting-text` | A single-select row's optional second line; rendered only when the option supplies `supportingText` |
82
116
 
83
- ### Border Tokens
84
- - `--rel-border-radius-xs` - Rounded border radius (default `border="rounded"`)
117
+ ## Tokens / CSS Variables
85
118
 
86
- ### Layout Tokens
87
- - `--rel-field-label-reserve` - Height held for the absent label row under `label-style="none"`, so the trigger bottom-aligns with labelled siblings. Defaults to the label line-height plus the wrapper gap; set to `0` to collapse the row entirely in compact contexts (toolbars, table cells)
119
+ ### Component hooks
120
+
121
+ Set these on the host (or an ancestor) to customise the element without touching design tokens.
122
+
123
+ | Custom property | Default | Description |
124
+ | --- | --- | --- |
125
+ | `--rel-field-label-reserve` | `calc(var(--rel-body-md-regular-line-height) + var(--rel-spacing-2xs))` | Height held for the label row whenever no label renders above the trigger — `labelStyle="none"`, and `labelStyle="floating"` where the label sits inside the trigger — so the trigger still lines up with labelled siblings in a form row. Defaults to that size's label line-height plus the wrapper gap (24px at `small`, 26px at `medium` and `large`). Set to `0` to collapse the row entirely (toolbars, table cells). |
126
+ | `--rel-select-bg` | `var(--rel-bg-neutral)` | Background of the trigger, the floating label, the panel and its sticky header |
127
+ | `--rel-select-bg-disabled` | `var(--rel-bg-neutral-elevated-2)` | Trigger background when `state="disabled"` |
128
+ | `--rel-select-border-default` | `var(--rel-border-default)` | Trigger border colour at rest |
129
+ | `--rel-select-border-disabled` | `var(--rel-border-accent)` | Trigger border colour when `state="disabled"` |
130
+ | `--rel-select-border-error` | `var(--rel-border-danger)` | Trigger border colour when `state="error"` |
131
+ | `--rel-select-border-focus` | `var(--rel-border-strong)` | Trigger border colour on hover, while focused (where it also thickens to 2px) and while the panel is open |
132
+ | `--rel-select-divider` | `var(--rel-border-subtle)` | Hairline under the search field, the Select-all row and every option row |
133
+ | `--rel-select-icon-color` | `var(--rel-text-default)` | Colour of the trigger icons, the search icon and option prefix icons |
134
+ | `--rel-select-label-color` | `var(--rel-text-label)` | Colour of the static label and of the floated label |
135
+ | `--rel-select-menu-border` | `var(--rel-border-default)` | Panel border colour. The panel is borderless by design and only draws this on dark surfaces, where its shadow alone can't separate it from the page. |
136
+ | `--rel-select-menu-radius` | `var(--rel-border-radius-sm)` | Panel corner radius |
137
+ | `--rel-select-menu-shadow` | `var(--rel-shadows-md)` | Panel box-shadow |
138
+ | `--rel-select-option-hover` | `var(--rel-action-overlay-hover)` | Background of the hovered / keyboard-highlighted / selected option and Select-all row |
139
+ | `--rel-select-option-pressed` | `var(--rel-action-overlay-pressed)` | Background of an option row while it is being pressed |
140
+ | `--rel-select-placeholder-color` | `var(--rel-text-placeholder)` | Placeholder colour (trigger, resting floating label, search field, empty-list message) |
141
+ | `--rel-select-placeholder-disabled-color` | `var(--rel-text-caption)` | Placeholder colour when disabled |
142
+ | `--rel-select-radius` | `var(--rel-border-radius-xs)` | Trigger corner radius when `border="rounded"` |
143
+ | `--rel-select-search-border` | `var(--rel-border-default)` | Border colour of the search field inside the panel |
144
+ | `--rel-select-supporting-color` | `var(--rel-text-body)` | Supporting text colour (non-error) |
145
+ | `--rel-select-value-color` | `var(--rel-text-default)` | Colour of the selected value, the search text, option labels and the check mark |
146
+
147
+ <!-- readme-gen:end -->
88
148
 
89
149
  ## Accessibility
90
- - Trigger input uses `role="combobox"` with `aria-haspopup="listbox"`, `aria-expanded`, and `aria-controls`
91
- - Dropdown panel uses `role="listbox"`; each option uses `role="option"` with `aria-selected`
92
- - `aria-activedescendant` tracks the keyboard-highlighted option
93
- - Label properly associated with the trigger via `for`/`id`
94
- - Disabled state prevents interaction and reduces opacity
95
- - Error state communicated through color and supporting text
96
-
97
- ### Keyboard Interactions
98
- - Typing - Filters the dropdown to matching options and shows an inline "ghost text" suggestion for the best prefix match
99
- - `→ (Arrow Right)` - When the cursor is at the end of the input, accepts the current ghost-text suggestion
100
- - `Enter` - Opens the dropdown (if closed); if open, selects the highlighted option, or commits the typed text if it exactly matches an option
101
- - `Arrow Up/Down` - Open dropdown (if closed) or move the highlighted option
102
- - `Home/End` - Jump to first/last filtered option (when open)
103
- - `Escape` - Revert input text to the committed value and close the dropdown
104
- - `Tab` - Accept any ghost suggestion, commit or revert the typed text, close the dropdown, and move focus to the next element
105
-
106
- ### Focus Behavior
107
- - Clear focus ring with primary color via `box-shadow` on the container (`:focus-within`)
108
- - Trigger input is a normal editable text field with a visible caret
150
+
151
+ - The trigger is a native `<button role="combobox">` with `aria-haspopup="listbox"`, `aria-expanded`, `aria-controls` (the listbox id) and `aria-invalid` (true in the error state). Its accessible name is composed as `"{label}, {selected label}"` in single mode and `"{label}, a, b, c, plus N more"` in multi mode (first three labels), so the current selection is always announced with the name. `field-aria-labelledby` replaces that computed name entirely.
152
+ - The static label (`label-style="default"`) is a `<label for>` linked to the trigger. The floating label is a visual `<span>`; the trigger's `aria-label` still carries the name. With `label-style="none"` the name comes from `field-aria-label` and a console warning is logged once if neither `field-aria-label` nor `field-aria-labelledby` is set.
153
+ - The panel is `role="listbox"` with `aria-multiselectable` and an `aria-label` of the field name (or "Options"). Every row is `role="option"` with `aria-selected`; multi-select rows and the Select-all row carry an explicit `aria-label` and contain an `inert`, `aria-hidden` `rel-checkbox` used purely as the visual.
154
+ - The in-panel search input is itself a `role="combobox"` with `aria-expanded`, `aria-controls` and `aria-activedescendant` tracking the highlighted row. With `no-search` the trigger keeps focus while open and carries `aria-activedescendant` instead.
155
+ - Screen-reader announcements via the shared `announce()` helper: `"{label} selected"` / `"{label} deselected"` on a checkbox toggle, `"All options selected"` / `"All options deselected"`, `"{label} removed"` when a chip is removed, and `"N options available"` / `"No matches found"` / `"No options available"` while typing in the search field.
156
+ - Focus: opening the panel moves focus to the search input (with `preventScroll`) unless `no-search`; closing always returns focus to the trigger. Chips render inside the trigger `<button>`, so their remove buttons are not Tab stops — Backspace / Delete is the keyboard path.
157
+
158
+ | Key | Action |
159
+ |-----|--------|
160
+ | `ArrowDown` / `ArrowUp` / `Enter` / `Space` (closed) | Open the panel |
161
+ | `Backspace` / `Delete` (closed, `multiple`) | Remove the most recently selected chip |
162
+ | `ArrowDown` / `ArrowUp` (open) | Move the highlight; in multi mode the Select-all row is a virtual first row above the options |
163
+ | `Home` / `End` (open) | Jump to the first / last filtered option |
164
+ | `Enter` (open) | Select the highlighted option (single), toggle it (multi) or toggle Select-all |
165
+ | `Space` (open, `no-search` only) | Same as Enter; with a search field Space types into it |
166
+ | `Escape` (open) | Close the panel and return focus to the trigger |
167
+ | `Tab` (open) | Close the panel, then focus moves on normally |
168
+ | `Backspace` in an empty search field (`multiple`) | Remove the most recently selected chip |
169
+ | Typing (search field) | Filters options by case-insensitive substring; the first match is highlighted |
170
+ | Printable keys (`no-search`) | APG type-ahead: keys accumulate for 500 ms and jump to the next option whose label starts with the buffer |
109
171
 
110
172
  ## Notes / Edge Cases
111
- - When `state="disabled"`, the select becomes non-interactive with reduced opacity and cursor indication
112
- - Empty `selected` value shows the `placeholder` text ("Select option" by default)
113
- - Icons (left/right) adjust internal padding automatically to accommodate icon space
114
- - The chevron is always visible on the far right and rotates 180° when the dropdown is open; a custom `rightIcon` replaces it entirely (no chevron) and does not rotate
115
- - In error state, supporting text is styled with error color for visual consistency; the error state automatically clears once a valid option is selected
116
- - Options accept plain strings or `{ value, label?, supportingText?, icon? }` objects; string entries use the same text for value and label
117
- - In multi-select, selected chips stay on a single line: the number of visible chips adapts to the field's width and the rest collapse into a trailing "+N" chip (recomputed on selection changes and via `ResizeObserver` on resize). At least one chip is always shown. `max-visible-chips` is an optional additional hard cap (uncapped by default)
118
- - An option's `icon` renders only when set — options without one simply omit the prefix icon (no reserved empty space), and an unknown icon name logs a `rel-icon` console warning and renders nothing
119
- - Typed text that doesn't exactly match (case-insensitive) any option reverts to the last committed `selected` value on blur, Escape, or Tab
120
- - The ghost-text suggestion shows the remainder of the first option whose name starts with the typed text (case-insensitive); it has no effect on filtering, which matches substrings anywhere in an option
121
- - Dropdown panel is positioned via `position: fixed` and flips above the trigger if there isn't enough viewport space below
122
- - The open listbox is promoted to the browser **top layer** (native Popover API, `popover="manual"`), so it renders correctly inside transformed / `overflow: hidden` containers — a centered `rel-modal`, a bottom `rel-drawer`. Engines without the Popover API fall back to `position: fixed` with a measure-and-cancel correction for transformed ancestors
173
+
174
+ - The change event is named `changed` (not `rel-change`); `detail.value` is a string in single mode and an array of strings when `multiple`.
175
+ - Opening the panel does not pre-highlight any row; the highlight appears on the first hover or arrow key so nothing reads as an unintended default.
176
+ - `state="error"` clears itself to `"default"` on the next selection, toggle, Select-all or chip removal. `state="disabled"` dims the whole field to 50% opacity, makes chips non-removable and blocks pointer events on them.
177
+ - Turning `multiple` off resets `selectedValues` to `[]`. `selected` is ignored in multi mode and `selectedValues` in single mode.
178
+ - Chips never wrap: the number shown adapts to the trigger width (measured through a hidden copy of every chip and a `ResizeObserver`), with the remainder collapsed into a trailing "+N" chip. At least one chip always stays visible; `max-visible-chips` is an extra hard cap. A selected value with no matching option is shown as its raw value.
179
+ - Option `icon` renders in both single and multi rows; `supportingText` only in multi rows. An unknown icon name logs a `rel-icon` warning and renders nothing (no reserved space).
180
+ - A custom `right-icon` replaces the chevron and does not rotate when the panel opens; the default chevron rotates 180°.
181
+ - The search field uses the small field sizing for both `small` and `medium` selects; only `large` bumps it to medium. Empty results show "No matches found" (or "No options available" when `options` is empty).
182
+ - The `helper` part exists only while `supporting-text` is non-empty.
183
+ - The open listbox is promoted to the browser top layer (native Popover API, `popover="manual"`) so it renders correctly inside transformed or `overflow: hidden` containers such as a centered `rel-modal` or a `rel-drawer`; engines without the Popover API fall back to `position: fixed` with a measure-and-cancel correction. The panel matches the trigger width, flips above the trigger when there is not enough room below, is capped at 360px with internal scrolling, and closes on any outside pointerdown or outside scroll (scrolling the list itself does not close it).
184
+ - `--rel-field-label-reserve` (see Component hooks) keeps a `label-style="none"` select bottom-aligned with labelled siblings; set it to `0` in toolbars or table cells.
185
+ - The trigger heights (32 / 40 / 48px) match `rel-textfield` and the date / time / range picker triggers so mixed form rows line up.