@antadesign/anta 0.3.33 → 0.3.35

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 (205) hide show
  1. package/LICENSE.md +14 -1
  2. package/README.md +9 -0
  3. package/dist/anta_helpers.d.ts +46 -30
  4. package/dist/anta_helpers.js +66 -25
  5. package/dist/bundle.css +1 -1
  6. package/dist/bundle.js +2 -1353
  7. package/dist/components/Banner.d.ts +25 -40
  8. package/dist/components/Banner.js +15 -17
  9. package/dist/components/Breadcrumbs.d.ts +0 -1
  10. package/dist/components/Breadcrumbs.js +0 -1
  11. package/dist/components/Button.d.ts +2 -3
  12. package/dist/components/Button.js +1 -0
  13. package/dist/components/ButtonCopy.js +3 -2
  14. package/dist/components/Calendar.js +13 -13
  15. package/dist/components/Card.d.ts +9 -17
  16. package/dist/components/Card.js +50 -46
  17. package/dist/components/Dialog.d.ts +1 -0
  18. package/dist/components/Dialog.js +16 -10
  19. package/dist/components/Expander.d.ts +6 -21
  20. package/dist/components/Expander.js +24 -23
  21. package/dist/components/Icon.d.ts +8 -14
  22. package/dist/components/Input.d.ts +77 -96
  23. package/dist/components/Input.js +23 -27
  24. package/dist/components/InputAutocomplete.d.ts +29 -10
  25. package/dist/components/InputAutocomplete.js +25 -8
  26. package/dist/components/InputDate.d.ts +0 -1
  27. package/dist/components/InputDate.js +2 -3
  28. package/dist/components/InputTime.d.ts +10 -4
  29. package/dist/components/InputTime.js +20 -16
  30. package/dist/components/Loader.d.ts +2 -2
  31. package/dist/components/Menu.d.ts +5 -4
  32. package/dist/components/Menu.js +13 -8
  33. package/dist/components/MenuItem.js +1 -14
  34. package/dist/components/MenuItemCopy.js +2 -2
  35. package/dist/components/Panel.d.ts +3 -1
  36. package/dist/components/Panel.js +17 -11
  37. package/dist/components/Progress.d.ts +21 -23
  38. package/dist/components/Progress.js +13 -9
  39. package/dist/components/RadioGroup.js +6 -8
  40. package/dist/components/{select-parts.css → Select.css} +1 -1
  41. package/dist/components/Select.d.ts +53 -36
  42. package/dist/components/Select.js +53 -98
  43. package/dist/components/SelectFaceted.d.ts +7 -4
  44. package/dist/components/SelectFaceted.js +21 -10
  45. package/dist/components/Slider.d.ts +28 -11
  46. package/dist/components/Slider.js +20 -20
  47. package/dist/components/Steps.css +1 -1
  48. package/dist/components/Steps.d.ts +0 -1
  49. package/dist/components/Steps.js +1 -12
  50. package/dist/components/Tabs.d.ts +17 -20
  51. package/dist/components/Tabs.js +7 -9
  52. package/dist/components/Text.d.ts +55 -36
  53. package/dist/components/Text.js +35 -22
  54. package/dist/components/Title.d.ts +19 -27
  55. package/dist/components/Title.js +2 -1
  56. package/dist/components/Toaster.js +9 -2
  57. package/dist/components/Tooltip.d.ts +10 -15
  58. package/dist/components/copy-props.d.ts +14 -3
  59. package/dist/components/copy-props.js +14 -3
  60. package/dist/components/select-tree.d.ts +24 -0
  61. package/dist/components/select-tree.js +35 -0
  62. package/dist/element-base.d.ts +10 -0
  63. package/dist/element-base.js +15 -0
  64. package/dist/elements/a-avatar.css +1 -1
  65. package/dist/elements/a-avatar.js +2 -28
  66. package/dist/elements/a-banner.css +1 -1
  67. package/dist/elements/a-banner.d.ts +4 -1
  68. package/dist/elements/a-banner.js +22 -112
  69. package/dist/elements/a-breadcrumbs.d.ts +1 -0
  70. package/dist/elements/a-breadcrumbs.js +1 -0
  71. package/dist/elements/a-button.css +1 -1
  72. package/dist/elements/a-button.d.ts +10 -7
  73. package/dist/elements/a-button.js +18 -1
  74. package/dist/elements/a-calendar.d.ts +14 -13
  75. package/dist/elements/a-calendar.js +29 -24
  76. package/dist/elements/a-card.css +1 -1
  77. package/dist/elements/a-card.d.ts +0 -9
  78. package/dist/elements/a-card.js +0 -184
  79. package/dist/elements/a-checkbox.css +1 -1
  80. package/dist/elements/a-copy.css +1 -1
  81. package/dist/elements/a-copy.d.ts +1 -5
  82. package/dist/elements/a-copy.js +129 -108
  83. package/dist/elements/a-dialog.css +1 -1
  84. package/dist/elements/a-dialog.d.ts +1 -1
  85. package/dist/elements/a-dialog.js +68 -194
  86. package/dist/elements/a-expander.css +1 -1
  87. package/dist/elements/a-expander.d.ts +4 -30
  88. package/dist/elements/a-expander.js +85 -344
  89. package/dist/elements/a-icon.d.ts +5 -23
  90. package/dist/elements/a-icon.js +1 -2
  91. package/dist/elements/a-input-date.d.ts +1 -0
  92. package/dist/elements/a-input-date.js +1 -0
  93. package/dist/elements/a-input-time.css +1 -1
  94. package/dist/elements/a-input-time.d.ts +2 -1
  95. package/dist/elements/a-input-time.js +101 -168
  96. package/dist/elements/a-input.css +1 -1
  97. package/dist/elements/a-input.d.ts +10 -8
  98. package/dist/elements/a-input.js +155 -230
  99. package/dist/elements/a-loader.d.ts +5 -8
  100. package/dist/elements/a-loader.js +1 -2
  101. package/dist/elements/a-menu-item.css +1 -1
  102. package/dist/elements/a-menu-item.d.ts +5 -3
  103. package/dist/elements/a-menu-item.js +5 -1
  104. package/dist/elements/a-menu.css +1 -1
  105. package/dist/elements/a-menu.d.ts +5 -7
  106. package/dist/elements/a-menu.js +383 -299
  107. package/dist/elements/a-panel.css +1 -1
  108. package/dist/elements/a-panel.js +19 -37
  109. package/dist/elements/a-progress.css +1 -1
  110. package/dist/elements/a-progress.d.ts +0 -22
  111. package/dist/elements/a-progress.js +0 -100
  112. package/dist/elements/a-radio-group.d.ts +1 -1
  113. package/dist/elements/a-radio-group.js +6 -4
  114. package/dist/elements/a-select-faceted.d.ts +1 -0
  115. package/dist/elements/a-select-faceted.js +1 -0
  116. package/dist/elements/a-select.d.ts +1 -0
  117. package/dist/elements/a-select.js +1 -0
  118. package/dist/elements/a-slider.css +1 -1
  119. package/dist/elements/a-slider.d.ts +11 -3
  120. package/dist/elements/a-slider.js +244 -242
  121. package/dist/elements/a-steps.d.ts +1 -0
  122. package/dist/elements/a-steps.js +1 -0
  123. package/dist/elements/a-switch.css +1 -1
  124. package/dist/elements/a-tab.css +1 -1
  125. package/dist/elements/a-tabpanel.d.ts +2 -2
  126. package/dist/elements/a-tabpanel.js +13 -3
  127. package/dist/elements/a-tabs.css +1 -1
  128. package/dist/elements/a-tabs.d.ts +1 -1
  129. package/dist/elements/a-tabs.js +6 -4
  130. package/dist/elements/a-tag.css +1 -1
  131. package/dist/elements/a-tag.d.ts +1 -0
  132. package/dist/elements/a-tag.js +1 -0
  133. package/dist/elements/a-text.css +1 -1
  134. package/dist/elements/a-text.d.ts +6 -43
  135. package/dist/elements/a-text.js +110 -193
  136. package/dist/elements/a-title.css +1 -1
  137. package/dist/elements/a-title.d.ts +1 -0
  138. package/dist/elements/a-title.js +1 -0
  139. package/dist/elements/a-toast.css +1 -1
  140. package/dist/elements/a-toast.d.ts +10 -45
  141. package/dist/elements/a-toast.js +80 -66
  142. package/dist/elements/a-toaster.css +1 -1
  143. package/dist/elements/a-toaster.js +9 -44
  144. package/dist/elements/a-tooltip.css +1 -1
  145. package/dist/elements/a-tooltip.d.ts +1 -1
  146. package/dist/elements/a-tooltip.js +142 -136
  147. package/dist/elements/copy-behavior.d.ts +1 -1
  148. package/dist/elements/copy-behavior.js +21 -21
  149. package/dist/elements/index.d.ts +9 -3
  150. package/dist/elements/index.js +6 -6
  151. package/dist/elements/input-focus-modality.d.ts +15 -0
  152. package/dist/elements/input-focus-modality.js +44 -0
  153. package/dist/elements/input-trailing-actions.d.ts +5 -0
  154. package/dist/elements/input-trailing-actions.js +38 -0
  155. package/dist/elements/shadow-aria.d.ts +2 -0
  156. package/dist/elements/shadow-aria.js +2 -1
  157. package/dist/elements/value-input.d.ts +3 -0
  158. package/dist/elements/value-input.js +12 -0
  159. package/dist/general_types.d.ts +103 -96
  160. package/dist/index.d.ts +1 -1
  161. package/dist/input-events.d.ts +4 -0
  162. package/dist/input-events.js +15 -0
  163. package/dist/jsx-runtime.d.ts +70 -23
  164. package/dist/jsx-runtime.js +26 -19
  165. package/dist/native-event-props.d.ts +2 -0
  166. package/dist/native-event-props.js +10 -0
  167. package/dist/reset.css +1 -1
  168. package/dist/theme-antithesis.css +10 -1
  169. package/dist/theme-antune.css +1 -1
  170. package/docs/accessibility.md +17 -3
  171. package/docs/changelog.md +384 -2
  172. package/docs/comparison.md +2 -2
  173. package/docs/components/avatar.md +5 -0
  174. package/docs/components/banner.md +117 -187
  175. package/docs/components/breadcrumbs.md +9 -6
  176. package/docs/components/button.md +22 -0
  177. package/docs/components/card.md +74 -28
  178. package/docs/components/checkbox.md +9 -10
  179. package/docs/components/dialog.md +83 -32
  180. package/docs/components/expander.md +198 -435
  181. package/docs/components/icon.md +11 -17
  182. package/docs/components/input-autocomplete.md +152 -52
  183. package/docs/components/input-date.md +175 -148
  184. package/docs/components/input-time.md +81 -38
  185. package/docs/components/input.md +343 -349
  186. package/docs/components/loader.md +2 -1
  187. package/docs/components/menu.md +35 -6
  188. package/docs/components/panel.md +18 -0
  189. package/docs/components/progress.md +83 -49
  190. package/docs/components/radio.md +39 -43
  191. package/docs/components/select-faceted.md +54 -7
  192. package/docs/components/select.md +390 -386
  193. package/docs/components/slider.md +136 -19
  194. package/docs/components/steps.md +6 -3
  195. package/docs/components/switch.md +8 -9
  196. package/docs/components/tabs.md +110 -104
  197. package/docs/components/text.md +110 -86
  198. package/docs/components/title.md +47 -33
  199. package/docs/components/toaster.md +242 -84
  200. package/docs/components/tooltip.md +204 -113
  201. package/docs/install-config.md +461 -96
  202. package/docs/packages/plot.md +228 -143
  203. package/docs/theming.md +29 -13
  204. package/package.json +43 -4
  205. package/theme-antithesis-license.md +66 -0
