@antadesign/anta 0.3.19 → 0.3.20

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 (75) hide show
  1. package/README.md +1 -1
  2. package/dist/anta_helpers.d.ts +2 -2
  3. package/dist/components/Banner.d.ts +3 -3
  4. package/dist/components/Button.d.ts +1 -1
  5. package/dist/components/Calendar.d.ts +11 -12
  6. package/dist/components/Card.d.ts +1 -1
  7. package/dist/components/Card.js +1 -1
  8. package/dist/components/Checkbox.d.ts +8 -9
  9. package/dist/components/Dialog.d.ts +1 -1
  10. package/dist/components/Expander.d.ts +5 -6
  11. package/dist/components/Input.d.ts +2 -2
  12. package/dist/components/Input.js +1 -1
  13. package/dist/components/InputAutocomplete.d.ts +8 -8
  14. package/dist/components/InputTime.d.ts +3 -3
  15. package/dist/components/InputTime.js +1 -1
  16. package/dist/components/Menu.d.ts +2 -2
  17. package/dist/components/MenuItem.d.ts +1 -1
  18. package/dist/components/MenuItem.js +1 -1
  19. package/dist/components/Progress.d.ts +1 -1
  20. package/dist/components/RadioGroup.d.ts +12 -13
  21. package/dist/components/Select.d.ts +31 -38
  22. package/dist/components/Select.js +1 -1
  23. package/dist/components/SelectFaceted.d.ts +27 -33
  24. package/dist/components/Slider.d.ts +5 -5
  25. package/dist/components/Switch.d.ts +4 -4
  26. package/dist/components/Tabs.d.ts +7 -7
  27. package/dist/components/Title.d.ts +1 -1
  28. package/dist/components/Toaster.d.ts +1 -1
  29. package/dist/components/Tooltip.d.ts +1 -1
  30. package/dist/elements/a-card.css +1 -1
  31. package/dist/elements/a-dialog.css +1 -1
  32. package/dist/elements/a-expander.css +1 -1
  33. package/dist/elements/a-expander.d.ts +1 -0
  34. package/dist/elements/a-expander.js +12 -2
  35. package/dist/elements/a-progress.css +1 -1
  36. package/dist/elements/a-tooltip.css +1 -1
  37. package/dist/elements/a-tooltip.d.ts +1 -1
  38. package/dist/elements/a-tooltip.js +1 -1
  39. package/dist/elements/index.d.ts +1 -1
  40. package/dist/general_types.d.ts +22 -22
  41. package/dist/index.d.ts +3 -3
  42. package/docs/accessibility.md +4 -4
  43. package/docs/changelog.md +23 -13
  44. package/docs/comparison.md +1 -1
  45. package/docs/components/banner.md +17 -17
  46. package/docs/components/button.md +6 -6
  47. package/docs/components/card.md +5 -6
  48. package/docs/components/checkbox.md +17 -18
  49. package/docs/components/dialog.md +2 -2
  50. package/docs/components/expander.md +27 -30
  51. package/docs/components/icon.md +1 -1
  52. package/docs/components/input-autocomplete.md +15 -16
  53. package/docs/components/input-date.md +54 -59
  54. package/docs/components/input-time.md +7 -7
  55. package/docs/components/input.md +14 -14
  56. package/docs/components/menu.md +72 -8
  57. package/docs/components/progress.md +2 -2
  58. package/docs/components/radio.md +24 -25
  59. package/docs/components/select-faceted.md +95 -100
  60. package/docs/components/select.md +110 -115
  61. package/docs/components/slider.md +15 -10
  62. package/docs/components/switch.md +13 -13
  63. package/docs/components/tabs.md +23 -23
  64. package/docs/components/tag.md +5 -5
  65. package/docs/components/text.md +4 -4
  66. package/docs/components/title.md +1 -1
  67. package/docs/components/toaster.md +4 -4
  68. package/docs/components/tooltip.md +2 -2
  69. package/docs/credits.md +0 -4
  70. package/docs/index.md +1 -1
  71. package/docs/install-config.md +7 -7
  72. package/docs/normalization.md +6 -9
  73. package/docs/overview.md +1 -1
  74. package/docs/packages/stickers.md +1 -1
  75. package/package.json +1 -1
@@ -210,7 +210,7 @@ returns to whatever opened the dialog, from the native `<dialog>`.
210
210
  | `children?` | ReactNode | — | The dialog body. Scrolls when it overflows the available height. |
211
211
  | `closable?` | boolean | true | Whether the top-right ✕ button is present. It's one way to close the dialog,
212
212
  alongside Esc, the backdrop, a `data-dialog-close` / footer action, and your
213
- own code; `false` just removes the ✕, it doesn't make the dialog un-closable. |
213
+ own code; `false` removes the ✕ without making the dialog unclosable. |
214
214
  | `defaultOpen?` | boolean | — | Initial open state for the uncontrolled case (read once on mount). |
215
215
  | `footer?` | ReactNode | — | Footer content, usually the action buttons. Rendered in the bottom zone as
216
216
  a right-aligned row (wraps under pressure). Omit for none. |
@@ -245,7 +245,7 @@ returns to whatever opened the dialog, from the native `<dialog>`.
245
245
  corners at `--dialog-radius`. Omit for the position default: `center` is
246
246
  rounded, drawers and `fullscreen` are square. |
247
247
 
248
- Use this when you are not using the React or Preact wrapper and a native HTML control does not fit: construct the equivalent Anta web component from the elements below.
248
+ Use the web component directly when you are not using React or Preact and a native control does not fit.
249
249
 
250
250
  Slots hold the header, footer, and close control. Add `tabindex="0"` to every
251
251
  raw `<a-button>`.
@@ -94,7 +94,7 @@ it's a no-op there. Use it where the expander sits in a content column
94
94
  with room to its left (a prose measure); in a full-width / edge-to-edge
95
95
  container the chevron would overhang the edge.
96
96
 
97
- ## Title & level
97
+ ## Title and level
98
98
 
99
99
  `title` takes a string or any node. For a string, `level` (1–6) applies
100
100
  the matching heading **type scale** — the same scale as `<Title>` —
@@ -114,14 +114,14 @@ horizontally.
114
114
 
115
115
  When you need real heading **semantics** — so the title lands in a
116
116
  page's table of contents (and assistive tech announces it as a heading) —
117
- pass a `<Title>` (or your own `<h2>`–`<h6>`) as the `title` instead of a
118
- string. The node owns its own type scale, so set the level on it, not on
119
- `<Expander>`; a slotted heading keeps its block-rhythm margins, so zero
120
- them in the header:
117
+ pass a `<Title>` (or an application `<h2>`–`<h6>`) as the `title` instead of a
118
+ string. The heading's `level` sets its type scale, so set it on the heading,
119
+ not on `<Expander>`. A slotted heading keeps its block margins, so reset them
120
+ in the header:
121
121
 
122
122
  ```tsx
123
- {/* A node title owns its own scale + heading semantics: set the level on
124
- the <Title>, not on <Expander>. (The preview uses element composition
123
+ {/* A Title supplies its type scale and heading semantics. Set the level on
124
+ the Title, not on Expander. (The preview uses element composition
125
125
  because these docs render statically; the title-prop form is identical.) */}
126
126
  <Expander title={<Title level={2}>Title level 2 example</Title>}>…</Expander>
127
127
  <Expander title={<Title level={4}>Title level 4 example</Title>}>…</Expander>
@@ -214,12 +214,11 @@ state stays as-is — disabling an open expander keeps it open. Header
214
214
 
215
215
  ## Open state
216
216
 
217
- Leave it uncontrolled and pass `defaultOpen` for the initial state — the
218
- element owns open/close from there. To drive it yourself, pass `open`
219
- and handle `onStateChange` (controlled): the expander then only follows
220
- the prop, and clicking the summary just *requests* a change — apply
221
- `detail.next` to your state to accept, or ignore it to reject. If you pass
222
- `open`, you own it.
217
+ For uncontrolled use, pass `defaultOpen` for the initial state. The element
218
+ then updates its open state after interaction. For controlled use, pass `open`
219
+ and handle `onStateChange`. The expander follows the `open` prop and reports a
220
+ requested change. Update application state with `detail.next` to accept it, or
221
+ leave the state unchanged to reject it.
223
222
 
224
223
  `onStateChange` is event-first: `(event, { next, prev })`, where `next`/`prev`
225
224
  are booleans (the requested / previous open state). `statechange` fires
@@ -243,16 +242,15 @@ const [open, setOpen] = useState(false)
243
242
 
244
243
  ## Controlled vs Uncontrolled
245
244
 
246
- Open state follows Anta's shared **state contract**. Leave it *uncontrolled* and
247
- the expander owns open/close; pass the controlled prop and *you* own it. The two
248
- layers speak different idioms — the `<Expander>` wrapper takes booleans, the
249
- `<a-expander>` element takes a `state` string enum — and the wrapper maps between
250
- them:
245
+ Open state follows Anta's shared **state contract**. In uncontrolled use, the
246
+ element updates open state. In controlled use, the application supplies it. The
247
+ `<Expander>` wrapper uses booleans; the `<a-expander>` element uses a `state`
248
+ string. The wrapper maps between them:
251
249
 
252
250
  | | `<Expander>` (JSX) | `<a-expander>` (element) |
253
251
  |---|---|---|
254
- | **Uncontrolled** — element owns it | `defaultOpen` | `default-state="open" \| "closed"` |
255
- | **Controlled** — you own it | `open` + `onStateChange` | `state="open" \| "closed"` + `statechange` |
252
+ | **Uncontrolled** | `defaultOpen` | `default-state="open" \| "closed"` |
253
+ | **Controlled** | `open` + `onStateChange` | `state="open" \| "closed"` + `statechange` |
256
254
  | **Change event** | `onStateChange(event, { next, prev })` — `next` / `prev` are **booleans** | `statechange`, a `CustomEvent<{ next, prev }>` — `'open'` / `'closed'` |
257
255
 
258
256
  `statechange` is **cancelable** and fires *before* the element applies the
@@ -330,10 +328,9 @@ function Accordion() {
330
328
  toggle (e.g. confirm before closing). `detail.next` is the requested
331
329
  open state, `detail.prev` the current one (booleans). In controlled
332
330
  mode, apply `detail.next` to `open` to accept, or do nothing to reject. |
333
- | `open?` | boolean | — | Controlled open state. When provided, the consumer owns open/close:
334
- the expander only follows this prop, and clicking the summary just
335
- requests a change via `onStateChange` (so a toggle can be rejected by
336
- not updating). Leave undefined for uncontrolled. |
331
+ | `open?` | boolean | — | Controlled open state. When provided, the consumer owns open/close. Selecting
332
+ the summary requests a change through `onStateChange`; omit `open` for
333
+ uncontrolled use. |
337
334
  | `outdent?` | boolean | — | Outdent the chevron into the left gutter so the title and body sit
338
335
  flush with surrounding content (the docs-header layout). Only takes
339
336
  effect with `priority="tertiary"` — on the filled priorities the
@@ -352,7 +349,7 @@ function Accordion() {
352
349
  its hue while lightness/chroma are pinned. `'neutral'` (the default) is
353
350
  the same as omitting it. |
354
351
 
355
- Use this when you are not using the React or Preact wrapper and a native HTML control does not fit: construct the equivalent Anta web component from the elements below.
352
+ Use the web component directly when you are not using React or Preact and a native control does not fit.
356
353
 
357
354
  Slots hold the title and actions. The details element contains the collapsible body.
358
355
 
@@ -369,7 +366,7 @@ Slots hold the title and actions. The details element contains the collapsible b
369
366
  ### Native HTML details
370
367
 
371
368
  For a simple disclosure, add `data-anta` to native `<details>`. It keeps the
372
- browser's opening, keyboard, and accessibility behaviour while taking on the
369
+ browser's opening, keyboard, and accessibility behavior while taking on the
373
370
  neutral tertiary Expander treatment. Add `priority="secondary"` or `"primary"`
374
371
  for a filled surface. `round`, `tone`, tertiary `outdent`, and `level="1"`–`"6"`
375
372
  use the matching Expander visual treatments. `level` changes visual type only;
@@ -382,9 +379,9 @@ use a heading inside the summary when you need heading semantics.
382
379
  </details>
383
380
  ```
384
381
 
385
- Reach for the props first: **`tone`** sets the colour (any CSS colour for a custom
382
+ Reach for the props first: **`tone`** sets the color (any CSS color for a custom
386
383
  tone — it derives the surface in oklch), **`priority`** the fill, **`level`** the
387
- heading. Two knobs are worth keeping: **`--expander-tone-source`** (the colour a
384
+ heading. Two knobs are worth keeping: **`--expander-tone-source`** (the color a
388
385
  custom `tone` derives from — set it to drive the palette from your own variable) and
389
386
  **`--expander-gutter`** (the shared left inset that re-insets header + body + chevron
390
387
  in lockstep; `outdent` sets it to `0`).
@@ -435,12 +432,12 @@ a-expander.hover-tint::part(summary):active { background: var(--bg-2); }
435
432
  ```
436
433
 
437
434
  **Highlight the title on hover.** The title inherits its `color` from `::part(summary)`,
438
- so recolour the part on hover and the title (and the `currentColor` chevron) follow — a
435
+ so recolor the part on hover and the title (and the `currentColor` chevron) follow — a
439
436
  custom title node that sets its own `color` opts out. Here we also drop the tertiary
440
437
  hover underline on the light-DOM `a-expander-summary`:
441
438
 
442
439
  ```css
443
- a-expander.title-highlight::part(summary):hover { color: #e5484d; } /* title colour on hover */
440
+ a-expander.title-highlight::part(summary):hover { color: #e5484d; } /* title color on hover */
444
441
  a-expander.title-highlight a-expander-summary { text-decoration: none; } /* drop the hover underline */
445
442
  ```
446
443
 
@@ -31,7 +31,7 @@ accessible loading status.
31
31
  noise alongside neighbouring text. |
32
32
  | `size?` | number | 16 | Width and height in pixels. |
33
33
 
34
- Use this when you are not using the React or Preact wrapper and a native HTML control does not fit: construct the equivalent Anta web component from the elements below.
34
+ Use the web component directly when you are not using React or Preact and a native control does not fit.
35
35
 
36
36
  Set the accessible name on the raw element. Use `--icon-size` for a portable custom
37
37
  size.
@@ -1,14 +1,14 @@
1
1
  # InputAutocomplete
2
2
 
3
- `InputAutocomplete` — a text field whose value is a free string, with a suggestion list that assists
4
- without constraining. Type anything: a value that isn't in the list stays as you
5
- typed it, and picking a suggestion fills the field. For a value that must be one of
6
- the options, use [Select](./select.md) with `filter`.
3
+ `InputAutocomplete` is a text field with a suggestion list. Its value remains a
4
+ free-form string: text that is not in the list stays in the field, and selecting
5
+ a suggestion fills the field. Use [Select](./select.md) with `filter` when the
6
+ value must be one of the options.
7
7
 
8
- It's a **composed component**, an [Input](./input.md) stitched to a [Menu](./menu.md) of
9
- suggestions. The field text arrives through **`onValueChange`**; `onSelect` fires
10
- only when a suggestion is chosen. Controlled via `value`, uncontrolled via
11
- `defaultValue`.
8
+ InputAutocomplete uses an [Input](./input.md) for the field and a [Menu](./menu.md)
9
+ for suggestions. **`onValueChange`** reports field text. `onSelect` runs only
10
+ after someone chooses a suggestion. Use `value` to control it, or `defaultValue`
11
+ for uncontrolled use.
12
12
 
13
13
  ## Free text is the point
14
14
 
@@ -29,7 +29,7 @@ closes the list; clicking the field to place the caret keeps it open.
29
29
 
30
30
  - **`true`** (default): built-in case-insensitive substring match on each option's
31
31
  `value` / `label` / `hint`.
32
- - **a function** `(option, query) => boolean`: your own matcher, called per option.
32
+ - **a function** `(option, query) => boolean`: a custom matcher, called for each option.
33
33
  - **`false`**: show `suggestions` as-is, doing no local filtering. Use this when you
34
34
  fetch or filter results yourself (async / remote) and feed the current set through
35
35
  `suggestions`.
@@ -44,12 +44,12 @@ Suggestions are bare strings or `SelectOption`s, the same option shape
44
44
  | `suggestions` | string \| SelectOption[] | — | The suggestions: bare strings or `SelectOption`s (`value` / `label` / `hint` /
45
45
  `icon` / `tone` / `disabled`). A flat list; picking one fills the field. |
46
46
  | `clearable?` | boolean | — | Show a clear button while the field has text. |
47
- | `defaultValue?` | string | — | Initial value for the uncontrolled case (the wrapper then owns it). |
47
+ | `defaultValue?` | string | — | Initial value for uncontrolled use. |
48
48
  | `disabled?` | boolean | — | Disable the field. |
49
49
  | `filter?` | boolean \| (option, query) => boolean | true | How suggestions match the text. `true` uses the built-in case-insensitive
50
50
  substring match on `value` / `label` / `hint`; a function `(option, query) =>
51
- boolean` does custom matching; `false` shows `suggestions` verbatim so you
52
- can filter them yourself (async / remote). |
51
+ boolean` uses a custom matcher; `false` shows `suggestions` unchanged for
52
+ application-provided async or remote filtering. |
53
53
  | `hint?` | ReactNode | — | Hint under the field. |
54
54
  | `label?` | ReactNode | — | Field label. |
55
55
  | `leading?` | ReactNode | — | Leading adornment inside the field. |
@@ -61,7 +61,7 @@ Suggestions are bare strings or `SelectOption`s, the same option shape
61
61
  | `size?` | 'small' \| 'medium' \| 'large' | medium | Field size. |
62
62
  | `status?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' | — | Validation status. |
63
63
  | `statusIcon?` | (string & {}) \| false \| IconShape | — | Status icon override, or `false` to drop it. |
64
- | `tone?` | string | — | Custom accent colour — a named tone or any CSS colour. |
64
+ | `tone?` | string | — | Custom accent color — a named tone or any CSS color. |
65
65
  | `trailing?` | ReactNode | — | Trailing adornment inside the field. |
66
66
  | `value?` | string | — | Controlled value: the field's text, a **free** string not constrained to a
67
67
  suggestion. Leave undefined for uncontrolled. |
@@ -76,9 +76,8 @@ For browser-native suggestions, pair a native input with a `<datalist>`. It
76
76
  keeps free-form entry and lets the browser filter and fill matching options,
77
77
  while `data-anta` supplies the field appearance.
78
78
 
79
- The suggestions popup is owned by the browser, so use `InputAutocomplete` when
80
- you need custom option content, popup styling, or consistent interaction across
81
- browsers.
79
+ The browser renders the suggestions popup. Use `InputAutocomplete` for custom
80
+ option content, popup styling, or consistent behavior across browsers.
82
81
 
83
82
  ```html
84
83
  <input data-anta type="text" list="people" placeholder="Search people">
@@ -94,17 +94,19 @@ field converts it (18 → 6 PM).
94
94
  The sections below document `Calendar`, the month grid `InputDate` opens. Reach for
95
95
  it directly when you want the grid inline, without a field.
96
96
 
97
- `Calendar` is **composed from Anta components in light DOM**, with nothing hidden in
98
- a shadow root. The days and prev/next chevrons are `<Button>`s (a selected day is a
99
- `tertiary` Button in its selected state, toned `brand`), inside a form-associated `<a-calendar>`
100
- grid. Date math runs on the [Temporal API](https://tc39.es/proposal-temporal/docs/),
101
- bundled via `temporal-polyfill`. For a raw grid, import `buildMonth` from
102
- `@antadesign/anta`: it returns localized weekday headers and every day record you
103
- need to render — ISO value, accessible label, and selected, disabled, today, and
104
- outside-month state. It does not coordinate navigation, roving focus, selection, or
105
- re-renders for you. The value is an ISO `YYYY-MM-DD` string, never a `Date`. It works
106
- controlled (`value` + `onStateChange`) or uncontrolled (`defaultValue`), and
107
- participates in forms under `name`.
97
+ `Calendar` uses Anta components in light DOM; it has no hidden shadow-root UI.
98
+ The day cells and previous/next chevrons are `<Button>`s inside a
99
+ form-associated `<a-calendar>` grid. Date calculations use the
100
+ [Temporal API](https://tc39.es/proposal-temporal/docs/) through
101
+ `temporal-polyfill`.
102
+
103
+ For a raw grid, import `buildMonth` from `@antadesign/anta`. It returns
104
+ localized weekday headers and day records with ISO values, accessible labels,
105
+ and selected, disabled, today, and outside-month state. It does not manage
106
+ navigation, roving focus, selection, or rendering. The value is an ISO
107
+ `YYYY-MM-DD` string, never a `Date`. Use `value` with `onStateChange` to
108
+ control it, or `defaultValue` for uncontrolled use. It participates in forms
109
+ under `name`.
108
110
 
109
111
  **Not SSR-safe.** `Calendar` (and `InputDate`, which embeds it) reads the current
110
112
  date (`Temporal.Now`) and locale (`navigator.language`) at render, so a server
@@ -241,60 +243,53 @@ stop, and an `aria-live` region announces the month when it changes.
241
243
 
242
244
  ## Customization
243
245
 
244
- The family stacks in layers: `InputDate` composes `Input` + `Menu` + `Calendar`,
245
- and `Calendar` itself splits into the `<a-calendar>` element (the day grid) and a
246
- JSX wrapper (the navigation chrome). Knowing which layer owns what tells you where to
247
- reach when you extend it, and lets a non-React consumer rebuild the chrome from stock
248
- elements.
249
-
250
- ### Components composition
251
-
252
- `InputDate` has no `a-inputdate` element; the wrapper is the coordinator, the role
253
- `Select` plays over `Input` + `Menu`. It renders an editable `<Input>` trigger and a
254
- `<Menu>` holding a `<Calendar>` (with `time`, the hour/minute row and AM/PM `Tabs`
255
- sit under the grid), and owns the draft text, the lenient parse on commit, and the
256
- ISO value. The menu anchors to the field and opens on click (via the `Menu`'s own
257
- anchor-click trigger, like `Select`) — a mouse open leaves focus in the field so you
258
- can keep typing, while ↓ opens it and moves focus into the grid. That
259
- focus move is declarative: `InputDate` bumps `Calendar`'s `focusSignal`, which
260
- renders a `data-focus` attribute the `<a-calendar>` element reads to focus the active
261
- day — the wrapper never touches the DOM. Nesting a stateful component in a menu has
262
- one wrinkle: the `Calendar` (and `Tabs`) dispatch their own
263
- `statechange`, so the `Menu` acts only on its own `open` / `closed`, and a bubbled
264
- pick isn't mistaken for a dismiss.
265
-
266
- The `<a-calendar>` element owns the grid and nothing else. It wires the day
267
- `<button>`s you render into it (click and keyboard: arrows, Home/End,
268
- PageUp/PageDown), holds the submitted value via `ElementInternals`,
269
- and dispatches the events: `statechange` / `change` on a pick, and `navigate` when a
270
- key pages past the rendered month. It's framework-agnostic and works on its own, with
271
- no notion of a "current month" and no sight of `min` / `max`.
272
-
273
- Everything above the grid is the JSX wrapper, rendered as **siblings outside**
274
- `<a-calendar>` from ordinary components: the prev/next arrows are `<Button>`s, and the
275
- month/year picker is a `<Button>` opening a `<Menu>` of year `MenuItem`s, each a
276
- submenu of months. The wrapper holds the displayed-month cursor in state, derives the
277
- day cells and the arrows' `disabled` state from `min` / `max`, and turns a click on an
278
- arrow, a pick in the menu, or a `navigate` event into a new cursor, then re-renders
279
- the grid. So range enforcement (disabled days, capped arrows, a disabled picker for a
280
- single-month range) lives entirely in the wrapper; the element only refuses to
281
- activate a day you've rendered `disabled`.
282
-
283
- That seam is why the element has no `min` / `max` and the switcher is a sibling: the
284
- grid stays a small, portable authority, and the chrome (Anta `Button`s and a `Menu`)
285
- is composition you can replace or rebuild. **Without React**, render the grid from the
286
- exported engine (`buildMonth`) and drive it with the same glue the wrapper packages:
246
+ `InputDate` uses Input, Menu, and Calendar. Calendar uses an `<a-calendar>`
247
+ element for the day grid and a JSX wrapper for navigation. The sections below
248
+ describe which layer provides each behavior and how to recreate the interface
249
+ without React.
250
+
251
+ ### Build it from elements
252
+
253
+ `InputDate` has no `a-inputdate` element. Its wrapper renders an editable
254
+ `<Input>` and a `<Menu>` containing a `<Calendar>`. When `time` is set, the
255
+ hour/minute fields and AM/PM `Tabs` appear below the grid. The wrapper manages
256
+ draft text, parsing on commit, and the ISO value.
257
+
258
+ Clicking the field opens the menu while keeping focus in the field. Pressing
259
+ ↓ opens the menu and moves focus to the grid. The wrapper passes a
260
+ `focusSignal` to Calendar, which renders `data-focus`; `<a-calendar>` then
261
+ focuses the active day. Calendar and Tabs emit their own `statechange` events,
262
+ but Menu responds only to changes in its own open state.
263
+
264
+ The `<a-calendar>` element provides grid behavior. It connects the supplied day
265
+ `<button>`s to click and keyboard navigation, including arrows, Home,
266
+ End, PageUp, and PageDown. It stores the form
267
+ value through `ElementInternals` and emits `statechange` / `change` on selection
268
+ and `navigate` when keyboard navigation leaves the displayed month. It is
269
+ framework-agnostic and does not track a current month, `min`, or `max`.
270
+
271
+ The JSX wrapper renders the controls above the grid as siblings of
272
+ `<a-calendar>`. The previous/next arrows are `<Button>`s. The month/year picker
273
+ is a `<Button>` that opens a `<Menu>` of year `MenuItem`s, each with a month
274
+ submenu. The wrapper stores the displayed-month cursor in state and derives day
275
+ cells and the arrows' `disabled` states from `min` and `max`. A click on an
276
+ arrow, a menu selection, or a `navigate` event updates the cursor and re-renders
277
+ the grid. The wrapper enforces the range. The element only ignores a day that
278
+ was rendered with `disabled`.
279
+
280
+ The element has no `min` or `max` because the wrapper provides the surrounding
281
+ controls. Without React, render the grid with `buildMonth` and add the
282
+ navigation behavior shown below:
287
283
 
288
284
  ```html
289
- <!-- `.calendar` is your own wrapper; the switcher is plain Buttons + a Menu. -->
285
+ <!-- `.calendar` scopes this example. Use an application selector instead. -->
290
286
  <div class="calendar" style="display: inline-flex; flex-direction: column; gap: 6px">
291
287
  <div style="display: flex; align-items: center">
292
288
  <a-button priority="tertiary" data-prev aria-label="Previous month"><a-icon shape="chevron-left"></a-icon></a-button>
293
289
  <a-button priority="tertiary" data-heading style="flex: 1; justify-content: center" aria-haspopup="menu"></a-button>
294
290
  <a-button priority="tertiary" data-next aria-label="Next month"><a-icon shape="chevron-right"></a-icon></a-button>
295
291
  </div>
296
- <!-- The element wires whatever day cells you render; it owns selection,
297
- keyboard, the form value, and the events. -->
292
+ <!-- The element provides selection, keyboard behavior, form value, and events. -->
298
293
  <a-calendar role="group" name="date"></a-calendar>
299
294
  </div>
300
295
  <script type="module">
@@ -367,7 +362,7 @@ field treatments.
367
362
  ```
368
363
 
369
364
  The calendar is **light DOM**, so everything is plain CSS, with no shadow parts or
370
- hidden internals. The days and chevrons are real `<a-button>`s, so their colours,
365
+ hidden internals. The days and chevrons are real `<a-button>`s, so their colors,
371
366
  hover, focus ring, and selected look come from the Button styles and theme
372
367
  themselves; they sit inside the `<a-calendar>` grid, and the switcher header sits in
373
368
  the `.anta-calendar` shell. An un-layered consumer rule beats `@layer anta` without
@@ -384,7 +379,7 @@ the `.anta-calendar` shell. An un-layered consumer rule beats `@layer anta` with
384
379
  Each day is a `<a-button data-part="day-cell" data-date="YYYY-MM-DD">`, so a
385
380
  specific date is addressable by its `data-date`: tint it, or hang an event marker
386
381
  off a `::after` (give the cell `position: relative` first). Below, three days carry a
387
- brand dot and one is flagged in the critical colour.
382
+ brand dot and one is flagged in the critical color.
388
383
 
389
384
  ```css
390
385
  /* Day cells are `<a-button data-part="day-cell" data-date="YYYY-MM-DD">`. Give them a
@@ -20,14 +20,14 @@ or uncontrolled (`defaultValue`), and submits under `name` via the native form A
20
20
 
21
21
  Focus a section and edit it with the keyboard: ↑/↓ to step (it
22
22
  wraps), PageUp/PageDown for a larger jump on minutes,
23
- or just type the digits. Home/End, selection, deletion, and
23
+ or enter the digits. Home/End, selection, deletion, and
24
24
  paste behave as they do in a native text input. Typing
25
25
  `9` then `3` `0` lands `09:30` and moves across the sections on its own; an
26
26
  out-of-range digit (a `6` in the minutes) commits and advances immediately.
27
27
  Backspace on an empty section selects the previous one. The whole thing
28
28
  reads as one field while preserving native caret behavior. In a
29
29
  12-hour field, typing a 24-hour hour converts it and flips AM/PM — `18` becomes
30
- `6 PM` — so pasting a 24-hour time just works.
30
+ `6 PM`, so pasted 24-hour times work.
31
31
 
32
32
  ```tsx
33
33
  <InputTime label="Start time" defaultValue="09:30" onValueChange={(_, { value }) => save(value)} />
@@ -69,7 +69,7 @@ range is clamped to the nearest bound as you step or leave the field, and flags
69
69
  | Prop | Type | Default | Description |
70
70
  |------|------|---------|-------------|
71
71
  | `children?` | ReactNode | — | Extra content rendered under the field, above the hint (a no-box child like
72
- a `<Tooltip>` takes no space and just anchors to the field). |
72
+ a `<Tooltip>` takes no space and anchors to the field). |
73
73
  | `clearable?` | boolean | — | Show a clear button once the field has a value. |
74
74
  | `defaultValue?` | string | — | Initial value for the uncontrolled case (24-hour `"HH:mm"`). |
75
75
  | `dimActions?` | boolean | — | Dim the trailing adornments at rest; they brighten on hover / focus. |
@@ -101,12 +101,12 @@ range is clamped to the nearest bound as you step or leave the field, and flags
101
101
  others are advisory. Omit (or `neutral`) for a plain field. |
102
102
  | `statusIcon?` | (string & {}) \| false \| IconShape | — | Glyph before the `hint` when `status` is set (per-status default; pass a
103
103
  shape to override, or `false` to drop it). |
104
- | `tone?` | string | — | Custom accent colour — any literal CSS colour tints the resting + hover
104
+ | `tone?` | string | — | Custom accent color — any literal CSS color tints the resting + hover
105
105
  border (focus ring stays `--focus-ring`); `status` overrides for validation. |
106
106
  | `trailing?` | ReactNode | — | Content pinned to the end of the field (after the clear button). |
107
107
  | `value?` | string | — | Controlled value — 24-hour `"HH:mm"`. Pair with `onValueChange`. |
108
108
 
109
- Use this when you are not using the React or Preact wrapper and a native HTML control does not fit: construct the equivalent Anta web component from the elements below.
109
+ Use the web component directly when you are not using React or Preact and a native control does not fit.
110
110
 
111
111
  Slots supply the field label and leading icon.
112
112
 
@@ -165,9 +165,9 @@ tighter `1em` line:
165
165
  `}</style>
166
166
  ```
167
167
 
168
- For a one-off accent, `tone` takes **any literal CSS colour** (e.g. `#7c3aed`) and
168
+ For a one-off accent, `tone` takes **any literal CSS color** (e.g. `#7c3aed`) and
169
169
  tints the resting + hover border — the focus ring stays the global ring. It's a
170
- colour, not a named tone; the named feedback tones live on `status`.
170
+ color, not a named tone; the named feedback tones live on `status`.
171
171
 
172
172
  ```tsx
173
173
  <InputTime label="Accent" tone="#7c3aed" defaultValue="09:30" />
@@ -24,7 +24,7 @@ Anta's text scale, so a field lines up with same-size `Text` / `Button`.
24
24
  | `medium` | 28px | 15 / 20 | 16px |
25
25
  | `large` | 32px | 17 / 22 | 18px |
26
26
 
27
- ## Label & hint
27
+ ## Label and hint
28
28
 
29
29
  ```tsx
30
30
  <Input label="Display name" defaultValue="Ada Lovelace" />
@@ -75,8 +75,8 @@ recolors it.
75
75
 
76
76
  It reuses the tone palette but isn't `tone`: **`status` is a validation *state*** (it
77
77
  also carries the glyph and, for `critical`, validity), whereas `tone` — elsewhere in
78
- Anta — is decorative colour. On `Input` the named tones live on `status`; `tone` is
79
- reserved for a one-off custom border colour.
78
+ Anta — is decorative color. On `Input` the named tones live on `status`; `tone` is
79
+ reserved for a one-off custom border color.
80
80
 
81
81
  Only `status="critical"` carries validity — it sets `aria-invalid`, flips the
82
82
  `:state(invalid)` hook, and reports invalid to a surrounding `<form>` through
@@ -87,7 +87,7 @@ Each status has a default glyph (`critical` → `warning-diamond`, `warning` →
87
87
  `circle-small-solid`). Override it with **`statusIcon`** (any shape), or drop it
88
88
  with `statusIcon={false}`.
89
89
 
90
- ## Leading, trailing & clear
90
+ ## Leading, trailing, and clear
91
91
 
92
92
  ```tsx
93
93
  {/* clearable — the clear button shows once there's a value */}
@@ -191,13 +191,13 @@ Autogrow uses CSS `field-sizing` where supported (Chromium, Safari ≥ 26.2) and
191
191
  built-in JS resize everywhere else (Firefox, older Safari), so it grows in every
192
192
  browser.
193
193
 
194
- ## Controlled & uncontrolled
194
+ ## Controlled and uncontrolled
195
195
 
196
196
  ```tsx
197
- // Uncontrolled — the element owns the value
197
+ // Uncontrolled: the element updates its value after input.
198
198
  <Input label="Name" defaultValue="Ada" onChange={(e) => log(e.target.value)} />
199
199
 
200
- // Controlled — you own the value
200
+ // Controlled: application state supplies the value.
201
201
  const [v, setV] = useState('')
202
202
  <Input label="Name" value={v} onInput={(e) => setV(e.target.value)} />
203
203
  ```
@@ -251,7 +251,7 @@ Because `<a-input>` is form-associated, it submits with the surrounding
251
251
  needed. `required` participates in native validity, and `<Button type="reset">`
252
252
  restores the field to its `defaultValue` via `formResetCallback`.
253
253
 
254
- ## Types & validation
254
+ ## Types and validation
255
255
 
256
256
  ```tsx
257
257
  <Input type="text" label="Text" />
@@ -263,7 +263,7 @@ restores the field to its `defaultValue` via `formResetCallback`.
263
263
  ```
264
264
 
265
265
  `type` accepts `text` (default), `email`, `password`, `tel`, `url`, and
266
- `number` — each gets the right mobile keyboard, autofill behaviour, and native
266
+ `number` — each gets the right mobile keyboard, autofill behavior, and native
267
267
  constraint validation. (`search` is omitted on purpose — see above.)
268
268
  `number` also takes `min` / `max` / `step`.
269
269
 
@@ -433,7 +433,7 @@ wraps to fewer columns as it narrows. Resize the preview to see it reflow.
433
433
  `username`, `current-password`, `new-password`, `one-time-code`, or `off`. |
434
434
  | `children?` | ReactNode | — | Extra content rendered directly under the field, above the hint/error (it
435
435
  pushes the message down). A no-box child like an Anta `<Tooltip>` takes no
436
- space and just anchors to the field — consistent with how tooltips attach
436
+ space and anchors to the field — consistent with how tooltips attach
437
437
  to any other element. Use the named `leading` / `trailing` props for
438
438
  in-field content. |
439
439
  | `clearable?` | boolean | — | Show a clear button as the first trailing item once the field has a
@@ -508,7 +508,7 @@ wraps to fewer columns as it narrows. Resize the preview to see it reflow.
508
508
  success → `circle-check`, info → `info`, brand → `circle-small-solid`); pass a
509
509
  shape to override, or `false` to drop it. `neutral` has no default glyph. |
510
510
  | `step?` | number \| string | — | |
511
- | `tone?` | string | — | Custom accent colour — any literal CSS colour tints the resting + hover
511
+ | `tone?` | string | — | Custom accent color — any literal CSS color tints the resting + hover
512
512
  border (focus ring stays the global `--focus-ring`). For consistency with the
513
513
  other controls' custom-tone knob; a `status` still overrides for validation. |
514
514
  | `trailing?` | ReactNode | — | Content pinned to the end of the field (e.g. icons, buttons), after the
@@ -523,7 +523,7 @@ this; use `truncate` when an editable field should keep the same treatment. |
523
523
  clear/search affordances never appear — Anta owns that chrome. |
524
524
  | `value?` | string | — | Controlled value. Pair with `onChange` / `onInput`. |
525
525
 
526
- Use this when you are not using the React or Preact wrapper and a native HTML control does not fit: construct the equivalent Anta web component from the elements below.
526
+ Use the web component directly when you are not using React or Preact and a native control does not fit.
527
527
 
528
528
  Slots hold the label, hint, and controls around the native field.
529
529
 
@@ -563,7 +563,7 @@ their `size` attribute, so Anta does not reuse it. `round` and a custom-color
563
563
  ```
564
564
 
565
565
  Reach for the props first: **`status`** sets a validation tone (border + message),
566
- **`tone`** a custom accent colour for the border (any CSS colour), **`size`** the
566
+ **`tone`** a custom accent color for the border (any CSS color), **`size`** the
567
567
  dimensions. The focus ring is the global [`--focus-ring`](../colors.md#focus-ring).
568
568
 
569
569
  ```tsx
@@ -587,7 +587,7 @@ The border is drawn as an **inset `box-shadow`**, not a real `border`, so its wi
587
587
  (`0.5px` → `1px` when a `status` is set) and placement never change the box size —
588
588
  the field height stays locked to the matching Button. Drop the leading `inset` to
589
589
  draw the border as an outset ring instead. You can also re-point an `--input-*`
590
- colour token on one instance (`style={{ '--input-border': 'var(--border-1)' }}`)
590
+ color token on one instance (`style={{ '--input-border': 'var(--border-1)' }}`)
591
591
  or a wrapping selector; the resolver lives in `@layer anta`, so any un-layered rule
592
592
  of yours wins.
593
593