@reliability-design/web 1.4.6 → 1.4.7

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 (146) hide show
  1. package/CHANGELOG.md +211 -49
  2. package/dist/components/rel-accordion/README.md +132 -132
  3. package/dist/components/rel-alert/README.md +145 -141
  4. package/dist/components/rel-autocomplete/README.md +156 -61
  5. package/dist/components/rel-avatar/README.md +116 -130
  6. package/dist/components/rel-badge/README.md +98 -93
  7. package/dist/components/rel-breadcrumb/README.md +96 -102
  8. package/dist/components/rel-button/README.md +160 -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 +184 -166
  13. package/dist/components/rel-checkbox/README.md +93 -75
  14. package/dist/components/rel-chip/README.md +132 -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 +78 -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-menu/README.md +178 -151
  21. package/dist/components/rel-menubar/README.md +218 -329
  22. package/dist/components/rel-otp/README.md +170 -440
  23. package/dist/components/rel-pagination/README.md +100 -424
  24. package/dist/components/rel-popover/README.md +203 -568
  25. package/dist/components/rel-progress/README.md +88 -110
  26. package/dist/components/rel-radio/README.md +131 -138
  27. package/dist/components/rel-range-picker/README.md +162 -0
  28. package/dist/components/rel-resizable/README.md +199 -140
  29. package/dist/components/rel-segmented-button/README.md +155 -141
  30. package/dist/components/rel-select/README.md +154 -93
  31. package/dist/components/rel-sheet/README.md +168 -224
  32. package/dist/components/rel-skeleton/README.md +103 -0
  33. package/dist/components/rel-slider/README.md +130 -135
  34. package/dist/components/rel-snackbar/README.md +162 -134
  35. package/dist/components/rel-stepper/README.md +158 -160
  36. package/dist/components/rel-switch/README.md +108 -102
  37. package/dist/components/rel-tab/README.md +171 -163
  38. package/dist/components/rel-textfield/README.md +159 -69
  39. package/dist/components/rel-time-picker/README.md +122 -157
  40. package/dist/components/rel-toast/README.md +157 -148
  41. package/dist/components/rel-tooltip/README.md +108 -111
  42. package/dist/components/rel-upload/README.md +220 -0
  43. package/dist/index.d.mts +1147 -340
  44. package/dist/index.d.ts +1147 -340
  45. package/dist/index.js +3451 -3163
  46. package/dist/index.js.map +1 -1
  47. package/dist/index.mjs +3416 -3118
  48. package/dist/index.mjs.map +1 -1
  49. package/dist/styles/vars.css +877 -866
  50. package/dist/{vars-XNMTSRVN.css → vars-JHTUGNRG.css} +877 -866
  51. package/package.json +3 -2
  52. package/dist/components/rel-accordion/rel-accordion.ts +0 -336
  53. package/dist/components/rel-alert/rel-alert.ts +0 -305
  54. package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -720
  55. package/dist/components/rel-avatar/rel-avatar.ts +0 -142
  56. package/dist/components/rel-badge/rel-badge.ts +0 -175
  57. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
  58. package/dist/components/rel-button/rel-button.ts +0 -560
  59. package/dist/components/rel-calendar/index.ts +0 -1
  60. package/dist/components/rel-calendar/rel-calendar.ts +0 -565
  61. package/dist/components/rel-card/rel-card.ts +0 -88
  62. package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
  63. package/dist/components/rel-carousel/index.ts +0 -1
  64. package/dist/components/rel-carousel/rel-carousel.ts +0 -723
  65. package/dist/components/rel-chart/README.md +0 -286
  66. package/dist/components/rel-chart/index.ts +0 -1
  67. package/dist/components/rel-chart/rel-chart.ts +0 -754
  68. package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
  69. package/dist/components/rel-chip/rel-chip.ts +0 -483
  70. package/dist/components/rel-container/rel-container.ts +0 -95
  71. package/dist/components/rel-date-picker/date-format.test.ts +0 -43
  72. package/dist/components/rel-date-picker/date-format.ts +0 -47
  73. package/dist/components/rel-date-picker/date-parse.test.ts +0 -116
  74. package/dist/components/rel-date-picker/date-parse.ts +0 -178
  75. package/dist/components/rel-date-picker/index.ts +0 -1
  76. package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1676
  77. package/dist/components/rel-divider/rel-divider.ts +0 -54
  78. package/dist/components/rel-drawer/index.ts +0 -1
  79. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
  80. package/dist/components/rel-drawer/rel-drawer.ts +0 -607
  81. package/dist/components/rel-kbd/rel-kbd.ts +0 -188
  82. package/dist/components/rel-layout/rel-layout.ts +0 -49
  83. package/dist/components/rel-menu/rel-menu.ts +0 -541
  84. package/dist/components/rel-menubar/index.ts +0 -2
  85. package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
  86. package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
  87. package/dist/components/rel-menubar/rel-menubar.ts +0 -777
  88. package/dist/components/rel-nav-menu/README.md +0 -289
  89. package/dist/components/rel-nav-menu/index.ts +0 -3
  90. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
  91. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
  92. package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
  93. package/dist/components/rel-navbar/README.md +0 -126
  94. package/dist/components/rel-navbar/rel-navbar.ts +0 -105
  95. package/dist/components/rel-otp/index.ts +0 -1
  96. package/dist/components/rel-otp/rel-otp.ts +0 -527
  97. package/dist/components/rel-pagination/index.ts +0 -1
  98. package/dist/components/rel-pagination/rel-pagination.ts +0 -522
  99. package/dist/components/rel-popover/index.ts +0 -2
  100. package/dist/components/rel-popover/rel-popover-content.ts +0 -81
  101. package/dist/components/rel-popover/rel-popover.ts +0 -475
  102. package/dist/components/rel-progress/rel-progress.ts +0 -234
  103. package/dist/components/rel-radio/rel-radio.ts +0 -481
  104. package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1688
  105. package/dist/components/rel-resizable/index.ts +0 -3
  106. package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
  107. package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
  108. package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
  109. package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -217
  110. package/dist/components/rel-select/rel-select.ts +0 -1416
  111. package/dist/components/rel-sheet/rel-sheet.ts +0 -610
  112. package/dist/components/rel-sidebar/README.md +0 -209
  113. package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1191
  114. package/dist/components/rel-skeleton/rel-skeleton.ts +0 -143
  115. package/dist/components/rel-slider/rel-slider.ts +0 -673
  116. package/dist/components/rel-snackbar/rel-snackbar.ts +0 -329
  117. package/dist/components/rel-stepper/rel-stepper.ts +0 -567
  118. package/dist/components/rel-switch/rel-switch.ts +0 -199
  119. package/dist/components/rel-tab/rel-tab.ts +0 -264
  120. package/dist/components/rel-table/README.md +0 -178
  121. package/dist/components/rel-table/rel-table-body.ts +0 -22
  122. package/dist/components/rel-table/rel-table-caption.ts +0 -30
  123. package/dist/components/rel-table/rel-table-cell.ts +0 -43
  124. package/dist/components/rel-table/rel-table-footer.ts +0 -24
  125. package/dist/components/rel-table/rel-table-head.ts +0 -47
  126. package/dist/components/rel-table/rel-table-header.ts +0 -22
  127. package/dist/components/rel-table/rel-table-row.ts +0 -37
  128. package/dist/components/rel-table/rel-table.ts +0 -62
  129. package/dist/components/rel-textfield/rel-textfield.ts +0 -1054
  130. package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1482
  131. package/dist/components/rel-time-picker/time-parse.test.ts +0 -67
  132. package/dist/components/rel-time-picker/time-parse.ts +0 -85
  133. package/dist/components/rel-toast/rel-toast.ts +0 -206
  134. package/dist/components/rel-tooltip/rel-tooltip.ts +0 -193
  135. package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
  136. package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
  137. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
  138. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
  139. package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
  140. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
  141. package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
  142. package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
  143. package/dist/components/rel-topbar/rel-topbar.ts +0 -446
  144. package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
  145. package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
  146. package/dist/components/rel-upload/rel-upload.ts +0 -968
@@ -1,106 +1,196 @@
1
1
  # rel-textfield
2
2
 
3
- ## Overview
4
-
5
- A text input field component with support for labels, icons, validation states, and supporting text. Used for single-line text entry in forms and user interfaces.
3
+ `<rel-textfield>` is the single-line and multi-line text input from the Figma "Text Field" component: a labelled control with three chromes (`default` outlined, `underline`, `legend` notched outline), three heights (32 / 40 / 48px), prefix and suffix blocks (icon, short text or slotted content), error / helper text and full-width or inline layout. Use it for free-text entry — names, emails, passwords, numbers, notes (`multiline`). For picking from a fixed list use `rel-select`; for type-ahead against a list use `rel-autocomplete`; for dates and times use the picker components, which reuse this field's chrome.
6
4
 
7
5
  ## Usage
8
6
 
9
7
  ```html
10
- <!-- Basic usage -->
11
- <rel-textfield label="Email" placeholder="Enter your email"></rel-textfield>
8
+ <!-- Basic field -->
9
+ <rel-textfield label="Email" placeholder="you@example.com" type="email"></rel-textfield>
10
+
11
+ <!-- Sizes and chromes -->
12
+ <rel-textfield label="Small" size="small"></rel-textfield>
13
+ <rel-textfield label="Large" size="large"></rel-textfield>
14
+ <rel-textfield label="Underline" variant="underline"></rel-textfield>
15
+ <rel-textfield label="Legend" variant="legend"></rel-textfield>
16
+
17
+ <!-- Label styles -->
18
+ <rel-textfield label="Floating label" label-style="floating"></rel-textfield>
19
+ <rel-textfield label-style="none" placeholder="Search orders" field-aria-label="Search orders"></rel-textfield>
20
+
21
+ <!-- Leading icon, helper text, required -->
22
+ <rel-textfield label="Search" prefixicon="search" helpertext="Type at least 3 characters" required></rel-textfield>
23
+
24
+ <!-- Prefix / suffix text (Figma .Preffix / .Suffix) -->
25
+ <rel-textfield label="Website" prefix-text="https://" prefix-style="fill"></rel-textfield>
26
+ <rel-textfield label="Weight" suffix-text="kg" suffix-divider></rel-textfield>
27
+
28
+ <!-- Slotted prefix / suffix content -->
29
+ <rel-textfield label="Amount">
30
+ <span slot="prefix">$</span>
31
+ <rel-button slot="suffix" variant="ghost" size="small">Max</rel-button>
32
+ </rel-textfield>
12
33
 
13
- <!-- With icons and supporting text -->
34
+ <!-- Clickable trailing icon (e.g. password toggle) -->
14
35
  <rel-textfield
36
+ id="pw"
15
37
  label="Password"
16
38
  type="password"
17
- leftIcon="🔒"
18
- supportingText="Must be at least 8 characters">
39
+ righticon="eye"
40
+ clickable-right-icon
41
+ right-icon-aria-label="Show password">
19
42
  </rel-textfield>
43
+ <script>
44
+ const pw = document.getElementById("pw");
45
+ pw.addEventListener("rel-right-icon-click", () => {
46
+ pw.type = pw.type === "password" ? "text" : "password";
47
+ });
48
+ pw.addEventListener("rel-change", (e) => console.log("typed:", e.detail));
49
+ </script>
20
50
 
21
- <!-- No label — an accessible name is required in its place -->
22
- <rel-textfield
23
- label-style="none"
24
- placeholder="Search orders"
25
- field-aria-label="Search orders">
26
- </rel-textfield>
51
+ <!-- Error state -->
52
+ <rel-textfield label="Username" value="a" error errormessage="Must be at least 3 characters"></rel-textfield>
53
+
54
+ <!-- Multiline (textarea) -->
55
+ <rel-textfield label="Notes" multiline rows="5" maxlength="500"></rel-textfield>
27
56
 
28
- <!-- Setting value via JavaScript -->
29
- <rel-textfield id="myField" label="Username"></rel-textfield>
57
+ <!-- Read-only, disabled, inline width -->
58
+ <rel-textfield label="Order ID" value="SO-4821" readonly></rel-textfield>
59
+ <rel-textfield label="Disabled" disabled></rel-textfield>
60
+ <rel-textfield id="qty" label="Quantity" type="number"></rel-textfield>
30
61
  <script>
31
- const field = document.getElementById('myField');
32
- field.value = 'john.doe';
62
+ // fullWidth defaults to true; a boolean attribute cannot express false, so unset it via the property
63
+ document.getElementById("qty").fullWidth = false;
33
64
  </script>
34
65
  ```
35
66
 
67
+ ### Deprecated aliases
68
+
69
+ ```html
70
+ <!-- floatingLabel → label-style="floating" (kept permanently as an alias; no console notice) -->
71
+ <rel-textfield label="Old spelling" floatinglabel></rel-textfield>
72
+ <rel-textfield label="Canonical" label-style="floating"></rel-textfield>
73
+ ```
74
+
75
+ <!-- readme-gen:start -->
76
+
77
+ <!-- Generated from rel-textfield.ts — do not edit; run readme-generate.mjs -->
78
+
36
79
  ## API
37
80
 
38
- ### Attributes/Properties
39
-
40
- | Name | Type | Default | Description |
41
- |------|------|---------|-------------|
42
- | `label` | `string` | - | Label text displayed above the input |
43
- | `label-style` | `"default" \| "floating" \| "none"` | - | Label presentation. `"none"` renders no label at all. Unset, the deprecated `floatingLabel` boolean decides |
44
- | `floatingLabel` | `boolean` | `false` | **Deprecated** — use `label-style="floating"`. Kept permanently as an alias |
45
- | `field-aria-label` | `string` | - | Accessible name for the input. Required with `label-style="none"` |
46
- | `field-aria-labelledby` | `string` | - | IDREF list of external element(s) naming the input. Wins over `field-aria-label` |
47
- | `placeholder` | `string` | `""` | Placeholder text |
48
- | `type` | `string` | `"text"` | Input type (text, email, password, etc.) |
49
- | `value` | `string` | `""` | Current input value |
50
- | `leftIcon` | `string` | - | Icon content on the left side |
51
- | `rightIcon` | `string` | - | Icon content on the right side |
52
- | `supportingText` | `string` | - | Helper or error text below input |
53
- | `state` | `"default" \| "focused" \| "error" \| "disabled"` | `"default"` | Visual state |
54
- | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Size variant |
81
+ ### Attributes / Properties
82
+
83
+ | Property | Attribute | Type | Default | Description |
84
+ | --- | --- | --- | --- | --- |
85
+ | `label` | `label` | `string` | `undefined` | Visible label text. Rendered above the control (default), inside it as a floating label (`labelStyle="floating"`) or as the notch of the `legend` variant; omitted entirely under `labelStyle="none"`. |
86
+ | `placeholder` | `placeholder` | `string` | `""` | Placeholder shown while the field is empty. Under the floating label style it stays hidden until the field is focused. |
87
+ | `type` | `type` | `string` | `"text"` | Native input `type` (`text`, `email`, `password`, `number`, …). Ignored when `multiline` is set. |
88
+ | `value` | `value` | `string` | `""` | Current text. Updated on every keystroke; set it to control the field programmatically. |
89
+ | `leftIcon` | `lefticon` | `string` | `undefined` | Legacy name for the leading icon (a `rel-icon` name). `prefixIcon` wins when both are set. |
90
+ | `rightIcon` | `righticon` | `string` | `undefined` | Trailing `rel-icon` name rendered in the suffix block. Decorative unless `clickableRightIcon` is set. |
91
+ | `clickableRightIcon` | `clickable-right-icon` | `boolean` | `false` | Makes the trailing icon interactive (pointer + focusable) and emits `rel-right-icon-click` — e.g. a password show/hide toggle or clear button. |
92
+ | `rightIconAriaLabel` | `right-icon-aria-label` | `string` | `undefined` | Accessible label for the trailing icon when it is clickable. |
93
+ | `prefixText` | `prefix-text` | `string` | `undefined` | Short text rendered before the input, e.g. `"http://"` or `"$"`. Sits in the same prefix block as `prefixIcon` and is styled by `prefixStyle`. |
94
+ | `prefixStyle` | `prefix-style` | `"clear" \| "divider" \| "fill"` | `"clear"` | Visual treatment of the prefix block — applies to the prefix icon, text and slotted `prefix` content alike. See `TextFieldPrefixStyle`. |
95
+ | `suffixText` | `suffix-text` | `string` | `undefined` | Short text rendered after the input, e.g. `"kg"` or `".00"`. Shares the suffix block with `rightIcon` and slotted `suffix` content. |
96
+ | `suffixDivider` | `suffix-divider` | `boolean` | `false` | Draws a hairline between the input and the suffix block. |
97
+ | `supportingText` | `supportingtext` | `string` | `undefined` | Legacy name for the helper line below the control. `helperText` wins when both are set. |
98
+ | `state` | `state` | `"default" \| "error" \| "disabled"` | `"default"` | Legacy visual state. `"error"` / `"disabled"` behave like the `error` / `disabled` booleans, which are preferred and take precedence. Typing into a `state="error"` field resets it to `"default"` until the value is emptied again. |
99
+ | `size` | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Control height — `small` 32px, `medium` 40px, `large` 48px — which also scales the text and the affix icons. |
100
+ | `labelStyle` | `label-style` | `"default" \| "floating" \| "none"` | `undefined` | Label presentation. Left `undefined` the deprecated `floatingLabel` boolean decides, so existing markup keeps working; set explicitly and it wins. Declares `attribute` because Lit would otherwise only listen for the all-lowercase `labelstyle`. Reflected. |
101
+ | `fieldAriaLabel` | `field-aria-label` | `string` | `undefined` | Accessible name for the input. Required when `labelStyle="none"`, since no `<label>` renders and a placeholder is not a valid naming mechanism. |
102
+ | `fieldAriaLabelledby` | `field-aria-labelledby` | `string` | `undefined` | IDREF list of external element(s) naming the input. Wins over `fieldAriaLabel`. |
103
+ | `variant` | `variant` | `"default" \| "underline" \| "legend"` | `"default"` | Field chrome: `default` outlined box, `underline` bottom border only, `legend` notched outline with the label sitting in the top border (native fieldset/legend). Reflected. |
104
+ | `disabled` | `disabled` | `boolean` | `false` | Disables the input (not focusable, not editable) and dims the control. |
105
+ | `required` | `required` | `boolean` | `false` | Marks the input required: sets native `required` / `aria-required` and appends an asterisk to the label. |
106
+ | `multiline` | `multiline` | `boolean` | `false` | Renders a textarea instead of a single-line input (see `rows`). Prefix/suffix blocks and `type` are ignored in this mode. Reflected. |
107
+ | `rows` | `rows` | `number` | `3` | Visible text rows of the textarea when `multiline` is set. |
108
+ | `prefixIcon` | `prefixicon` | `string` | `undefined` | Leading `rel-icon` name rendered in the prefix block (preferred over the legacy `leftIcon`). |
109
+ | `prefixIconAriaLabel` | `prefixiconarialabel` | `string` | `undefined` | Accessible label for the leading icon. Without it the icon is `aria-hidden` (decorative). |
110
+ | `error` | `error` | `boolean` | `false` | Puts the field in the error state: danger border, `aria-invalid`, and `errorMessage` replacing the helper text. |
111
+ | `errorMessage` | `errormessage` | `string` | `undefined` | Text shown below the control instead of `helperText` while the field is in the error state. |
112
+ | `helperText` | `helpertext` | `string` | `undefined` | Helper line below the control (preferred over the legacy `supportingText`); linked to the input via `aria-describedby`. |
113
+ | `readonly` | `readonly` | `boolean` | `false` | Makes the input read-only: focusable and selectable but not editable. |
114
+ | `maxLength` | `maxlength` | `number` | `undefined` | Maximum number of characters (native `maxlength`). |
115
+ | `name` | `name` | `string` | `undefined` | Form field name forwarded to the native input, for form submission and autofill. |
116
+ | `fullWidth` | `fullwidth` | `boolean` | `true` | Fills the parent width as a block element (default). Unset it to render inline at the content's natural width. Reflected. |
117
+
118
+ #### Deprecated (still supported — removal targeted for 3.0)
119
+
120
+ | Property | Attribute | Type | Use instead |
121
+ | --- | --- | --- | --- |
122
+ | `floatingLabel` | `floatinglabel` | `boolean` | Use `labelStyle="floating"`. Kept permanently as an alias. |
123
+
124
+ ### Methods
125
+
126
+ | Method | Signature | Description |
127
+ | --- | --- | --- |
128
+ | `focus` | `focus(options?: FocusOptions)` | Focuses the inner input/textarea (the host itself is not focusable). |
55
129
 
56
130
  ## Events
57
131
 
58
- | Event | Detail | Description |
59
- |-------|--------|-------------|
60
- | `input-change` | `string` (the current value) | Fires when the input value changes through user interaction |
132
+ | Event | Detail | Bubbles / composed | Description |
133
+ | --- | --- | --- | --- |
134
+ | `rel-change` | `value` | bubbles / composed | Fired on every keystroke. Detail: the current text (`value`). |
135
+ | `rel-right-icon-click` | — | bubbles / composed | Fired when the trailing icon is clicked or activated with Enter / Space while `clickableRightIcon` is set. No detail. |
61
136
 
62
137
  ## Slots
63
138
 
64
- Default slot not used. All content controlled via properties.
139
+ | Slot | Description |
140
+ | --- | --- |
141
+ | `prefix` | Custom content at the far left of the control (e.g. a unit dropdown); shares the prefix block with `prefixIcon` / `prefixText`. Hidden when `multiline` is set. |
142
+ | `suffix` | Custom content at the far right of the control (e.g. an action button); shares the suffix block with `rightIcon` / `suffixText`. Hidden when `multiline` is set. |
65
143
 
66
144
  ## CSS Parts
67
145
 
68
- None exposed.
146
+ | Part | Description |
147
+ | --- | --- |
148
+ | `label` | The visible label element (default and floating label styles) |
149
+ | `input` | The native input / textarea |
150
+ | `right-icon` | The trailing `rel-icon` |
151
+ | `prefix` | The prefix block (leading icon, prefix text and the `prefix` slot) |
152
+ | `suffix` | The suffix block (suffix text, trailing icon and the `suffix` slot) |
153
+ | `helper` | The helper / error text line below the control |
154
+ | `control` | The bordered control box (the fieldset in the legend variant) |
69
155
 
70
156
  ## Tokens / CSS Variables
71
157
 
72
- - `--rel-fonts-font-family`: Font family
73
- - `--rel-body-sm-regular-font-size`, `--rel-body-md-regular-font-size`: Font sizes
74
- - `--rel-fonts-weight-body-medium`: Font weight for label
75
- - `--rel-text-label`: Label color
76
- - `--rel-text-default`: Input text color
77
- - `--rel-text-placeholder`: Placeholder and icon color
78
- - `--rel-text-disabled`: Disabled text color
79
- - `--rel-border-default`: Default border color
80
- - `--rel-bg-info`: Focus border color
81
- - `--rel-border-danger`: Error state border color
82
- - `--rel-bg-surface`: Input background (light mode)
83
- - `--rel-bg-neutral-elevated-1`: Disabled background
84
- - `--rel-spacing-*`: Spacing tokens
85
- - `--rel-border-radius-sm`: Border radius
86
- - `--rel-field-label-reserve`: Height held for the absent label row under `label-style="none"`, so the input 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)
158
+ ### Component hooks
159
+
160
+ Set these on the host (or an ancestor) to customise the element without touching design tokens.
161
+
162
+ | Custom property | Default | Description |
163
+ | --- | --- | --- |
164
+ | `--rel-field-label-reserve` | `calc(var(--rel-body-sm-medium-line-height) + var(--rel-spacing-2xs))` | Height held for the absent label row under `labelStyle="none"` so the control bottom-aligns with labelled siblings; defaults to the label line-height plus the wrapper gap. Set to `0` to collapse the row entirely (toolbars, table cells). |
165
+
166
+ <!-- readme-gen:end -->
87
167
 
88
168
  ## Accessibility
89
169
 
90
- - Associates `<label>` with input via `for`/`id`
91
- - With `label-style="none"` no `<label>` renders, so `field-aria-label` (or
92
- `field-aria-labelledby`) must supply the accessible name — a placeholder is
93
- not a valid naming mechanism. The component logs a console warning when both
94
- are missing.
95
- - Supports native HTML5 input attributes (`required`, `disabled`, `readonly`, `maxlength`)
96
- - Focus ring visible on keyboard navigation (`:focus-visible`)
97
- - Error state communicated visually via border color
98
- - Supporting text can provide `aria-describedby` context
170
+ - The visible `<label>` is linked to the native input via `for`/`id` (the host `id`, or a generated one). In the `legend` variant the input is instead named through `aria-labelledby` pointing at the `<legend>`.
171
+ - With `label-style="none"` no label renders: `field-aria-labelledby` (wins) or `field-aria-label` must supply the accessible name. The component logs a console warning once when neither is set, because a placeholder is not a valid naming mechanism.
172
+ - The input carries `aria-required`, `aria-invalid` (true in the error state) and `aria-describedby` pointing at the helper line. That line is always rendered with `aria-live="polite"`, so a change from helper text to `errorMessage` is announced.
173
+ - The leading icon is `aria-hidden` unless `prefixIconAriaLabel` is set, in which case it becomes `role="img"` with that label. The trailing icon is `aria-hidden` unless `clickable-right-icon` is set, in which case it is `role="button"`, `tabindex="0"` and named by `right-icon-aria-label`.
174
+ - No screen-reader announcements are made via `announce()`; the live helper region is the only announcement path.
175
+
176
+ | Key | Action |
177
+ |-----|--------|
178
+ | `Tab` | Focus the input; with `clickable-right-icon` the trailing icon is the next Tab stop |
179
+ | `Enter` / `Space` (on the trailing icon) | Fires `rel-right-icon-click` (default prevented) |
180
+
181
+ Focus behaviour: `focus()` on the host focuses the inner input / textarea (the host itself is not focusable). A mousedown anywhere on the control — border, legend notch, prefix / suffix padding — focuses the input like clicking a label would; presses on the input, a clickable icon or slotted content keep their native behaviour. The focused control shows a `--rel-border-strong` border plus a 1px ring; the `legend` variant recolours the border and legend only (a ring would strike through the notch).
99
182
 
100
183
  ## Notes / Edge Cases
101
184
 
102
- - The `state` prop overrides internal focus state if set explicitly
103
- - Icons are decorative (text content, not `<img>` elements)
104
- - Input type `password` does not auto-toggle visibility (use `rightIcon` for toggle button)
105
- - Error state must be set programmatically; no built-in validation
106
- - Disabled inputs cannot be focused or edited
185
+ - Attribute spellings: props declared with an explicit `attribute` use kebab-case (`label-style`, `prefix-text`, `clickable-right-icon`, `field-aria-label`, …). The others have Lit's default all-lowercase attribute — `prefixicon`, `righticon`, `helpertext`, `errormessage`, `maxlength`, `fullwidth`, `supportingtext`, `floatinglabel` — see the Attribute column above. HTML attribute names are case-insensitive, so `helperText="…"` in markup also works.
186
+ - Legacy names still work but the canonical ones win when both are set: `leftIcon` → `prefixIcon`, `supportingText` → `helperText`, `state="error"` → `error`, `state="disabled"` → `disabled`. `lefticon` / `righticon` attributes are also read once at connect time.
187
+ - `errorMessage` replaces the helper line only while the field is in the error state **and** the message is set; otherwise the helper text stays visible. There is no built-in validation — set `error` from your form layer.
188
+ - `state="error"` (legacy) auto-clears to `"default"` as soon as the user types a non-empty value and comes back once the value is emptied again; the `error` boolean never auto-clears.
189
+ - `multiline` renders a `<textarea>` (`rows`, vertical resize, 80px minimum height). `type`, the prefix / suffix blocks and their slots are ignored in this mode (the slots stay mounted but hidden).
190
+ - `fullWidth` defaults to `true` and reflects as `fullwidth`; without it the host becomes `inline-block` at its content width. To turn it off from HTML you must bind the property (`.fullWidth=${false}`), since a boolean attribute cannot express `false`.
191
+ - `label-style="floating"`: the placeholder is hidden until the field is focused; the label floats onto the border when focused or filled. Internally the deprecated `floatingLabel` boolean is kept in sync with `labelStyle` and its reflected `floatinglabel` attribute is the styling hook, so both spellings render identically. With a `prefix-style="clear"` prefix the floated label slides back over the control's left edge (Figma "Floating Label"); `divider` / `fill` prefixes keep it after the block.
192
+ - `variant="legend"` + `label-style="none"` drops the `<legend>` entirely so the top border runs continuous; a `fill` prefix is squared off in this variant because it cannot reach the notched top border.
193
+ - `--rel-field-label-reserve` (see Component hooks) keeps a `label-style="none"` field bottom-aligned with labelled siblings; set it to `0` in toolbars or table cells.
194
+ - Inside the prefix block the order is slotted content → icon → text (nearest the input); in the suffix block it is text → icon → slotted content. Clicking a non-interactive part of either block focuses the input.
195
+ - The control height (32 / 40 / 48px) is explicit so the field lines up with `rel-select` and the date / time / range picker triggers; the textarea keeps the padding model and grows with `rows`.
196
+ - In Figma, the `error` state's border has no focus ring; that matches the code (`box-shadow: none` in error).
@@ -1,199 +1,164 @@
1
1
  # rel-time-picker
2
2
 
3
- A fully accessible time picker component built with Lit for the Reliability Design System. Features a trigger button with an hour/minute (and AM/PM) column popover. Selections are held in draft state until Apply is clicked, mirroring the rel-range-picker interaction model.
4
-
5
- ## Features
6
-
7
- - Trigger button with time-column popover (Hour / Min / AM-PM)
8
- - 12-hour and 24-hour display formats
9
- - Configurable minute intervals (1, 5, 10, 15, 30, …)
10
- - Draft state with Now / Cancel / Apply footer
11
- - Min/max time constraints
12
- - Optional clear button
13
- - Label, placeholder, helper text and error states
14
- - Standardized input API (mirrors rel-textfield / rel-date-picker)
15
- - Keyboard navigation and full ARIA support
16
- - Token-driven styling with `::part` hooks
17
-
18
- ## Value format
19
-
20
- The committed `value` is **always 24-hour `"HH:mm"`** (e.g. `"09:30"`, `"17:05"`) regardless of the display `format`, so consumers get a consistent machine-readable string.
3
+ Time selection field implementing the Figma "Time Picker" component (Reliability DS V1.0, node 8301:3771): an editable text field with a clock icon that opens a panel of Hour / Minute (/ Second / AM-PM) columns and a Now / Cancel / OK footer. Column picks are held as a draft until OK; the field also accepts free-form typed times ("930", "9:30 pm", "n" for now). The committed `value` is always 24-hour `"HH:mm"` (or `"HH:mm:ss"` with `seconds`) regardless of the display `format`. Use it for a single time of day; use `rel-date-picker` for dates and `rel-range-picker` for date ranges.
21
4
 
22
5
  ## Usage
23
6
 
24
- ### Basic
25
-
26
7
  ```html
8
+ <!-- Basic (12-hour display, 5-minute steps) -->
27
9
  <rel-time-picker></rel-time-picker>
28
- ```
29
-
30
- ### With Value
31
10
 
32
- ```html
33
- <rel-time-picker value="09:30"></rel-time-picker>
34
- ```
35
-
36
- ### With Label
37
-
38
- ```html
39
- <rel-time-picker label="Start time" placeholder="Select time"></rel-time-picker>
40
- ```
11
+ <!-- Value is always 24-hour HH:mm; the field shows it per `format` -->
12
+ <rel-time-picker value="09:30" label="Start time"></rel-time-picker>
13
+ <rel-time-picker value="17:05" format="24h"></rel-time-picker>
41
14
 
42
- ### 24-hour format
15
+ <!-- Seconds column; value becomes HH:mm:ss -->
16
+ <rel-time-picker seconds value="17:05:30" format="24h"></rel-time-picker>
43
17
 
44
- ```html
45
- <rel-time-picker format="24h" value="17:05"></rel-time-picker>
46
- ```
47
-
48
- ### Minute intervals
49
-
50
- ```html
18
+ <!-- Minute granularity of the column (typed entry still accepts any minute) -->
51
19
  <rel-time-picker minute-step="15"></rel-time-picker>
52
- <rel-time-picker minute-step="30"></rel-time-picker>
53
- ```
20
+ <rel-time-picker minute-step="1"></rel-time-picker>
54
21
 
55
- ### With Min/Max
22
+ <!-- Constrain to business hours (inclusive, HH:mm) -->
23
+ <rel-time-picker min-time="09:00" max-time="17:30" placeholder="Business hours only"></rel-time-picker>
56
24
 
57
- ```html
58
- <rel-time-picker
59
- min-time="09:00"
60
- max-time="17:30"
61
- placeholder="Business hours only"
62
- ></rel-time-picker>
63
- ```
64
-
65
- ### Clearable
66
-
67
- ```html
25
+ <!-- Clear button, helper / error text, required -->
68
26
  <rel-time-picker value="09:30" clearable></rel-time-picker>
69
- ```
70
-
71
- ### States
27
+ <rel-time-picker label="Start time" helper-text="Local timezone"></rel-time-picker>
28
+ <rel-time-picker label="Start time" error error-message="Select a valid time"></rel-time-picker>
29
+ <rel-time-picker label="Start time" required></rel-time-picker>
72
30
 
73
- ```html
31
+ <!-- States -->
74
32
  <rel-time-picker value="09:30" disabled></rel-time-picker>
75
33
  <rel-time-picker value="09:30" readonly></rel-time-picker>
76
- <rel-time-picker error error-message="Select a valid time"></rel-time-picker>
77
- <rel-time-picker helper-text="Local timezone"></rel-time-picker>
78
- <rel-time-picker label="Start time" required></rel-time-picker>
79
- ```
80
-
81
- ### Sizes / layout
82
34
 
83
- ```html
35
+ <!-- Sizes (field 32 / 40 / 48px, panel 220 / 250 / 280px) and layout -->
84
36
  <rel-time-picker size="small"></rel-time-picker>
37
+ <rel-time-picker size="medium"></rel-time-picker>
85
38
  <rel-time-picker size="large"></rel-time-picker>
86
- <rel-time-picker fullWidth></rel-time-picker>
87
- ```
88
-
89
- ### Form usage
39
+ <rel-time-picker fullwidth label="Full width"></rel-time-picker>
90
40
 
91
- ```html
41
+ <!-- Form integration: `name` renders a hidden input carrying the value -->
92
42
  <form>
93
43
  <rel-time-picker name="startTime" label="Start time" required></rel-time-picker>
94
44
  </form>
95
- ```
96
-
97
- The value is mirrored onto a hidden `<input name="...">` so native form submission works.
98
-
99
- ### Programmatic Control (JavaScript)
100
45
 
101
- ```javascript
102
- const picker = document.querySelector('rel-time-picker');
103
-
104
- // Set value (always 24h HH:mm)
105
- picker.value = '14:30';
106
-
107
- // Open programmatically
108
- picker.open = true;
109
-
110
- // Listen to changes (fired on Apply, and on clear with value: null)
111
- picker.addEventListener('rel-change', (e) => {
112
- console.log('Selected time:', e.detail.value); // "14:30" | null
113
- });
114
-
115
- picker.addEventListener('rel-open-change', (e) => {
116
- console.log('Open:', e.detail.open);
117
- });
46
+ <!-- Programmatic control -->
47
+ <rel-time-picker id="tp" clearable></rel-time-picker>
48
+ <script>
49
+ const picker = document.getElementById('tp');
50
+ picker.value = '14:30'; // always 24h
51
+ picker.open = true; // opens the panel and focuses the selected hour
52
+
53
+ picker.addEventListener('rel-change', (e) => {
54
+ console.log('time:', e.detail.value); // "14:30" | "14:30:00" | null (cleared)
55
+ });
56
+ picker.addEventListener('rel-open-change', (e) => {
57
+ console.log('open:', e.detail.open);
58
+ });
59
+ </script>
118
60
  ```
119
61
 
120
- ## Interaction model
62
+ ### Typed entry
121
63
 
122
- - Clicking a column entry updates the **draft** shown in the readout row; nothing is committed yet.
123
- - **Apply** commits the draft, emits `rel-change` and closes.
124
- - **Cancel**, `Escape` or an outside click discards the draft (same as rel-range-picker).
125
- - **Now** sets the draft to the current time, snapped to `minute-step` and clamped into `min-time`/`max-time`.
126
- - Picking an hour with no minute selected defaults the minute to `00`.
64
+ Typing into the field and pressing `Enter` (or leaving the field) commits the text. Accepted forms are case- and whitespace-insensitive: `9` → 09:00, `930` / `0930` / `9:30` / `9.30` / `9 30` → 09:30, `093015` / `9:30:15` → 09:30:15, `9 pm` / `9:30p` → 21:00 / 21:30, `12am` → 00:00, `12 pm` → 12:00, and `n` / `now` → the current time. Unparseable text shows "Invalid time", a time outside `min-time` / `max-time` shows "Time out of range"; in both cases the text stays in the field for correction and the committed `value` is untouched.
127
65
 
128
- ## Keyboard
66
+ <!-- readme-gen:start -->
129
67
 
130
- | Key | Context | Action |
131
- | --- | --- | --- |
132
- | `Enter` / `Space` / `ArrowDown` | Trigger | Open the popover |
133
- | `ArrowUp` / `ArrowDown` | Column | Move and select the previous/next entry |
134
- | `ArrowLeft` / `ArrowRight` | Column | Jump between Hour / Min / AM-PM columns |
135
- | `Home` / `End` | Column | First / last entry |
136
- | `Enter` | Column | Select the entry and Apply |
137
- | `Escape` | Anywhere | Discard draft and close |
68
+ <!-- Generated from rel-time-picker.ts — do not edit; run readme-generate.mjs -->
69
+
70
+ ## API
138
71
 
139
- ## Properties
72
+ ### Attributes / Properties
140
73
 
141
74
  | Property | Attribute | Type | Default | Description |
142
75
  | --- | --- | --- | --- | --- |
143
- | `value` | `value` | `string \| null` | `null` | Committed time, 24h `"HH:mm"` |
144
- | `placeholder` | `placeholder` | `string` | `"Select time"` | Trigger text when empty |
145
- | `label` | `label` | `string \| null` | `null` | Field label |
146
- | `format` | `format` | `"12h" \| "24h"` | `"12h"` | Display format (value stays `HH:mm`) |
147
- | `minuteStep` | `minute-step` | `number` | `1` | Minute column granularity |
148
- | `minTime` | `min-time` | `string \| null` | `null` | Earliest selectable time (inclusive) |
149
- | `maxTime` | `max-time` | `string \| null` | `null` | Latest selectable time (inclusive) |
150
- | `disabled` | `disabled` | `boolean` | `false` | Disables all interaction |
151
- | `readonly` | `readonly` | `boolean` | `false` | Value shown, popover cannot open |
152
- | `required` | `required` | `boolean` | `false` | Renders `*` and sets `aria-required` |
153
- | `clearable` | `clearable` | `boolean` | `false` | Clear button when a value is set |
154
- | `open` | `open` | `boolean` | `false` | Controls / reflects popover state |
155
- | `error` | `error` | `boolean` | `false` | Error styling |
156
- | `invalid` | `invalid` | `boolean` | `false` | Alias for `error` |
157
- | `errorMessage` | `error-message` | `string` | — | Shown below when error/invalid |
158
- | `helperText` | `helper-text` | `string` | — | Assistive text below the field |
159
- | `name` | `name` | `string` | — | Form field name (hidden input) |
160
- | `size` | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Trigger size |
161
- | `fullWidth` | `fullWidth` | `boolean` | `false` | Fill the parent width |
162
- | `variant` | `variant` | `"outlined"` | `"outlined"` | Visual style |
76
+ | `value` | `value` | `string \| null` | `null` | Committed time, 24-hour "HH:mm" or "HH:mm:ss" (e.g. "09:30", "17:05:30"). Reflected. |
77
+ | `placeholder` | `placeholder` | `string` | `"Select time"` | Placeholder shown in the field while no value is committed and nothing is typed. |
78
+ | `label` | `label` | `string \| null` | `null` | Field label rendered above the trigger; also the input's accessible name (unset → "Select time"). |
79
+ | `disabled` | `disabled` | `boolean` | `false` | Disables the field (0.6 opacity, no pointer events); the panel cannot open. Reflected. |
80
+ | `required` | `required` | `boolean` | `false` | Renders the `*` mark after the label and sets `aria-required`; not enforced by the component. |
81
+ | `readonly` | `readonly` | `boolean` | `false` | Shows the value but blocks typing and opening the panel. Reflected. |
82
+ | `format` | `format` | `"12h" \| "24h"` | `"12h"` | Display format of the trigger text and hour column. Value stays 24-hour. |
83
+ | `seconds` | `seconds` | `boolean` | `false` | Shows the seconds column; committed value becomes "HH:mm:ss". Reflected. |
84
+ | `minuteStep` | `minute-step` | `number` | `5` | Minute granularity of the minutes column (1, 5, 10, 15, 30, …). |
85
+ | `minTime` | `min-time` | `string \| null` | `null` | Earliest selectable time, "HH:mm" (inclusive). |
86
+ | `maxTime` | `max-time` | `string \| null` | `null` | Latest selectable time, "HH:mm" (inclusive). |
87
+ | `open` | `open` | `boolean` | `false` | Whether the panel is open. Settable from outside; opening resets the draft from `value` (programmatic changes do not fire `rel-open-change`). Reflected. |
88
+ | `clearable` | `clearable` | `boolean` | `false` | Shows a clear (×) button inside the field while a value is set; clearing commits `null`. |
89
+ | `error` | `error` | `boolean` | `false` | Error styling (danger border, `aria-invalid`) and shows `errorMessage` below the field. |
90
+ | `invalid` | `invalid` | `boolean` | `false` | Alias for `error` (requested-API compat); either flag applies error styling. |
91
+ | `errorMessage` | `error-message` | `string` | `undefined` | Text shown below the field while `error` / `invalid` is set (falls back to the typed-entry parse error, then `helperText`). |
92
+ | `helperText` | `helper-text` | `string` | `undefined` | Assistive text shown below the field when not in error. |
93
+ | `name` | `name` | `string` | `undefined` | When set, renders a hidden `<input name="…">` carrying `value` for native form submission. |
94
+ | `size` | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Trigger height on the shared field scale — small 32 / medium 40 / large 48px border-box; also sets the panel width (220 / 250 / 280px) and option row height (32 / 36 / 40px). Small lowers the trigger min-width from 160 to 128px. |
95
+ | `variant` | `variant` | `"outlined"` | `"outlined"` | Visual style; only `"outlined"` exists today. Reflected. |
96
+ | `fullWidth` | `fullwidth` | `boolean` | `false` | Fills the parent width (the host is inline-block with intrinsic width otherwise). The attribute is lowercase `fullwidth`. Reflected. |
163
97
 
164
98
  ## Events
165
99
 
166
- | Event | Detail | Description |
167
- | --- | --- | --- |
168
- | `rel-change` | `{ value: string \| null }` | Fired on Apply and on clear |
169
- | `rel-open-change` | `{ open: boolean }` | Popover opened/closed |
170
- | `rel-focus` | — | Trigger received focus |
171
- | `rel-blur` | — | Trigger lost focus |
100
+ | Event | Detail | Bubbles / composed | Description |
101
+ | --- | --- | --- | --- |
102
+ | `rel-blur` | — | bubbles / composed | Fired when the text input loses focus (before any typed text is committed). No detail. |
103
+ | `rel-change` | `{ value: newValue } / { value: null }` | bubbles / composed | Fired when a time is committed (OK, Enter / blur on typed text, the Now shortcut) or cleared. Detail: `{ value: string \| null }` — 24-hour "HH:mm" / "HH:mm:ss", or `null` after a clear. |
104
+ | `rel-focus` | — | bubbles / composed | Fired when the text input receives focus. No detail. |
105
+ | `rel-open-change` | `{ open: this.open } / { open: false }` | bubbles / composed | Fired whenever the panel opens or closes through user interaction. Detail: `{ open: boolean }`. |
172
106
 
173
- ## CSS Parts
107
+ ## Slots
174
108
 
175
- | Part | Element |
109
+ | Slot | Description |
176
110
  | --- | --- |
177
- | `base` | Outer wrapper |
178
- | `label` | Field label |
179
- | `trigger` | Trigger button |
180
- | `value` | Trigger value/placeholder text |
181
- | `icon` | Trailing clock icon |
182
- | `panel` | Popover panel |
183
- | `option` | Hour/minute/period entries |
184
- | `footer` | Now / Cancel / Apply row |
185
- | `helper` | Helper / error text |
186
-
187
- ```css
188
- /* Example: widen the popover and restyle options */
189
- rel-time-picker::part(panel) {
190
- box-shadow: var(--rel-shadows-shadow-400);
191
- }
192
- rel-time-picker::part(option) {
193
- border-radius: var(--rel-border-radius-xs);
194
- }
195
- ```
111
+ | — | This element has no slots. |
196
112
 
197
- ## Design tokens
113
+ ## CSS Parts
198
114
 
199
- Styling is fully token-driven: surfaces (`--rel-bg-surface`, `--rel-bg-surface-elevated`), borders (`--rel-border-default`, `--rel-border-strong`, `--rel-border-focus`, `--rel-border-danger`, `--rel-border-subtle`), text (`--rel-text-default`, `--rel-text-label`, `--rel-text-placeholder`, `--rel-text-danger`, `--rel-text-disabled`), selection (`--rel-action-brand-fill`, `--rel-action-brand-label-icons-primary`), spacing, radius, shadows and the `--rel-body-*` typography roles.
115
+ | Part | Description |
116
+ | --- | --- |
117
+ | `base` | Outer wrapper; carries the size / error / readonly classes |
118
+ | `label` | The field label above the trigger |
119
+ | `trigger` | The field box that anchors the panel (wraps the text input and the icons) |
120
+ | `value` | The editable text input showing the formatted time |
121
+ | `icon` | The trailing clock icon |
122
+ | `panel` | The popover panel (`role="dialog"`) |
123
+ | `shortcuts` | The typed-shortcut chip row at the top of the panel (N → Now) |
124
+ | `option` | Each hour / minute / second / AM-PM entry button |
125
+ | `footer` | The Now / Cancel / OK row |
126
+ | `helper` | The helper / error text live region under the field |
127
+
128
+ <!-- readme-gen:end -->
129
+
130
+ ## Accessibility
131
+
132
+ - The editable field is an `<input type="text" role="combobox">` with `aria-haspopup="dialog"`, `aria-expanded`, `aria-controls` pointing at the panel, `aria-required` (from `required`), `aria-invalid` (from `error` / `invalid` / a typed-entry error) and `aria-describedby` pointing at the helper / error text. Its accessible name is the `label` (via `aria-labelledby`), falling back to `aria-label="Select time"`.
133
+ - The clock icon is a button labelled "Open time panel" with its own `aria-expanded`; the optional clear button is labelled "Clear time". The required mark `*` is `aria-hidden`.
134
+ - The panel is `role="dialog"` `aria-label="Choose time"`, promoted to the browser top layer (`popover="manual"`) so a modal / drawer ancestor cannot clip it. Each column is a `role="listbox"` named "Hours", "Minutes", "Seconds" or "AM or PM"; entries are `role="option"` buttons with `aria-selected`, and out-of-range entries are native `disabled`. Each column uses a roving `tabindex` (the selected entry, else the first).
135
+ - The helper / error text (part `helper`) is a persistent `aria-live="polite"` region, so error messages and typed-entry errors are announced as they appear.
136
+ - Focus: opening with the clock icon, `ArrowDown`, `Enter` or by setting `open` moves focus to the selected (or first enabled) hour without scrolling the page; opening by clicking into the field keeps focus in the input so typing continues to work. Cancel, `Escape` and OK return focus to the field; an outside click closes without refocusing.
137
+ - Screen-reader announcement (shared `announce()` utility): "{formatted time} selected" when OK commits the draft.
138
+
139
+ | Key | Where | Action |
140
+ | --- | --- | --- |
141
+ | Typing | Field | Edits the free-form text (see Typed entry); clears any previous parse error |
142
+ | `Enter` | Field | Commit the typed text; if nothing was typed, open the panel (closed) or apply the draft (open) |
143
+ | `ArrowDown` | Field | Open the panel, or move focus into the columns when already open |
144
+ | `Escape` | Field | Revert uncommitted typed text; also closes the panel discarding the draft |
145
+ | Blur | Field | Commit the typed text when focus leaves the component (moving into the panel does not commit) |
146
+ | `ArrowUp` / `ArrowDown` | Column | Move to and select the previous / next enabled entry |
147
+ | `ArrowLeft` / `ArrowRight` | Column | Jump to the previous / next column (Hour → Minute → Second → AM/PM) |
148
+ | `Home` / `End` | Column | Select the first / last enabled entry |
149
+ | `Enter` | Column | Select the entry and apply (OK) |
150
+ | `N` | Panel | Commit the current time immediately (same as the "N → Now" chip) and close |
151
+ | `Escape` | Anywhere | Discard the draft and close |
152
+
153
+ ## Notes / Edge Cases
154
+
155
+ - `value` is always 24-hour `"HH:mm"`, or `"HH:mm:ss"` when `seconds` is set; the `format` prop only changes the field text and the hour column (`12h` shows 01–12 plus an AM/PM column, `24h` shows 00–23). An unparseable `value` renders as empty.
156
+ - `min-time` / `max-time` are `"HH:mm"` (inclusive) and are compared at minute precision even with `seconds`. Hours with no in-range step minute and AM/PM periods with no enabled hour are disabled; picking an hour snaps the draft minute to the nearest in-range step.
157
+ - Draft model: column clicks only update the draft; **OK** commits and fires `rel-change`; **Cancel**, `Escape` or an outside click discard it (and fire `rel-open-change` with `open: false`). The OK button is disabled until an hour is chosen; picking an hour with no minute defaults the minute to `00`; picking a minute or second first defaults the hour to the first enabled one.
158
+ - Two "now" behaviours: the footer **Now** link sets the draft to the current time snapped to `minute-step` (and to the nearest enabled hour) — you still press OK; the `N` shortcut / typed `n` / the "N → Now" chip commit the current time immediately, clamped into `min-time` / `max-time`.
159
+ - Typed entry is not limited to `minute-step`: `9:07` commits `09:07` even with `minute-step="15"`. Typing an empty string and committing behaves like the clear button (`rel-change` with `value: null`).
160
+ - `minute-step` outside 1–60 falls back to 1; the seconds column is always step 1.
161
+ - `invalid` is a non-deprecated alias of `error`; either flag applies error styling. The text below the field is `error-message`, else the typed-entry error, else `helper-text`.
162
+ - Panel widths are fixed per `size` (220 / 250 / 280px, border-box) and capped to the viewport; it flips above the field when there is no room below and shrinks (columns scroll) rather than covering the field. Footer buttons are always small per the Figma spec; OK is narrowed to 60px min-width.
163
+ - Setting `open` programmatically opens the panel and resets the draft from `value`; it does not fire `rel-open-change` (only user interaction does).
164
+ - `fullWidth` reflects as the lowercase `fullwidth` attribute; the host is `inline-block` with intrinsic width otherwise.