@@ -1,49 +1,46 @@
1
1
  # InputDate
2
2
 
3
- **`InputDate`** is a date field that replaces the native `type="date"` (and, with
4
- `time`, `type="datetime-local"`): a text field you can type into, backed by a
5
- `Calendar` that opens when you click the field (or press ↓), marked by a
6
- leading calendar icon. `Calendar`, the month grid it builds on, is documented lower
7
- down.
8
-
9
- The field accepts free text and resolves it on commit (blur / Enter): a lenient
10
- parser reads the locale's order (`06/07/2026` is June 7 in en-US, July 6 in de-DE),
11
- month names, and run-together digits, then rewrites the entry to the canonical
12
- format. An unrecognized entry marks the field until you fix it. The value is an ISO
13
- `YYYY-MM-DD` string, never a `Date`; it works controlled (`value` + `onValueChange`)
14
- or uncontrolled (`defaultValue`), and submits under `name`.
3
+ Use InputDate to enter a date by typing or choosing a day from a calendar.
4
+ Add `time` when the task also needs a time of day. Use [Calendar](#calendar)
5
+ directly when the month grid should stay visible without a field.
6
+
7
+ Render InputDate and Calendar only on the client. Their current date and locale
8
+ can differ between server and browser rendering. In Astro, use `client:only`.
9
+ See [Server and worker rendering](../install-config.md#server-and-worker-rendering) for
10
+ setup guidance.
15
11
 
16
12
  ## The date field
17
13
 
18
- Type a date and it resolves on blur or Enter, or click the field to open
19
- the calendar and pick a day. Either way the field settles on the locale's canonical
20
- format and the value is ISO `YYYY-MM-DD`. A click keeps focus in the field, so you
21
- can keep typing or click a day; ↓ opens the calendar and moves focus into
22
- the grid, and Esc closes it back to the field. While you type, the calendar
23
- previews the entry — it jumps to that month and highlights the day — and commits it on
24
- Enter (which also closes the calendar); an unrecognized entry keeps the
25
- calendar open and marks the field.
14
+ Enter a date and press Enter or leave the field to confirm it.
15
+ InputDate accepts the locale's date order, month names, and dates without
16
+ separators, then formats the result. An unrecognized entry stays visible and
17
+ marks the field invalid so it can be corrected.
18
+
19
+ Select the field to open its calendar while keeping focus in the text.
20
+ Press ↓ to open the calendar and focus a day. Press Esc
21
+ to close it and return focus to the field. While you type, the calendar previews
22
+ recognized dates. Pressing Enter confirms the date and closes the
23
+ calendar. An unrecognized entry keeps the calendar open.
26
24
 
27
25
  ```tsx
28
26
  <InputDate label="Due date" defaultValue="2026-06-15" onValueChange={(v) => save(v)} />
29
27
  ```
30
28
 
31
- The placeholder shows the locale's format, and parsing follows the same order, so
32
- `de-DE` reads and writes `TT.MM.JJJJ` while `en-US` uses `MM/DD/YYYY`. `min` / `max`
33
- bound both the calendar and what a typed date will accept.
29
+ Set `locale` to choose the date format. For example, `06/07/2026` means June 7
30
+ in `en-US` and July 6 in `de-DE`. The placeholder shows the expected format.
31
+ Use `min` and `max` to limit both typed dates and calendar choices.
34
32
 
35
33
  ```tsx
36
34
  <InputDate label="US English" locale="en-US" defaultValue="2026-06-15" />
37
35
  <InputDate label="German" locale="de-DE" defaultValue="2026-06-15" />
38
36
  ```
39
37
 
40
- `time` turns it into a date-time field (value `YYYY-MM-DDTHH:mm`). The menu grows a
41
- time row under the calendar: an [`InputTime`](./input-time.md) (segmented hour / minute,
42
- plus AM/PM in 12-hour locales) and a confirm (✓) button. Picking a day keeps the menu
43
- open so you can set the time. The clock follows the locale (`en-US` is 12-hour, most
44
- others 24), overridable with `hour12`. The field parses a trailing time after a space
45
- (`06/15/2026 2:30pm` or `… 14:30`), and typing a 24-hour hour into the 12-hour time
46
- field converts it (18 → 6 PM).
38
+ Pass `time` to add a time field and confirmation button to the calendar.
39
+ Choosing a day keeps the calendar open so the time can be entered before
40
+ confirming. The clock follows the locale. Use `hour12` to override it.
41
+
42
+ You can also type a date and time together, such as `06/15/2026 2:30pm` or
43
+ `06/15/2026 14:30`. In a 12-hour time field, entering `18` converts to `6 PM`.
47
44
 
48
45
  ```tsx
49
46
  {/* `size` scales the calendar, the time inputs, the AM/PM toggle, and the buttons together. */}
@@ -52,6 +49,19 @@ field converts it (18 → 6 PM).
52
49
  <InputDate size="large" label="Large" time defaultValue="2026-06-15T09:00" />
53
50
  ```
54
51
 
52
+ ### Manage the value
53
+
54
+ Use `defaultValue` for an initial date, or pair `value` with `onValueChange`
55
+ to control it. InputDate reports an ISO string: `YYYY-MM-DD`, or
56
+ `YYYY-MM-DDTHH:mm` with `time`. An empty field reports `''`. Add `name` to
57
+ submit that value with a form.
58
+
59
+ ```tsx
60
+ const [date, setDate] = useState('2026-06-15')
61
+
62
+ <InputDate label="Due date" name="due-date" value={date} onValueChange={setDate} />
63
+ ```
64
+
55
65
  ## InputDate props
56
66
 
57
67
  | Prop | Type | Default | Description |
@@ -79,35 +89,18 @@ field converts it (18 → 6 PM).
79
89
 
80
90
  ## Calendar
81
91
 
82
- The sections below document `Calendar`, the month grid `InputDate` opens. Reach for
83
- it directly when you want the grid inline, without a field.
84
-
85
- `Calendar` uses Anta components in light DOM; it has no hidden shadow-root UI.
86
- The day cells and previous/next chevrons are `<Button>`s inside a
87
- form-associated `<a-calendar>` grid. Date calculations use the
88
- [Temporal API](https://tc39.es/proposal-temporal/docs/) through
89
- `temporal-polyfill`.
90
-
91
- For a raw grid, import `buildMonth` from `@antadesign/anta`. It returns
92
- localized weekday headers and day records with ISO values, accessible labels,
93
- and selected, disabled, today, and outside-month state. It does not manage
94
- navigation, roving focus, selection, or rendering. The value is an ISO
95
- `YYYY-MM-DD` string, never a `Date`. Use `value` with `onStateChange` to
96
- control it, or `defaultValue` for uncontrolled use. It participates in forms
97
- under `name`.
98
-
99
- **Not SSR-safe.** `Calendar` (and `InputDate`, which embeds it) reads the current
100
- date (`Temporal.Now`) and locale (`navigator.language`) at render, so a server
101
- render disagrees with the client on the highlighted "today" and on localized
102
- labels, and the page hydrates with a mismatch. Render it client-side only: Astro
103
- `client:only`, or a dynamic import inside `useEffect`. Every demo on this page uses
104
- `client:only` for that reason.
92
+ Use Calendar for an inline date picker, such as a booking form with a month
93
+ always in view. It provides day selection, month navigation, keyboard access,
94
+ and form submission.
95
+
96
+ Like InputDate, Calendar must render only on the client. The examples use
97
+ Astro's `client:only` directive.
105
98
 
106
99
  ## Selecting a date
107
100
 
108
- Click a day, or focus the grid and press Enter / Space. The
109
- selected day takes the tertiary-button selected look toned brand — a brand-tinted
110
- filled cell with a ring. Today shows as a secondary Button, a resting fill.
101
+ Select a day, or focus it and press Enter or Space.
102
+ The selected day has a brand-colored fill and ring. Today's date has a subtle
103
+ fill when it is not selected.
111
104
 
112
105
  ```tsx
113
106
  <Calendar defaultValue="2026-06-12" />
@@ -160,9 +153,8 @@ button are disabled, and only the in-range days stay selectable.
160
153
 
161
154
  ## Locale
162
155
 
163
- `Calendar` respects the browser locale by default (`navigator.language`). Pass a
164
- BCP-47 `locale` to override it: the first day of week comes from the locale's week
165
- info (never hard-coded to Sunday), and the weekday and month names are localized.
156
+ Calendar uses the browser's locale by default. Pass a locale tag such as
157
+ `en-US`, `fr-FR`, or `ja-JP` to choose the language and first day of the week.
166
158
 
167
159
  ```tsx
168
160
  <Calendar defaultValue="2026-06-28" locale="en-US" /> {/* week starts Sunday */}
@@ -200,9 +192,9 @@ vetoes it (uncontrolled). A controlled calendar with no `onStateChange` is read-
200
192
 
201
193
  ## Forms
202
194
 
203
- `Calendar` is form-associated. Give it a `name` and it submits the selected ISO
204
- date under that key, resets to `defaultValue` on form reset, and reports through
205
- the standard constraint-validation pipeline.
195
+ Add `name` to submit the selected ISO date with a form. An uncontrolled Calendar
196
+ returns to `defaultValue` on form reset. For a controlled Calendar, handle the
197
+ reset request in `onStateChange` and update `value` as needed.
206
198
 
207
199
  ```tsx
208
200
  <form>
@@ -212,19 +204,13 @@ the standard constraint-validation pipeline.
212
204
 
213
205
  ## Accessibility
214
206
 
215
- The focused Input field is associated directly with its Calendar dialog through
216
- ARIA element reflection. The relationship does not require a generated popup ID,
217
- so separate application roots can render InputDate instances without ID
218
- collisions. An explicitly authored relationship on a raw Anta Input takes
219
- precedence.
207
+ InputDate identifies its calendar as a dialog associated with the field.
208
+ Opening it with ↓ moves focus into the calendar. Closing it with
209
+ Esc returns focus to the field.
220
210
 
221
- The calendar is a labelled **group** of day buttons, a flat grid rather than a
222
- nested `grid`/`row`/`gridcell` tree. The visible month names the group directly.
223
- Each day is a `<button>` whose `aria-label` is its full date
224
- including weekday ("Sunday, June 28, 2026"), with `aria-pressed` on the selected day
225
- and `aria-current="date"` on today. The decorative weekday headers are `aria-hidden`,
226
- since each day already names its weekday. A roving `tabindex` keeps a single tab
227
- stop, and an `aria-live` region announces the month when it changes.
211
+ Calendar announces the displayed month and gives each day its full date,
212
+ including the weekday. Screen readers can identify the selected day and today's
213
+ date. The day grid has one Tab stop, and changing months announces the new month.
228
214
 
229
215
  | Key | Moves focus |
230
216
  |---|---|
@@ -237,53 +223,38 @@ stop, and an `aria-live` region announces the month when it changes.
237
223
 
238
224
  ## Customization
239
225
 
240
- `InputDate` uses Input, Menu, and Calendar. Calendar uses an `<a-calendar>`
241
- element for the day grid and a JSX wrapper for navigation. The sections below
242
- describe which layer provides each behavior and how to recreate the interface
243
- without React.
226
+ Use the InputDate and Calendar wrappers for the complete date-entry interface.
227
+ For custom calendar rendering, `buildMonth` supplies localized weekday headers
228
+ and day records with ISO values, accessible labels, and selection and range
229
+ states. Your application supplies rendering and navigation.
244
230
 
245
231
  ### Build it from elements
246
232
 
247
- `InputDate` has no `a-inputdate` element. Its wrapper renders an editable
248
- `<Input>` and a `<Menu>` containing a `<Calendar>`. When `time` is set, the
249
- hour/minute fields and AM/PM `Tabs` appear below the grid. The wrapper manages
250
- draft text, parsing on commit, and the ISO value.
251
-
252
- Clicking the field opens the menu while keeping focus in the field. Pressing
253
- ↓ opens the menu and moves focus to the grid. The wrapper passes a
254
- `focusSignal` to Calendar, which renders `data-focus`; `<a-calendar>` then
255
- focuses the active day. Calendar and Tabs emit their own `statechange` events,
256
- but Menu responds only to changes in its own open state.
257
-
258
- The `<a-calendar>` element provides grid behavior. It connects the supplied day
259
- `<button>`s to click and keyboard navigation, including arrows, Home,
260
- End, PageUp, and PageDown. It stores the form
261
- value through `ElementInternals` and emits `statechange` / `change` on selection
262
- and `navigate` when keyboard navigation leaves the displayed month. It is
263
- framework-agnostic and does not track a current month, `min`, or `max`.
264
-
265
- The JSX wrapper renders the controls above the grid as siblings of
266
- `<a-calendar>`. The previous/next arrows are `<Button>`s. The month/year picker
267
- is a `<Button>` that opens a `<Menu>` of year `MenuItem`s, each with a month
268
- submenu. The wrapper stores the displayed-month cursor in state and derives day
269
- cells and the arrows' `disabled` states from `min` and `max`. A click on an
270
- arrow, a menu selection, or a `navigate` event updates the cursor and re-renders
271
- the grid. The wrapper enforces the range. The element only ignores a day that
272
- was rendered with `disabled`.
273
-
274
- The element has no `min` or `max` because the wrapper provides the surrounding
275
- controls. Without React, render the grid with `buildMonth` and add the
276
- navigation behavior shown below:
233
+ InputDate combines Input, Menu, and Calendar. There is no separate InputDate host
234
+ element. Recreating it requires text parsing, date formatting, popup behavior,
235
+ and focus management. For a form that can use the browser's picker, consider
236
+ [Native HTML date input](#native-html-date-input).
237
+
238
+ For a custom month grid, use `<a-calendar>` with day buttons marked
239
+ `data-part="day-cell"` and `data-date="YYYY-MM-DD"`. It provides keyboard
240
+ navigation, selection requests, and form submission. Render the surrounding
241
+ month controls yourself and handle `navigate` when focus moves to another date.
242
+
243
+ `buildMonth` returns the month heading, weekdays, and day records. Pass `min`
244
+ and `max` to it when building a bounded grid, render out-of-range dates with
245
+ `disabled`, and limit your month controls to that range.
246
+
247
+ This example supplies previous and next month controls and a controlled
248
+ selection:
277
249
 
278
250
  ```html
279
251
  <!-- `.calendar` scopes this example. Use an application selector instead. -->
280
252
  <div class="calendar" style="display: inline-flex; flex-direction: column; gap: 6px">
281
253
  <div style="display: flex; align-items: center">
282
- <a-button priority="tertiary" data-prev aria-label="Previous month"><a-icon shape="chevron-left"></a-icon></a-button>
283
- <a-button priority="tertiary" data-heading style="flex: 1; justify-content: center" aria-haspopup="menu"></a-button>
284
- <a-button priority="tertiary" data-next aria-label="Next month"><a-icon shape="chevron-right"></a-icon></a-button>
254
+ <a-button role="button" tabindex="0" priority="tertiary" data-prev aria-label="Previous month"><a-icon shape="chevron-left" aria-hidden="true"></a-icon></a-button>
255
+ <span data-heading style="flex: 1; text-align: center"></span>
256
+ <a-button role="button" tabindex="0" priority="tertiary" data-next aria-label="Next month"><a-icon shape="chevron-right" aria-hidden="true"></a-icon></a-button>
285
257
  </div>
286
- <!-- The element provides selection, keyboard behavior, form value, and events. -->
287
258
  <a-calendar role="group" name="date"></a-calendar>
288
259
  </div>
289
260
  <script type="module">
@@ -295,40 +266,99 @@ navigation behavior shown below:
295
266
  const grid = root.querySelector('a-calendar')
296
267
  const heading = root.querySelector('[data-heading]')
297
268
  let cursor = Temporal.Now.plainDateISO()
269
+ grid.value = cursor.toString()
270
+
271
+ function focusDate(iso) {
272
+ grid.querySelector(`[data-date="${iso}"]`)?.focus()
273
+ }
298
274
 
299
275
  function render() {
300
- const m = buildMonth({ anchor: cursor, locale: navigator.language })
301
- heading.textContent = m.heading
276
+ const month = buildMonth({
277
+ anchor: cursor,
278
+ locale: navigator.language,
279
+ selected: parseISODate(grid.value),
280
+ })
281
+ heading.textContent = month.heading
282
+ grid.setAttribute('aria-label', month.heading)
302
283
  grid.replaceChildren()
303
- for (const w of m.weekdays) {
304
- const s = Object.assign(document.createElement('span'), { textContent: w.narrow })
305
- s.setAttribute('data-part', 'weekday')
306
- s.setAttribute('aria-hidden', 'true')
307
- grid.append(s)
284
+ for (const weekday of month.weekdays) {
285
+ const label = Object.assign(document.createElement('span'), { textContent: weekday.narrow })
286
+ label.setAttribute('data-part', 'weekday')
287
+ label.setAttribute('aria-hidden', 'true')
288
+ grid.append(label)
308
289
  }
309
- for (const d of m.weeks.flat()) {
310
- const b = Object.assign(document.createElement('a-button'), { textContent: String(d.day) })
311
- b.setAttribute('priority', 'tertiary')
312
- b.setAttribute('data-part', 'day-cell')
313
- b.setAttribute('data-date', d.iso) // the element keys off this
314
- b.tabIndex = d.iso === cursor.toString() ? 0 : -1
315
- if (d.selected) b.setAttribute('selected', '')
316
- if (d.disabled) b.setAttribute('disabled', '')
317
- grid.append(b)
290
+ for (const day of month.weeks.flat()) {
291
+ const button = document.createElement('a-button')
292
+ const label = Object.assign(document.createElement('a-button-label'), { textContent: String(day.day) })
293
+ button.append(label)
294
+ button.setAttribute('role', 'button')
295
+ button.setAttribute('priority', day.today && !day.selected ? 'secondary' : 'tertiary')
296
+ button.setAttribute('data-part', 'day-cell')
297
+ button.setAttribute('data-date', day.iso)
298
+ button.setAttribute('aria-label', day.label)
299
+ button.setAttribute('aria-pressed', String(day.selected))
300
+ button.tabIndex = day.iso === cursor.toString() ? 0 : -1
301
+ if (day.selected) {
302
+ button.setAttribute('selected', '')
303
+ button.setAttribute('tone', 'brand')
304
+ }
305
+ if (day.disabled) button.setAttribute('disabled', '')
306
+ if (day.today) {
307
+ button.setAttribute('data-today', '')
308
+ button.setAttribute('aria-current', 'date')
309
+ }
310
+ if (day.outside) button.setAttribute('data-outside', '')
311
+ grid.append(button)
318
312
  }
319
313
  }
320
314
 
321
- root.querySelector('[data-prev]').addEventListener('click', () => { cursor = cursor.subtract({ months: 1 }); render() })
322
- root.querySelector('[data-next]').addEventListener('click', () => { cursor = cursor.add({ months: 1 }); render() })
323
- // Keyboard paging past the rendered grid: the element asks for a month flip.
324
- grid.addEventListener('navigate', (e) => { cursor = parseISODate(e.detail.date) ?? cursor; render() })
315
+ root.querySelector('[data-prev]').addEventListener('click', () => {
316
+ cursor = cursor.subtract({ months: 1 })
317
+ render()
318
+ })
319
+ root.querySelector('[data-next]').addEventListener('click', () => {
320
+ cursor = cursor.add({ months: 1 })
321
+ render()
322
+ })
323
+ grid.addEventListener('statechange', (event) => {
324
+ grid.value = event.detail.next ?? ''
325
+ cursor = parseISODate(grid.value) ?? cursor
326
+ render()
327
+ focusDate(cursor.toString())
328
+ })
329
+ grid.addEventListener('navigate', (event) => {
330
+ cursor = parseISODate(event.detail.date) ?? cursor
331
+ render()
332
+ focusDate(cursor.toString())
333
+ })
325
334
  render()
326
335
  </script>
327
336
  ```
328
337
 
329
- Tracking the cursor, painting the grid from `buildMonth`, and re-rendering on
330
- `navigate` is what the React `Calendar` packages, plus the year/month jump menu,
331
- which uses the same `Menu` composition shown on the [Menu](./menu.md) page.
338
+ Use the Calendar wrapper when you also need its year/month jump menu and
339
+ range-aware controls.
340
+
341
+ ### Raw Calendar value and form API
342
+
343
+ Use `value` for controlled selection or `defaultvalue` for an uncontrolled
344
+ initial and reset date. The `.value` property returns the applied ISO date, or
345
+ `null` when empty. The `.defaultValue` property returns the authored reset date,
346
+ or `null` when unset.
347
+
348
+ Assigning a string to `.value` supplies controlled input. An empty `value=""`
349
+ keeps the calendar controlled and empty. Remove `value`, or assign `null` or
350
+ `undefined` to `.value`, to release control while retaining the applied date.
351
+ Uncontrolled picks, reset, and restoration leave those input attributes unchanged.
352
+
353
+ Accept controlled picks from `statechange` by updating `value`. `change` reports
354
+ applied selection. `navigate` requests a new focus date. The application renders
355
+ the requested month and focuses that day. Author the month label and each day
356
+ button's accessible name. Calendar does not generate those labels.
357
+
358
+ For Preact, prefer the Calendar wrapper. Raw `<a-calendar value={...}>` bindings
359
+ can retain the old value when assigned `undefined`, or write `value=""` when the
360
+ prop is omitted. Release a raw instance by removing its `value` attribute or
361
+ assigning `null` to `.value`.
332
362
 
333
363
  ## Calendar props
334
364
 
@@ -350,11 +380,8 @@ which uses the same `Menu` composition shown on the [Menu](./menu.md) page.
350
380
 
351
381
  ## Web Component
352
382
 
353
- `InputDate` has no host element. Rebuilding its text parsing, dropdown calendar,
354
- localized day grid, month navigation, keyboard navigation, and focus management
355
- in vanilla JavaScript is significant work, even with Anta's `buildMonth` and
356
- `parseISODate` helpers. Use the `InputDate` wrapper when React or Preact is an
357
- option. Otherwise, a native HTML date input is the practical baseline.
383
+ Use a native date input when the browser's picker fits your form. To build a
384
+ custom picker without JSX, see [Build it from elements](#build-it-from-elements).
358
385
 
359
386
  ### Native HTML date input
360
387
 
@@ -377,12 +404,12 @@ field treatments.
377
404
 
378
405
  ## Styling
379
406
 
380
- The calendar is **light DOM**, so everything is plain CSS, with no shadow parts or
381
- hidden internals. The days and chevrons are real `<a-button>`s, so their colors,
382
- hover, focus ring, and selected look come from the Button styles and theme
383
- themselves; they sit inside the `<a-calendar>` grid, and the switcher header sits in
384
- the `.anta-calendar` shell. An un-layered consumer rule beats `@layer anta` without
385
- `!important`. The class below is for the demo.
407
+ Use ordinary CSS to style the calendar and its day buttons. `className` applies
408
+ to the outer `.anta-calendar` container. Unlayered consumer rules override Anta's
409
+ defaults. Replace the example classes with your own selectors.
410
+
411
+ Set `--calendar-cell-width` and `--calendar-cell-height` to make day targets
412
+ larger, then use `border-radius` to shape them:
386
413
 
387
414
  ```css
388
415
  /* Bigger, circular day cells. `className` lands on the outer .anta-calendar
@@ -413,5 +440,5 @@ brand dot and one is flagged in the critical color.
413
440
  }
414
441
  ```
415
442
 
416
- The engine (`buildMonth`, `firstDayOfWeek`, …) is exported, so you can render a
417
- fully custom grid and keep only the `<a-calendar>` form plumbing.
443
+ For a custom month layout, use the exported `buildMonth` and `firstDayOfWeek`
444
+ helpers. See [Build it from elements](#build-it-from-elements).
@@ -1,33 +1,33 @@
1
1
  # InputTime
2
2
 
3
- **`InputTime`** is a segmented wall-clock field: one boxed input, like the others,
4
- holding separate **hour** and **minute** sections — plus an **AM/PM** section in
5
- 12-hour locales — that behave as a single control. Each section is a native text
6
- input:
7
- ↑/↓ steps its value (wrapping 59 → 00), ←/→
8
- moves between sections at the caret boundary, and typing a digit fills the section
9
- and advances to the next once it can't hold more. AM/PM accepts its locale text or
10
- `a` / `p`.
11
-
12
- A leading clock icon marks the affordance (change it with `icon`). Unlike a text
13
- `Input` (which fills its container), the field **sizes to its content** — a time
14
- is a small, fixed control — so set a `width` (e.g. `style={{ width: '100%' }}`) if
15
- you want it to fill. The value is a 24-hour `"HH:mm"` string (`''` until both hour
16
- and minute are set), never a `Date`. It works controlled (`value` + `onValueChange`)
17
- or uncontrolled (`defaultValue`), and submits under `name` via the native form APIs.
3
+ Use InputTime for a time of day, such as a meeting start or office hours.
4
+ The field provides hour and minute sections, with AM/PM in 12-hour locales.
5
+ Enter digits or use the arrow keys to adjust each section.
6
+
7
+ The value is a 24-hour `HH:mm` string, or `''` while the time is incomplete.
8
+ Use `defaultValue` for an initial time, or pair `value` with `onValueChange`
9
+ to control it. Add `name` to submit the value with a form.
18
10
 
19
11
  ## The field
20
12
 
21
- Focus a section and edit it with the keyboard: ↑/↓ to step (it
22
- wraps), PageUp/PageDown for a larger jump on minutes,
23
- or enter the digits. Home/End, selection, deletion, and
24
- paste behave as they do in a native text input. Typing
25
- `9` then `3` `0` lands `09:30` and moves across the sections on its own; an
26
- out-of-range digit (a `6` in the minutes) commits and advances immediately.
27
- Backspace on an empty section selects the previous one. The whole thing
28
- reads as one field while preserving native caret behavior. In a
29
- 12-hour field, typing a 24-hour hour converts it and flips AM/PM — `18` becomes
30
- `6 PM`, so pasted 24-hour times work.
13
+ Focus a section and enter digits. The caret advances when the section is
14
+ complete. For example, entering `9`, `3`, and `0` produces `09:30`.
15
+ In a 12-hour field, entering `18` sets `6 PM`. The period section accepts its
16
+ localized text or `a` and `p`.
17
+
18
+ Use the keyboard to adjust or move between sections:
19
+
20
+ - ↑ and ↓ step the value, wrapping at its limits.
21
+ - ← and → move between sections at the caret boundary.
22
+ - PageUp and PageDown make larger minute adjustments.
23
+ - Backspace in an empty section selects the previous section.
24
+
25
+ Home, End, selection, deletion, and paste keep their native text-field behavior.
26
+ When one digit cannot grow into a valid two-digit value, the field confirms
27
+ that section and advances. Entering `6` in minutes, for example, sets `06`.
28
+
29
+ The field sizes to its content. Set `width` when it should fill its container.
30
+ Use `icon` to replace the leading clock, or `icon={false}` to remove it.
31
31
 
32
32
  ```tsx
33
33
  <InputTime label="Start time" defaultValue="09:30" onValueChange={(_, { value }) => save(value)} />
@@ -35,12 +35,13 @@ reads as one field while preserving native caret behavior. In a
35
35
 
36
36
  ## Locale
37
37
 
38
- The clock follows the locale: `en-US` shows 12-hour with an AM/PM section, most
39
- others show 24-hour with none. It's derived from `Intl` (the resolved `hourCycle`),
40
- and the section **order**, the **separator**, and the **AM/PM text** all come from
41
- the locale too — Japanese renders the period first (`午後2:05`), Finnish separates
42
- with a dot (`14.05`), and 24-hour locales drop the AM/PM section entirely. Set
43
- `hour12` to force one clock regardless of locale.
38
+ Set `locale` to choose the clock format, section order, separator, and AM/PM
39
+ text. For example, `en-US` uses a 12-hour clock and `de-DE` uses a 24-hour clock.
40
+ Japanese can place the period first, and Finnish uses a dot separator.
41
+ Pass `hour12` or `hour12={false}` to override the locale's clock format.
42
+
43
+ The period section reserves enough space for either label, so switching AM/PM
44
+ does not change the field's width.
44
45
 
45
46
  ```tsx
46
47
  <InputTime label="US English" locale="en-US" defaultValue="14:05" /> {/* 12-hour: 02 : 05 PM */}
@@ -54,7 +55,7 @@ with a dot (`14.05`), and 24-hour locales drop the AM/PM section entirely. Set
54
55
  `size` (`small` / `medium` / `large`) matches the other inputs' scale; `status`
55
56
  recolors the box and hint (only `critical` marks it invalid); `clearable` adds a
56
57
  clear button once a value is set — clearing empties every section back to the
57
- placeholder dashes. `disabled` behaves as it does on `Input`.
58
+ placeholder dashes. `disabled` prevents focus and editing. `readOnly` keeps the time selectable and focusable while preventing edits.
58
59
 
59
60
  `min` and `max` (24-hour `"HH:mm"`) bound the value: a complete time outside the
60
61
  range is clamped to the nearest bound as you step or leave the field, and flags
@@ -66,6 +67,25 @@ range is clamped to the nearest bound as you step or leave the field, and flags
66
67
  <InputTime label="Office hours" size="large" min="09:00" max="17:00" defaultValue="09:30" />
67
68
  ```
68
69
 
70
+ ## Controlled values and clearing
71
+
72
+ Update `value` from `onValueChange` to control the time. An incomplete time reports
73
+ an empty string while the native segments retain the text being edited. Call
74
+ `preventDefault()` in `onClearClick` to keep a value when clearing is disallowed.
75
+
76
+ ```tsx
77
+ const [value, setValue] = useState('09:30')
78
+ const [keep, setKeep] = useState(false)
79
+
80
+ <InputTime label="Meeting" locale="en-US" value={value} clearable
81
+ onValueChange={(_, attrs) => setValue(attrs.value)}
82
+ onClearClick={event => { if (keep) event.preventDefault() }} />
83
+ <Checkbox checked={keep} onStateChange={(_, { next }) => setKeep(next === true)}>
84
+ Keep the value when clearing
85
+ </Checkbox>
86
+ <Tag>{value || 'Incomplete time'}</Tag>
87
+ ```
88
+
69
89
  ## InputTime props
70
90
 
71
91
  | Prop | Type | Default | Description |
@@ -85,9 +105,11 @@ range is clamped to the nearest bound as you step or leave the field, and flags
85
105
  | `min?` | string | — | Earliest allowed time, 24-hour `"HH:mm"`. A complete value below it is clamped up (on step / blur) and flagged `rangeUnderflow` for form validity. |
86
106
  | `name?` | string | — | Form field name — the 24-hour value submits under this key. |
87
107
  | `onBlur?` | (e) => void | — | Fires when the field loses focus. |
108
+ | `onClearClick?` | (e) => void | — | Fires before clearing. Call `preventDefault()` synchronously to keep the value. |
88
109
  | `onClearInput?` | (e) => void | — | Fires after the built-in clear button has cleared the field. |
89
110
  | `onFocus?` | (e) => void | — | Fires when the field gains focus. |
90
- | `onValueChange?` | (event, attrs) => void | — | Fires on every edit (`input`), with the native event + an `attrs` snapshot (`value`, `name`, `empty`, `valid`, `validationMessage`). Also fires on `change` (blur) and on clear. |
111
+ | `onValueChange?` | (event, attrs) => void | — | Fires on every edit (`input`), with the native event + an `attrs` snapshot (`value`, `name`, `empty`, `valid`, `validationMessage`). Also fires on `change` (blur) and on clear. Read-only blur is silent unless committing an edit made before the field became read-only. |
112
+ | `readOnly?` | boolean | — | Keep the time selectable and focusable while preventing edits. Blur emits `change` only if an edit was pending when the field became read-only. |
91
113
  | `required?` | boolean | — | Mark the field required (drives native validity). |
92
114
  | `round?` | boolean \| number \| string | — | Fully-round the field, or a custom radius (`number` px / CSS length). |
93
115
  | `size?` | 'small' \| 'medium' \| 'large' | medium | Size variant. small=24px, medium=28px, large=32px tall. |
@@ -99,9 +121,28 @@ range is clamped to the nearest bound as you step or leave the field, and flags
99
121
 
100
122
  ## Web Component
101
123
 
102
- Use the web component directly when you are not using React or Preact and a native control does not fit.
124
+ Use `<a-input-time>` directly outside JSX. Its `label`, `leading`, `clear`,
125
+ `trailing`, and `hint` slots accept field content. Use the `hour`, `minute`, and
126
+ `period` parts to style sections independently of their locale order.
127
+
128
+ If existing selectors target JSX grouping spans, update them to the
129
+ `a-input-time-label`, `a-input-time-leading`, `a-input-time-clear`,
130
+ `a-input-time-trailing`, and `a-input-time-hint` tags or their slot attributes.
131
+ Raw HTML can keep its existing slot markup.
103
132
 
104
- Slots supply the field label and leading icon.
133
+ For styles and renderer setup, follow [Install and configure](../install-config.md).
134
+ Custom colors and dimensions may require external CSS under a strict
135
+ [Content Security Policy](../install-config.md#content-security-policy).
136
+
137
+ The canonical value remains `HH:mm`, or an empty string for an incomplete time.
138
+ `input` bubbles and is composed; `change` bubbles on commit. A clear action first
139
+ fires cancelable, bubbling `clearclick`, then `input`, `change`, and `clearinput`
140
+ when accepted. Cancel `clearclick` synchronously to retain the value.
141
+
142
+ A read-only field emits `change` on blur only for an edit made before it became
143
+ read-only. Partial digits remain visible, with an empty canonical value until
144
+ editing resumes and completes the time. Removing the field while blur settles
145
+ still completes a pending commit, including range clamping on an editable field.
105
146
 
106
147
  ```html
107
148
  <a-input-time name="start-time" defaultvalue="09:30">
@@ -125,9 +166,9 @@ field treatments.
125
166
 
126
167
  ## Styling
127
168
 
128
- `InputTime` renders an `<a-input-time>` whose field chrome mirrors `<a-input>`.
129
- Route feedback color through `status`; everything else is reachable as plain CSS on
130
- the host or through `::part()`. `segment` targets every editable input. `hour`,
169
+ Use `status` for feedback color and `tone` for a custom accent. Style the host
170
+ with ordinary CSS and its sections with `::part()`. Unlayered consumer rules
171
+ override Anta's defaults. `segment` targets every editable input. `hour`,
131
172
  `minute`, and `period` target one segment type regardless of locale order. `literal`
132
173
  targets the localized separator. The element also exposes `field`, `segments`,
133
174
  `leading`, `label`, `hint`, `clear`, and `trailing`. Don't override the internal
@@ -142,6 +183,8 @@ targets the localized separator. The element also exposes `field`, `segments`,
142
183
  .my-time::part(literal) { color: var(--text-4); }
143
184
  ```
144
185
 
186
+ The example classes are consumer selectors; replace them with your own.
187
+
145
188
  Drop the clock icon with the `icon={false}` prop, and point the `segments` part at
146
189
  a monospaced family for fixed-width digits — a small `padding-top` re-centers the
147
190
  tighter `1em` line: