@antadesign/anta 0.3.26 → 0.3.27

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 (69) hide show
  1. package/README.md +26 -28
  2. package/dist/anta_helpers.d.ts +4 -0
  3. package/dist/anta_helpers.js +14 -0
  4. package/dist/box-types.d.ts +1 -1
  5. package/dist/bundle.css +1 -1
  6. package/dist/bundle.js +25 -25
  7. package/dist/capture-types.d.ts +156 -0
  8. package/dist/capture-types.js +0 -0
  9. package/dist/components/Box.d.ts +15 -0
  10. package/dist/components/Box.js +3 -11
  11. package/dist/components/Capture.d.ts +39 -0
  12. package/dist/components/Capture.js +36 -0
  13. package/dist/components/capture-props.d.ts +30 -0
  14. package/dist/components/capture-props.js +44 -0
  15. package/dist/elements/a-box.d.ts +1 -1
  16. package/dist/elements/a-box.js +11 -3
  17. package/dist/elements/a-capture.css +1 -0
  18. package/dist/elements/a-capture.d.ts +10 -0
  19. package/dist/elements/a-capture.js +24 -0
  20. package/dist/elements/a-checkbox.js +4 -4
  21. package/dist/elements/a-slider.js +6 -6
  22. package/dist/elements/capture-input.d.ts +5 -0
  23. package/dist/elements/capture-input.js +609 -0
  24. package/dist/elements/index.d.ts +1 -0
  25. package/dist/elements/index.js +3 -0
  26. package/dist/general_types.d.ts +34 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.js +2 -0
  29. package/dist/jsx-runtime.d.ts +2 -1
  30. package/docs/accessibility.md +24 -0
  31. package/docs/changelog.md +22 -0
  32. package/docs/colors.md +141 -0
  33. package/docs/comparison.md +463 -28
  34. package/docs/components/avatar.md +13 -17
  35. package/docs/components/banner.md +13 -27
  36. package/docs/components/box.md +226 -154
  37. package/docs/components/breadcrumbs.md +7 -6
  38. package/docs/components/button.md +48 -37
  39. package/docs/components/capture.md +345 -0
  40. package/docs/components/card.md +17 -38
  41. package/docs/components/checkbox.md +16 -39
  42. package/docs/components/dialog.md +14 -36
  43. package/docs/components/expander.md +15 -40
  44. package/docs/components/icon.md +111 -9
  45. package/docs/components/input-autocomplete.md +9 -11
  46. package/docs/components/input-date.md +33 -23
  47. package/docs/components/input-time.md +15 -23
  48. package/docs/components/input.md +27 -76
  49. package/docs/components/loader.md +7 -5
  50. package/docs/components/menu.md +73 -77
  51. package/docs/components/progress.md +10 -14
  52. package/docs/components/radio.md +17 -36
  53. package/docs/components/select-faceted.md +7 -25
  54. package/docs/components/select.md +24 -97
  55. package/docs/components/slider.md +16 -21
  56. package/docs/components/steps.md +38 -8
  57. package/docs/components/switch.md +11 -14
  58. package/docs/components/tabs.md +52 -40
  59. package/docs/components/tag.md +11 -22
  60. package/docs/components/text.md +10 -23
  61. package/docs/components/title.md +7 -8
  62. package/docs/components/toaster.md +6 -3
  63. package/docs/components/tooltip.md +13 -23
  64. package/docs/index.md +3 -1
  65. package/docs/install-config.md +24 -0
  66. package/docs/normalization.md +45 -0
  67. package/docs/packages/stickers.md +56 -0
  68. package/docs/theming.md +270 -0
  69. package/package.json +3 -1
@@ -424,106 +424,55 @@ wraps to fewer columns as it narrows. Resize the preview to see it reflow.
424
424
  `}</style>
425
425
  ```
426
426
 
427
- ### Props
427
+ ## Component props
428
428
 
429
429
  | Prop | Type | Default | Description |
430
430
  |------|------|---------|-------------|
431
- | `autoComplete?` | 'off' \| 'on' \| 'name' \| 'username' \| 'email' \| 'current-password' \| 'new-password' \| 'one-time-code' \| 'tel' \| 'url' \| (string & {}) | — | Native autocomplete token. Overrides the value derived from `type`
432
- (`email` / `tel` / `url`) — set it for the cases `type` can't express, e.g.
433
- `username`, `current-password`, `new-password`, `one-time-code`, or `off`. |
431
+ | `autoComplete?` | 'off' \| 'on' \| 'name' \| 'username' \| 'email' \| 'current-password' \| 'new-password' \| 'one-time-code' \| 'tel' \| 'url' \| (string & {}) | — | Native autocomplete token. Overrides the value derived from `type` (`email` / `tel` / `url`) — set it for the cases `type` can't express, e.g. `username`, `current-password`, `new-password`, `one-time-code`, or `off`. |
434
432
  | `autoFocus?` | boolean | — | Focus this field when its containing `Dialog` opens. |
435
- | `children?` | ReactNode | — | Extra content rendered directly under the field, above the hint/error (it
436
- pushes the message down). A no-box child like an Anta `<Tooltip>` takes no
437
- space and anchors to the field — consistent with how tooltips attach
438
- to any other element. Use the named `leading` / `trailing` props for
439
- in-field content. |
440
- | `clearable?` | boolean | — | Show a clear button as the first trailing item once the field has a
441
- value. |
433
+ | `children?` | ReactNode | — | Extra content rendered directly under the field, above the hint/error (it pushes the message down). A no-box child like an Anta `<Tooltip>` takes no space and anchors to the field — consistent with how tooltips attach to any other element. Use the named `leading` / `trailing` props for in-field content. |
434
+ | `clearable?` | boolean | — | Show a clear button as the first trailing item once the field has a value. |
442
435
  | `defaultValue?` | string | — | Initial value for the uncontrolled case. |
443
- | `dimActions?` | boolean | — | Dim the `leading` / `trailing` adornments at rest; they brighten to full
444
- when the field is hovered or focused (a quiet-until-engaged affordance for
445
- trailing actions). |
436
+ | `dimActions?` | boolean | — | Dim the `leading` / `trailing` adornments at rest; they brighten to full when the field is hovered or focused (a quiet-until-engaged affordance for trailing actions). |
446
437
  | `disabled?` | boolean | — | Disable the field. |
447
- | `hint?` | ReactNode | — | Message below the field. Neutral helper text by default; `status` recolors
448
- it and prefixes the matching glyph. |
438
+ | `hint?` | ReactNode | — | Message below the field. Neutral helper text by default; `status` recolors it and prefixes the matching glyph. |
449
439
  | `inputMode?` | 'none' \| 'text' \| 'decimal' \| 'numeric' \| 'tel' \| 'search' \| 'email' \| 'url' | — | Virtual-keyboard hint. Overrides the value derived from `type`. |
450
- | `label?` | ReactNode | — | Field label, shown above the control. A string is rendered with the
451
- label type scale; pass a node for full control. Associated with the
452
- control as its accessible name (the element mirrors the label text to
453
- `aria-label`, since `<label for>` can't cross the shadow boundary). |
440
+ | `label?` | ReactNode | — | Field label, shown above the control. A string is rendered with the label type scale; pass a node for full control. Associated with the control as its accessible name (the element mirrors the label text to `aria-label`, since `<label for>` can't cross the shadow boundary). |
454
441
  | `leading?` | ReactNode | — | Content pinned to the start of the field (e.g. an icon). |
455
442
  | `max?` | number \| string | — | |
456
443
  | `maxLength?` | number | — | Max input length. |
457
- | `maxRows?` | number | — | Cap the autogrow height (in rows) of a `multiline` field with no `rows`.
458
- Omit for unbounded growth. |
444
+ | `maxRows?` | number | — | Cap the autogrow height (in rows) of a `multiline` field with no `rows`. Omit for unbounded growth. |
459
445
  | `min?` | number \| string | — | Min / max / step — for `type="number"`. |
460
446
  | `minLength?` | number | — | Min input length. |
461
- | `multiline?` | boolean | — | Render a `<textarea>` instead of an `<input>`. Without `rows` it grows
462
- with its content from one line (capped by `maxRows` if set). Autogrow uses
463
- CSS `field-sizing` where supported (Chrome/Edge, Safari ≥ 26.2) and falls
464
- back to a built-in JS resize elsewhere (Firefox, older Safari), so it grows
465
- in every browser. |
447
+ | `multiline?` | boolean | — | Render a `<textarea>` instead of an `<input>`. Without `rows` it grows with its content from one line (capped by `maxRows` if set). Autogrow uses CSS `field-sizing` where supported (Chrome/Edge, Safari ≥ 26.2) and falls back to a built-in JS resize elsewhere (Firefox, older Safari), so it grows in every browser. |
466
448
  | `name?` | string | — | Form field name — submitted with the form via ElementInternals. |
467
449
  | `onBlur?` | (e) => void | — | Fires when the field loses focus. |
468
- | `onChange?` | (e) => void | — | Fires on **commit** (blur / Enter) — the platform `change` semantics, **not**
469
- React's per-keystroke `onChange`. This is a web component, so `onChange` keeps
470
- the native meaning; reach for `onInput` (every keystroke) or `onValueChange`
471
- (both) for live updates. Read `e.target.value`. |
472
- | `onClearClick?` | (e) => void | — | Fires when the built-in clear button (`clearable`) is clicked, *before*
473
- the field is cleared. Call `e.preventDefault()` to keep the current value
474
- — the clear is cancelled and `onClearInput` won't fire. Backed by the
475
- element's cancelable, bubbling `clearclick` event. |
476
- | `onClearInput?` | (e) => void | — | Fires after the built-in clear button (`clearable`) has cleared the field
477
- — so `onInput` / `onChange` fire too — making this useful for reacting
478
- specifically to a clear. Doesn't fire if `onClearClick` cancelled the
479
- clear. Backed by the element's bubbling `clearinput` event. |
450
+ | `onChange?` | (e) => void | — | Fires on **commit** (blur / Enter) — the platform `change` semantics, **not** React's per-keystroke `onChange`. This is a web component, so `onChange` keeps the native meaning; reach for `onInput` (every keystroke) or `onValueChange` (both) for live updates. Read `e.target.value`. |
451
+ | `onClearClick?` | (e) => void | — | Fires when the built-in clear button (`clearable`) is clicked, *before* the field is cleared. Call `e.preventDefault()` to keep the current value — the clear is cancelled and `onClearInput` won't fire. Backed by the element's cancelable, bubbling `clearclick` event. |
452
+ | `onClearInput?` | (e) => void | — | Fires after the built-in clear button (`clearable`) has cleared the field — so `onInput` / `onChange` fire too — making this useful for reacting specifically to a clear. Doesn't fire if `onClearClick` cancelled the clear. Backed by the element's bubbling `clearinput` event. |
480
453
  | `onFocus?` | (e) => void | — | Fires when the field gains focus. |
481
454
  | `onInput?` | (e) => void | — | Fires on every keystroke. Read `e.target.value`. |
482
- | `onValueChange?` | (event, attrs) => void | — | Unified value-change handler — the easy path for state. Fires on `input`
483
- *and* `change` (and on clear), with the native `event` plus a convenience
484
- `attrs` snapshot (`value`, `name`, `empty`, `valid`, `validationMessage`) so
485
- you can do `setForm(s => ({ ...s, [attrs.name]: attrs.value }))` without
486
- digging into the event. Use `event.type` to tell a live edit (`input`) from
487
- a commit (`change`); read `id` / `type` / `className` off `event.target`. |
455
+ | `onValueChange?` | (event, attrs) => void | — | Unified value-change handler — the easy path for state. Fires on `input` *and* `change` (and on clear), with the native `event` plus a convenience `attrs` snapshot (`value`, `name`, `empty`, `valid`, `validationMessage`) so you can do `setForm(s => ({ ...s, [attrs.name]: attrs.value }))` without digging into the event. Use `event.type` to tell a live edit (`input`) from a commit (`change`); read `id` / `type` / `className` off `event.target`. |
488
456
  | `pattern?` | string | — | Validation pattern (single-line). |
489
457
  | `placeholder?` | string | — | Placeholder shown when empty. |
490
458
  | `readOnly?` | boolean | — | Make the field read-only. |
491
459
  | `required?` | boolean | — | Mark the field required (drives native validity). |
492
- | `role?` | string | — | ARIA `role` for the field — e.g. `combobox` when the input drives a
493
- suggestion `listbox` (see `InputAutocomplete`). Left unset by default. |
494
- | `round?` | boolean \| number \| string | — | Fully-round the field (`border-radius: 999px`). Pass a `number` (px) or a CSS
495
- length string for a custom radius. The `clearable` × button always rounds to
496
- a circle to match (it isn't sized by a custom field value). |
497
- | `rows?` | number | — | Fixed visible row count — a constant-height `<textarea>` (implies
498
- `multiline`). |
499
- | `size?` | 'small' \| 'medium' \| 'large' | medium | Size variant. small=24px, medium=28px, large=32px tall; the type scale and
500
- icon track the size (small 13/16 + 14px icon · medium 15/20 + 16px ·
501
- large 17/22 + 18px). |
460
+ | `role?` | string | — | ARIA `role` for the field — e.g. `combobox` when the input drives a suggestion `listbox` (see `InputAutocomplete`). Left unset by default. |
461
+ | `round?` | boolean \| number \| string | — | Fully-round the field (`border-radius: 999px`). Pass a `number` (px) or a CSS length string for a custom radius. The `clearable` × button always rounds to a circle to match (it isn't sized by a custom field value). |
462
+ | `rows?` | number | — | Fixed visible row count — a constant-height `<textarea>` (implies `multiline`). |
463
+ | `size?` | 'small' \| 'medium' \| 'large' | medium | Size variant. small=24px, medium=28px, large=32px tall; the type scale and icon track the size (small 13/16 + 14px icon · medium 15/20 + 16px · large 17/22 + 18px). |
502
464
  | `spellCheck?` | boolean | — | Toggle native spell-checking. |
503
- | `status?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' | — | Validation / feedback tone — colors the border + `hint` and prefixes a
504
- glyph. Only `critical` marks the field invalid (`aria-invalid`, blocks form
505
- submission, `:state(invalid)`); `success` / `warning` / `info` / `brand`
506
- are advisory and stay valid. Omit (or `neutral`) for a plain field. |
507
- | `statusIcon?` | (string & {}) \| false \| IconShape | — | Glyph shown before the `hint` when `status` is set. Each status has a
508
- default (critical → `warning-diamond`, warning → `warning-triangle`,
509
- success → `circle-check`, info → `info`, brand → `circle-small-solid`); pass a
510
- shape to override, or `false` to drop it. `neutral` has no default glyph. |
465
+ | `status?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' | — | Validation / feedback tone — colors the border + `hint` and prefixes a glyph. Only `critical` marks the field invalid (`aria-invalid`, blocks form submission, `:state(invalid)`); `success` / `warning` / `info` / `brand` are advisory and stay valid. Omit (or `neutral`) for a plain field. |
466
+ | `statusIcon?` | (string & {}) \| false \| IconShape | — | Glyph shown before the `hint` when `status` is set. Each status has a default (critical → `warning-diamond`, warning → `warning-triangle`, success → `circle-check`, info → `info`, brand → `circle-small-solid`); pass a shape to override, or `false` to drop it. `neutral` has no default glyph. |
511
467
  | `step?` | number \| string | — | |
512
- | `tone?` | string | — | Custom accent color — any literal CSS color tints the resting + hover
513
- border (focus ring stays the global `--focus-ring`). For consistency with the
514
- other controls' custom-tone knob; a `status` still overrides for validation. |
515
- | `trailing?` | ReactNode | — | Content pinned to the end of the field (e.g. icons, buttons), after the
516
- clear button when `clearable`. |
517
- | `truncate?` | boolean | true | Ellipsize an overflowing single-line value. Read-only inputs already do
518
- this; pass `false` when an editable field should show the full value. |
519
- | `type?` | 'text' \| 'search' \| 'email' \| 'password' \| 'tel' \| 'url' \| 'number' | text | Single-line input type. Ignored when `multiline`. `search` is a
520
- **wrapper-only** shorthand: it defaults a leading search icon and a clear
521
- button (both overridable — pass your own `leading`, or `clearable={false}`)
522
- and sets `inputmode="search"`, but the DOM input stays `type="text"`. The
523
- native `search` type never reaches the element, so the browser's own
524
- clear/search affordances never appear — Anta owns that chrome. |
468
+ | `tone?` | string | — | Custom accent color — any literal CSS color tints the resting + hover border (focus ring stays the global `--focus-ring`). For consistency with the other controls' custom-tone knob; a `status` still overrides for validation. |
469
+ | `trailing?` | ReactNode | — | Content pinned to the end of the field (e.g. icons, buttons), after the clear button when `clearable`. |
470
+ | `truncate?` | boolean | true | Ellipsize an overflowing single-line value. Read-only inputs already do this; pass `false` when an editable field should show the full value. |
471
+ | `type?` | 'text' \| 'search' \| 'email' \| 'password' \| 'tel' \| 'url' \| 'number' | text | Single-line input type. Ignored when `multiline`. `search` is a **wrapper-only** shorthand: it defaults a leading search icon and a clear button (both overridable — pass your own `leading`, or `clearable={false}`) and sets `inputmode="search"`, but the DOM input stays `type="text"`. The native `search` type never reaches the element, so the browser's own clear/search affordances never appear — Anta owns that chrome. |
525
472
  | `value?` | string | — | Controlled value. Pair with `onChange` / `onInput`. |
526
473
 
474
+ ## Web Component
475
+
527
476
  Use the web component directly when you are not using React or Preact and a native control does not fit.
528
477
 
529
478
  Slots hold the label, hint, and controls around the native field.
@@ -568,6 +517,8 @@ their `size` attribute, so Anta does not reuse it. `round` and a custom-color
568
517
  <input data-anta type="reset" value="Reset">
569
518
  ```
570
519
 
520
+ ## Styling
521
+
571
522
  Reach for the props first: **`status`** sets a validation tone (border + message),
572
523
  **`tone`** a custom accent color for the border (any CSS color), **`size`** the
573
524
  dimensions. The focus ring is the global [`--focus-ring`](../colors.md#focus-ring).
@@ -52,18 +52,18 @@ label, state, and semantics.
52
52
 
53
53
  Use `<Loader>` when you need a value, speed, or accessible loading status.
54
54
 
55
- ### Props
55
+ ## Component props
56
56
 
57
57
  | Prop | Type | Default | Description |
58
58
  |------|------|---------|-------------|
59
- | `label?` | string | — | Accessible name for a standalone loader. Without a label, the loader is
60
- decorative so nearby status text remains the single announcement. |
59
+ | `label?` | string | — | Accessible name for a standalone loader. Without a label, the loader is decorative so nearby status text remains the single announcement. |
61
60
  | `max?` | number | 100 | Upper bound of the range. |
62
61
  | `size?` | number | 16 | Width and height in pixels. |
63
62
  | `speed?` | number | 0.75 | Seconds per rotation. Pass a positive number. |
64
63
  | `tone?` | LoaderTone | — | Color tone for the loader. Omit it to use the standard text-3 color. |
65
- | `value?` | number \| false | — | Current progress value. Omit this prop, or pass `false`, for an
66
- indeterminate rotating gradient. |
64
+ | `value?` | number \| false | — | Current progress value. Omit this prop, or pass `false`, for an indeterminate rotating gradient. |
65
+
66
+ ## Web Component
67
67
 
68
68
  Use a raw `<a-loader>` when you are not using the JSX wrapper. Set its size,
69
69
  speed, and determinate percentage with CSS custom properties. `value` selects
@@ -92,6 +92,8 @@ the static ring; `--loader-value` is the percentage it shows.
92
92
  When the value is unknown, omit `value` and give a standalone loader an
93
93
  accessible name. Mark a loader beside visible status text with `aria-hidden`.
94
94
 
95
+ ## Styling
96
+
95
97
  The standalone Loader uses the quiet `--text-3` color by default. Set `color`
96
98
  directly when it needs a deliberate semantic or one-off color; the `loader`
97
99
  icon alias inherits its parent control’s color.
@@ -366,6 +366,8 @@ chevron stay centered on the row, so it composes with a trailing counter.
366
366
  </Menu>
367
367
  ```
368
368
 
369
+ ## MenuItemCopy
370
+
369
371
  `MenuItemCopy` is a row that copies to the clipboard when chosen: `copy` for a
370
372
  string, `copyNode` for a DOM region, or `copyUrl` for the current URL. It composes
371
373
  a `<MenuItem>` with a slotted `<a-copy>` element that performs the write, and
@@ -390,89 +392,32 @@ navigating links.
390
392
 
391
393
  | Prop | Type | Default | Description |
392
394
  |------|------|---------|-------------|
393
- | `children?` | ReactNode | — | Item content. With `label` set, children are extra content — most
394
- notably the nested `<Menu>` for a submenu parent. |
395
+ | `children?` | ReactNode | — | Item content. With `label` set, children are extra content — most notably the nested `<Menu>` for a submenu parent. |
395
396
  | `copiedLabel?` | string | Copied | Text in the successful no-icon confirmation. |
396
397
  | `copy?` | string | — | Text copied to the clipboard on activation. |
397
- | `copyNode?` | boolean \| string | — | Copy a DOM node as rich text (`text/html`) + plain text. `true` copies
398
- the nearest ancestor marked `data-copy-source`; a string is a CSS
399
- selector for an ancestor region (`closest`). The copy control is stripped
400
- from the copied output. |
398
+ | `copyNode?` | boolean \| string | — | Copy a DOM node as rich text (`text/html`) + plain text. `true` copies the nearest ancestor marked `data-copy-source`; a string is a CSS selector for an ancestor region (`closest`). The copy control is stripped from the copied output. |
401
399
  | `copyUrl?` | true | — | Copy the current page URL (`location.href`). |
402
400
  | `copyWithUrl?` | boolean | — | Prefix the copied text with `// URL: <current page URL>`. |
403
- | `disabled?` | boolean | — | Disable the item: greyed out, not focusable for activation, no close. A
404
- disabled link also drops its `href`, so it can't navigate. |
405
- | `hint?` | ReactNode | — | Secondary text under the label — explanatory copy, like `RadioGroup`'s
406
- option `hint`. Requires `label` (it stacks in a column beneath it). Muted
407
- (`--text-3`) and tracks the row's `tone`. A string, or any node. |
401
+ | `disabled?` | boolean | — | Disable the item: greyed out, not focusable for activation, no close. A disabled link also drops its `href`, so it can't navigate. |
402
+ | `hint?` | ReactNode | — | Secondary text under the label — explanatory copy, like `RadioGroup`'s option `hint`. Requires `label` (it stacks in a column beneath it). Muted (`--text-3`) and tracks the row's `tone`. A string, or any node. |
408
403
  | `icon?` | IconShape | — | Leading icon shape. |
409
- | `iconPlacement?` | 'leading' \| 'trailing' \| 'none' | 'leading' | Where the copy glyph sits relative to the label — or `'none'` to omit it.
410
- Without a glyph, a successful copy shows a confirmation label near the
411
- pointer and leaves the row unchanged. |
412
- | `iconTrailing?` | IconShape | — | A trailing icon. On a `submenu` item this **overrides** the default
413
- chevron (omit it to keep the chevron); on a normal item it's the trailing
414
- glyph (omit for none). |
415
- | `indeterminate?` | boolean | — | Only meaningful with `selectionIndicator="checkbox"`: render the box in the
416
- mixed state (`aria-checked="mixed"`) — e.g. a "Select all" row when some but
417
- not all of its options are selected. |
418
- | `indicator?` | React.ReactNode | — | Replace the built-in selection-indicator *visual* with your own node,
419
- rendered at the **leading** edge (where the checkbox / radio sit). Pair with
420
- `selectionIndicator` to keep the semantics — the row stays the control and
421
- carries `role` + `aria-checked`; only the drawn mark changes. Suppresses the
422
- built-in checkbox / radio and the trailing `check` glyph. The node is made
423
- passive (aria-hidden, no pointer events) so the row receives the click. |
424
- | `kbd?` | string | — | Trailing shortcut hint, e.g. `"⌘E"`. Anta translates it to
425
- `aria-keyshortcuts` and hides the visual glyphs from assistive technology.
426
- Use `+` within a chord and spaces between shortcuts (`"⌘K ⌘S"` becomes
427
- `"Meta+K Meta+S"`). Pass `aria-keyshortcuts` to override the translation.
428
- This does not bind the shortcut. |
429
- | `label?` | ReactNode | — | The item's text. Usually a string, but any node is accepted — e.g. a filtered
430
- `Select` bolds the matched substring. Omit and pass `children` for richer
431
- content. |
404
+ | `iconPlacement?` | 'leading' \| 'trailing' \| 'none' | 'leading' | Where the copy glyph sits relative to the label — or `'none'` to omit it. Without a glyph, a successful copy shows a confirmation label near the pointer and leaves the row unchanged. |
405
+ | `iconTrailing?` | IconShape | — | A trailing icon. On a `submenu` item this **overrides** the default chevron (omit it to keep the chevron); on a normal item it's the trailing glyph (omit for none). |
406
+ | `indeterminate?` | boolean | — | Only meaningful with `selectionIndicator="checkbox"`: render the box in the mixed state (`aria-checked="mixed"`) — e.g. a "Select all" row when some but not all of its options are selected. |
407
+ | `indicator?` | React.ReactNode | — | Replace the built-in selection-indicator *visual* with your own node, rendered at the **leading** edge (where the checkbox / radio sit). Pair with `selectionIndicator` to keep the semantics — the row stays the control and carries `role` + `aria-checked`; only the drawn mark changes. Suppresses the built-in checkbox / radio and the trailing `check` glyph. The node is made passive (aria-hidden, no pointer events) so the row receives the click. |
408
+ | `kbd?` | string | — | Trailing shortcut hint, e.g. `"⌘E"`. Anta translates it to `aria-keyshortcuts` and hides the visual glyphs from assistive technology. Use `+` within a chord and spaces between shortcuts (`"⌘K ⌘S"` becomes `"Meta+K Meta+S"`). Pass `aria-keyshortcuts` to override the translation. This does not bind the shortcut. |
409
+ | `label?` | ReactNode | — | The item's text. Usually a string, but any node is accepted — e.g. a filtered `Select` bolds the matched substring. Omit and pass `children` for richer content. |
432
410
  | `onCopied?` | (ok) => void | — | Fires after the copy attempt with whether it succeeded. |
433
- | `onCopyRequest?` | () => void | — | Refresh a dynamic `copy` value before activation. Set the new string in
434
- application state so the next render updates `copy`. Return values are
435
- ignored. Fires on pointerdown and Enter/Space keydown. |
436
- | `onMouseDown?` | (event) => void | — | Raw `mousedown` on the row. Mainly to `preventDefault()` so the row doesn't
437
- take focus on a mouse press — e.g. a combobox option keeping focus in its
438
- input field while the click still selects. |
439
- | `onSelect?` | (event, detail) => void | — | Activation handler — fires when *this* item is chosen (click / Enter /
440
- Space), unless it's `disabled`. It does **not** fire for a submenu parent
441
- (clicking that opens the flyout, which isn't a selection) nor for a
442
- selection bubbling up from a nested submenu. On a link item it fires
443
- alongside the navigation. Receives the event plus a `{ value, label }`
444
- detail. |
445
- | `role?` | string | — | ARIA role override. Defaults to the role implied by `selectionIndicator`
446
- (`menuitem` / `menuitemcheckbox` / `menuitemradio`); set it to reparent the
447
- row under a different container role — e.g. `option` inside a `listbox`. |
448
- | `selected?` | boolean | — | Mark the item as selected. On a plain row (no `selectionIndicator`) this is
449
- a persistent background tint, the same resting fill a pressed row shows —
450
- also the way to flag the current page on a link item. On a checkable row
451
- (`selectionIndicator` set) it instead drives the leading `checkbox` / `radio`
452
- indicator and the row's `aria-checked`. |
453
- | `selectionIndicator?` | 'checkbox' \| 'radio' \| 'check' | — | Turn the row into a checkable item, driven by `selected` (the row stays the
454
- control and carries `aria-checked`):
455
- - `'checkbox'` → `role="menuitemcheckbox"`, a leading passive `<a-checkbox>`
456
- (before `icon`); the tint is dropped (the box carries state).
457
- - `'radio'` → `role="menuitemradio"`, a leading passive `<a-radio>`; tint dropped.
458
- - `'check'` → `role="menuitemradio"`, a trailing check glyph on the selected
459
- row *and* the background tint (the canonical single-select look).
460
- Omit for a plain row (the default). |
461
- | `submenu?` | boolean | — | Marks this item as a submenu parent: adds the trailing chevron and
462
- `aria-haspopup="menu"`. Nest the flyout as a `<Menu>` child. |
463
- | `tone?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | neutral | Semantic tone — colors the label, icon, and hover/selected tint (and the
464
- `checkbox`/`radio` indicator, which adopts it). A named tone, or any literal
465
- CSS color (`'#ff1493'`, `'rebeccapurple'`) for a one-off custom tone whose
466
- hue + chroma are kept while the lightness is pinned to match the brand text.
467
- `critical` is the destructive action; `neutral` (the default) is the standard
468
- gray. |
469
- | `toneSelected?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | neutral | Like `tone`, but applied only while the row is `selected` — an unselected row
470
- stays neutral. The whole selected row (label, icon, tint, and the `checkbox` /
471
- `radio` indicator) takes the tone. Same value set as `tone`; on a selected row
472
- `toneSelected` wins over `tone` when both are set. |
473
- | `value?` | string \| number | — | An opaque value identifying this item, handed back in `onSelect`'s detail
474
- so a shared handler can tell which row was chosen without a per-item
475
- closure. |
411
+ | `onCopyRequest?` | () => void | — | Refresh a dynamic `copy` value before activation. Set the new string in application state so the next render updates `copy`. Return values are ignored. Fires on pointerdown and Enter/Space keydown. |
412
+ | `onMouseDown?` | (event) => void | — | Raw `mousedown` on the row. Mainly to `preventDefault()` so the row doesn't take focus on a mouse press — e.g. a combobox option keeping focus in its input field while the click still selects. |
413
+ | `onSelect?` | (event, detail) => void | — | Activation handler — fires when *this* item is chosen (click / Enter / Space), unless it's `disabled`. It does **not** fire for a submenu parent (clicking that opens the flyout, which isn't a selection) nor for a selection bubbling up from a nested submenu. On a link item it fires alongside the navigation. Receives the event plus a `{ value, label }` detail. |
414
+ | `role?` | string | — | ARIA role override. Defaults to the role implied by `selectionIndicator` (`menuitem` / `menuitemcheckbox` / `menuitemradio`); set it to reparent the row under a different container role — e.g. `option` inside a `listbox`. |
415
+ | `selected?` | boolean | — | Mark the item as selected. On a plain row (no `selectionIndicator`) this is a persistent background tint, the same resting fill a pressed row shows — also the way to flag the current page on a link item. On a checkable row (`selectionIndicator` set) it instead drives the leading `checkbox` / `radio` indicator and the row's `aria-checked`. |
416
+ | `selectionIndicator?` | 'checkbox' \| 'radio' \| 'check' | — | Turn the row into a checkable item, driven by `selected` (the row stays the control and carries `aria-checked`): - `'checkbox'` → `role="menuitemcheckbox"`, a leading passive `<a-checkbox>` (before `icon`); the tint is dropped (the box carries state). - `'radio'` → `role="menuitemradio"`, a leading passive `<a-radio>`; tint dropped. - `'check'` → `role="menuitemradio"`, a trailing check glyph on the selected row *and* the background tint (the canonical single-select look). Omit for a plain row (the default). |
417
+ | `submenu?` | boolean | — | Marks this item as a submenu parent: adds the trailing chevron and `aria-haspopup="menu"`. Nest the flyout as a `<Menu>` child. |
418
+ | `tone?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | neutral | Semantic tone — colors the label, icon, and hover/selected tint (and the `checkbox`/`radio` indicator, which adopts it). A named tone, or any literal CSS color (`'#ff1493'`, `'rebeccapurple'`) for a one-off custom tone whose hue + chroma are kept while the lightness is pinned to match the brand text. `critical` is the destructive action; `neutral` (the default) is the standard gray. |
419
+ | `toneSelected?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | neutral | Like `tone`, but applied only while the row is `selected` — an unselected row stays neutral. The whole selected row (label, icon, tint, and the `checkbox` / `radio` indicator) takes the tone. Same value set as `tone`; on a selected row `toneSelected` wins over `tone` when both are set. |
420
+ | `value?` | string \| number | — | An opaque value identifying this item, handed back in `onSelect`'s detail so a shared handler can tell which row was chosen without a per-item closure. |
476
421
 
477
422
  ## Keyboard
478
423
 
@@ -506,6 +451,55 @@ call `event.preventDefault()` to keep the menu as-is (e.g. block a dismiss).
506
451
  Controlled, the menu never self-moves — apply `detail.next` to `open` to accept,
507
452
  or do nothing to reject. (Submenus are always uncontrolled, regardless of `open`.)
508
453
 
454
+ ## Menu props
455
+
456
+ | Prop | Type | Default | Description |
457
+ |------|------|---------|-------------|
458
+ | `autoWidth?` | boolean | — | Size the menu to its content instead of flooring its width to the trigger. A root menu is never narrower than its trigger by default; set this for a content menu under a wide trigger (e.g. a calendar below a full-width field) so it wraps its content and left-aligns under the trigger. |
459
+ | `children?` | ReactNode | — | The menu's contents: `MenuItem`, `MenuSeparator`, `MenuGroup`, or any custom element. |
460
+ | `context?` | boolean | — | Open on right-click (the `contextmenu` event) of the trigger region instead of a left click, positioned at the pointer. |
461
+ | `coord?` | boolean | — | Open at the pointer coordinates rather than aligned to the trigger box. Pairs naturally with `context`; on its own it positions a left-click menu at the cursor. |
462
+ | `inset?` | number \| string | 4 | Inner inset for the scroll body and named header/footer content. A number is pixels; a CSS length string is used as written. |
463
+ | `nohover?` | boolean | — | Submenu-only (a `<Menu>` nested inside a `MenuItem`); ignored on a root menu. Submenus open on hover by default (with intent timing) as well as on click — set `nohover` to make this submenu click-only. Hover-intent is mouse-only regardless: on touch (and pen) a submenu always opens on tap and stays open until dismissed. |
464
+ | `offset?` | number | 4 | Gap in pixels between the trigger and the menu. |
465
+ | `onactivedescendant?` | (e) => void | — | Combobox-mode cursor report (raw element event). Fires when the active option changes as arrow keys move the cursor while focus stays in a `[data-menu-search]` filter field; `detail.id` is the active option's `id` (`null` when none). The reactive layer that renders the field reflects this onto its `aria-activedescendant` — the element can't write that light-DOM attribute itself. Passed straight through to the element. |
466
+ | `onStateChange?` | (event, detail) => void | — | Fired before the open state changes — on open, and on every dismiss (Esc, outside-click, scroll, selecting an item). `event` is the cancelable `statechange`; `detail.next`/`detail.prev` are the requested/previous open state (booleans). It's the declarative way to observe a menu, and the handler you pair with `open` to drive a controlled menu (apply `detail.next` to `open`). Uncontrolled, `event.preventDefault()` vetoes the transition (e.g. keep the menu open). |
467
+ | `open?` | boolean | — | Controlled open state. Omit for the default **uncontrolled** menu (it opens/closes itself via its triggers). Pass a boolean to **control** it: the menu's visibility follows `open`, and user dismiss (Esc, outside-click, select) fires `onStateChange` *without* self-closing — you update `open` in response. Submenus are always uncontrolled regardless of this. See STATEFUL-COMPONENTS.md. |
468
+ | `placement?` | 'bottom-start' \| 'bottom-end' \| 'top-start' \| 'top-end' \| 'bottom' \| 'top' \| 'right-start' \| 'right-end' \| 'left-start' \| 'left-end' \| 'right' \| 'left' | bottom-start | Preferred placement relative to the trigger. `bottom` / `top` place the menu below / above; `right` / `left` place it beside (like a submenu flyout). The cross-axis suffix (`-start` / `-end`) aligns the near / far edges, and no suffix centers on the trigger. The menu flips to the opposite side when the preferred one lacks room, and clamps to the viewport. |
469
+ | `role?` | string | menu | ARIA role of the menu surface. |
470
+ | `round?` | boolean \| number \| string | — | Round the menu: the container softens to a 20px radius and its items go fully round. A `number` (px) or CSS length string tunes the container radius only — items stay full pills. |
471
+ | `stopPropagation?` | boolean \| string \| string[] | — | Contain events so they don't bubble out of the menu to ancestor handlers (e.g. a clickable row the menu sits in). Selecting an item is always contained; this extends it to whole event types. `true` contains `click`; pass an event name or list (`"click pointerdown"`) to contain those. |
472
+
473
+ ## MenuItem props
474
+
475
+ | Prop | Type | Default | Description |
476
+ |------|------|---------|-------------|
477
+ | `children?` | ReactNode | — | Item content. With `label` set, children are extra content — most notably the nested `<Menu>` for a submenu parent. |
478
+ | `disabled?` | boolean | — | Disable the item: greyed out, not focusable for activation, no close. A disabled link also drops its `href`, so it can't navigate. |
479
+ | `download?` | string \| boolean | — | Download the resource instead of navigating: `true` / `''` uses the resource's default filename, a string overrides it. |
480
+ | `hint?` | ReactNode | — | Secondary text under the label — explanatory copy, like `RadioGroup`'s option `hint`. Requires `label` (it stacks in a column beneath it). Muted (`--text-3`) and tracks the row's `tone`. A string, or any node. |
481
+ | `href?` | string | — | Renders the item as `<a role="menuitem" data-anta-menu-item href>`. |
482
+ | `icon?` | IconShape | — | Leading icon shape. |
483
+ | `iconTrailing?` | IconShape | — | A trailing icon. On a `submenu` item this **overrides** the default chevron (omit it to keep the chevron); on a normal item it's the trailing glyph (omit for none). |
484
+ | `indeterminate?` | boolean | — | Only meaningful with `selectionIndicator="checkbox"`: render the box in the mixed state (`aria-checked="mixed"`) — e.g. a "Select all" row when some but not all of its options are selected. |
485
+ | `indicator?` | React.ReactNode | — | Replace the built-in selection-indicator *visual* with your own node, rendered at the **leading** edge (where the checkbox / radio sit). Pair with `selectionIndicator` to keep the semantics — the row stays the control and carries `role` + `aria-checked`; only the drawn mark changes. Suppresses the built-in checkbox / radio and the trailing `check` glyph. The node is made passive (aria-hidden, no pointer events) so the row receives the click. |
486
+ | `kbd?` | string | — | Trailing shortcut hint, e.g. `"⌘E"`. Anta translates it to `aria-keyshortcuts` and hides the visual glyphs from assistive technology. Use `+` within a chord and spaces between shortcuts (`"⌘K ⌘S"` becomes `"Meta+K Meta+S"`). Pass `aria-keyshortcuts` to override the translation. This does not bind the shortcut. |
487
+ | `label?` | ReactNode | — | The item's text. Usually a string, but any node is accepted — e.g. a filtered `Select` bolds the matched substring. Omit and pass `children` for richer content. |
488
+ | `onMouseDown?` | (event) => void | — | Raw `mousedown` on the row. Mainly to `preventDefault()` so the row doesn't take focus on a mouse press — e.g. a combobox option keeping focus in its input field while the click still selects. |
489
+ | `onSelect?` | (event, detail) => void | — | Activation handler — fires when *this* item is chosen (click / Enter / Space), unless it's `disabled`. It does **not** fire for a submenu parent (clicking that opens the flyout, which isn't a selection) nor for a selection bubbling up from a nested submenu. On a link item it fires alongside the navigation. Receives the event plus a `{ value, label }` detail. |
490
+ | `ping?` | string | — | Space-separated URLs the browser pings on navigation. |
491
+ | `rel?` | string | — | Anchor rel. |
492
+ | `role?` | string | — | ARIA role override. Defaults to the role implied by `selectionIndicator` (`menuitem` / `menuitemcheckbox` / `menuitemradio`); set it to reparent the row under a different container role — e.g. `option` inside a `listbox`. |
493
+ | `selected?` | boolean | — | Mark the item as selected. On a plain row (no `selectionIndicator`) this is a persistent background tint, the same resting fill a pressed row shows — also the way to flag the current page on a link item. On a checkable row (`selectionIndicator` set) it instead drives the leading `checkbox` / `radio` indicator and the row's `aria-checked`. |
494
+ | `selectionIndicator?` | 'checkbox' \| 'radio' \| 'check' | — | Turn the row into a checkable item, driven by `selected` (the row stays the control and carries `aria-checked`): - `'checkbox'` → `role="menuitemcheckbox"`, a leading passive `<a-checkbox>` (before `icon`); the tint is dropped (the box carries state). - `'radio'` → `role="menuitemradio"`, a leading passive `<a-radio>`; tint dropped. - `'check'` → `role="menuitemradio"`, a trailing check glyph on the selected row *and* the background tint (the canonical single-select look). Omit for a plain row (the default). |
495
+ | `submenu?` | boolean | — | Marks this item as a submenu parent: adds the trailing chevron and `aria-haspopup="menu"`. Nest the flyout as a `<Menu>` child. |
496
+ | `target?` | string | — | Anchor target, e.g. `'_blank'`. |
497
+ | `tone?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | neutral | Semantic tone — colors the label, icon, and hover/selected tint (and the `checkbox`/`radio` indicator, which adopts it). A named tone, or any literal CSS color (`'#ff1493'`, `'rebeccapurple'`) for a one-off custom tone whose hue + chroma are kept while the lightness is pinned to match the brand text. `critical` is the destructive action; `neutral` (the default) is the standard gray. |
498
+ | `toneSelected?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | neutral | Like `tone`, but applied only while the row is `selected` — an unselected row stays neutral. The whole selected row (label, icon, tint, and the `checkbox` / `radio` indicator) takes the tone. Same value set as `tone`; on a selected row `toneSelected` wins over `tone` when both are set. |
499
+ | `value?` | string \| number | — | An opaque value identifying this item, handed back in `onSelect`'s detail so a shared handler can tell which row was chosen without a per-item closure. |
500
+
501
+ ## Web Component
502
+
509
503
  Use the web component directly when you are not using React or Preact and a native control does not fit.
510
504
 
511
505
  Put a focusable trigger immediately before `<a-menu>` and omit `state` for an
@@ -522,6 +516,8 @@ values.
522
516
  </a-menu>
523
517
  ```
524
518
 
519
+ ## Styling
520
+
525
521
  The popover surface lives in shadow DOM and is exposed as a **part** — style its
526
522
  chrome (background, frost, border, radius, shadow, padding, min-width) with
527
523
  `::part(menu)`. Menu items are light DOM, so style them (and their
@@ -92,25 +92,19 @@ works for one at the bottom.
92
92
  `round` makes the track fully round. Pass a number or CSS length for a custom
93
93
  radius.
94
94
 
95
- ### Props
95
+ ## Component props
96
96
 
97
97
  | Prop | Type | Default | Description |
98
98
  |------|------|---------|-------------|
99
- | `hint?` | string | — | Right-aligned hint text (e.g. "3 of 7"). Like `label`, it's not rendered
100
- when custom `children` are provided but still feeds the accessible name. |
101
- | `label?` | string | — | Text label displayed after the percentage, or on its own for
102
- indeterminate progress. When you provide custom `children` (which replace
103
- the default label row), `label` is no longer rendered — but it still
104
- supplies the progressbar's accessible name. |
99
+ | `hint?` | string | — | Right-aligned hint text (e.g. "3 of 7"). Like `label`, it's not rendered when custom `children` are provided but still feeds the accessible name. |
100
+ | `label?` | string | — | Text label displayed after the percentage, or on its own for indeterminate progress. When you provide custom `children` (which replace the default label row), `label` is no longer rendered — but it still supplies the progressbar's accessible name. |
105
101
  | `max?` | number | 100 | Upper bound of the range. |
106
- | `round?` | boolean \| number \| string | — | Fully-round track (`border-radius: 999px`); the fill is clipped to it. Pass a
107
- `number` (px) or a CSS length string for a custom radius. |
102
+ | `round?` | boolean \| number \| string | — | Fully-round track (`border-radius: 999px`); the fill is clipped to it. Pass a `number` (px) or a CSS length string for a custom radius. |
108
103
  | `size?` | 'small' \| 'medium' \| 'large' | medium | Size variant. Scales the track and the default label row together. |
109
- | `tone?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | 'neutral' | Color variant, or any literal CSS color for a one-off custom tone (the
110
- surface / indicator / text are derived from it in oklch). Named tones track
111
- light/dark automatically. |
112
- | `value?` | number \| false | — | Current progress value. Omit this prop, or pass `false`, to show
113
- indeterminate progress. Negative values are clamped to 0. |
104
+ | `tone?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | 'neutral' | Color variant, or any literal CSS color for a one-off custom tone (the surface / indicator / text are derived from it in oklch). Named tones track light/dark automatically. |
105
+ | `value?` | number \| false | — | Current progress value. Omit this prop, or pass `false`, to show indeterminate progress. Negative values are clamped to 0. |
106
+
107
+ ## Web Component
114
108
 
115
109
  ```html
116
110
  <a-progress style="width: 100%" value="42" max="100" tone="info" role="progressbar"
@@ -158,6 +152,8 @@ is absent, while using Anta's track, fill, tone, and size treatments. Native
158
152
  progress does not render Anta's label or hint structure; give it an accessible
159
153
  name instead.
160
154
 
155
+ ## Styling
156
+
161
157
  ```css
162
158
  /* a fancy bar: rounded track (all sides), a multi-stop gradient fill with a glow,
163
159
  and a crisp hard right edge (the default soft right-edge fade is cleared). */
@@ -250,50 +250,29 @@ element — `Input`, and the composed `Select` — skip it and expose only
250
250
  <RadioGroup onValueChange={(_e, { value }) => save(value)} options={…} />
251
251
  ```
252
252
 
253
+ ## RadioGroup props
254
+
253
255
  | Prop | Type | Default | Description |
254
256
  |------|------|---------|-------------|
255
- | `options` | RadioOption[] | — | The options. The wrapper renders one `<a-radio>` per entry and computes its
256
- `selected` / roving `tabindex` / `role` declaratively. |
257
+ | `options` | RadioOption[] | — | The options. The wrapper renders one `<a-radio>` per entry and computes its `selected` / roving `tabindex` / `role` declaratively. |
257
258
  | `defaultValue?` | string | — | Initial selected value for uncontrolled use. |
258
259
  | `disabled?` | boolean | — | Disable the whole group. |
259
- | `hint?` | string | — | Plain-text description for the group, rendered directly under `label` (above
260
- the options) — typically instructional copy. Per-option helper text goes on
261
- the option's own `hint` instead. |
260
+ | `hint?` | string | — | Plain-text description for the group, rendered directly under `label` (above the options) — typically instructional copy. Per-option helper text goes on the option's own `hint` instead. |
262
261
  | `label?` | string | — | Plain-text label for the whole group, rendered above the options. |
263
- | `name?` | string | — | Form field name — the group submits one `name=value` (it's the
264
- form-associated element). |
262
+ | `name?` | string | — | Form field name — the group submits one `name=value` (it's the form-associated element). |
265
263
  | `onBlur?` | (event) => void | — | Fired when focus leaves the group entirely — wired to `focusout`. |
266
- | `onChange?` | (event) => void | — | Fired *after* the selection changes — a native `change` event (the post-apply
267
- counterpart to `onStateChange`). Not cancelable; for a controlled group it
268
- fires once you've updated `value`. |
269
- | `onFocus?` | (event) => void | — | Fired when focus enters the group (any option) — wired to `focusin`, since
270
- focus lands on an individual option, not the group element itself. |
271
- | `onStateChange?` | (event, detail) => void | — | Fired whenever selection changes — event-first. `detail` is
272
- `{ next, prev, reason }`: `next`/`prev` are values (`null` = nothing selected);
273
- `reason` is `'user'` \| `'reset'` \| `'restore'`. A `'user'` pick fires *before*
274
- applying and is **cancelable** — `event.preventDefault()` vetoes it
275
- (uncontrolled), or in controlled mode answer by updating `value` (reject by
276
- doing nothing). `'reset'` (form reset) and `'restore'` (bfcache / autofill) are
277
- not cancelable — filter on `reason` if you only track user picks. |
278
- | `onValueChange?` | (event, attrs) => void | — | Like `onChange`, but with a `{ value, name }` snapshot as the second argument,
279
- matching `Input`. |
264
+ | `onChange?` | (event) => void | — | Fired *after* the selection changes — a native `change` event (the post-apply counterpart to `onStateChange`). Not cancelable; for a controlled group it fires once you've updated `value`. |
265
+ | `onFocus?` | (event) => void | — | Fired when focus enters the group (any option) — wired to `focusin`, since focus lands on an individual option, not the group element itself. |
266
+ | `onStateChange?` | (event, detail) => void | — | Fired whenever selection changes — event-first. `detail` is `{ next, prev, reason }`: `next`/`prev` are values (`null` = nothing selected); `reason` is `'user'` \| `'reset'` \| `'restore'`. A `'user'` pick fires *before* applying and is **cancelable** — `event.preventDefault()` vetoes it (uncontrolled), or in controlled mode answer by updating `value` (reject by doing nothing). `'reset'` (form reset) and `'restore'` (bfcache / autofill) are not cancelable — filter on `reason` if you only track user picks. |
267
+ | `onValueChange?` | (event, attrs) => void | — | Like `onChange`, but with a `{ value, name }` snapshot as the second argument, matching `Input`. |
280
268
  | `orientation?` | 'vertical' \| 'horizontal' | 'vertical' | Layout + arrow-key axis. |
281
269
  | `size?` | 'small' \| 'medium' \| 'large' | 'medium' | Size applied to every option (an option's own `size` wins). |
282
- | `status?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' | 'neutral' | Validation/feedback tone for the group `hint` — recolors it (same tone set
283
- as `Input`'s `status`). Use `critical` for an error message, etc.; omit for
284
- the neutral default. |
285
- | `tone?` | 'brand' \| 'neutral' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | 'neutral' | Mark tone applied to every option (an option's own `tone` wins), or any literal
286
- CSS color for a one-off custom tone. Colors the selected-ring fill + dot *and*
287
- the unselected ring border. Named tones track light/dark mode. Use `toneSelected`
288
- instead to tone only the selected option and leave the rest neutral. The option
289
- text stays neutral — recolor it in plain CSS via the `--text-N-{tone}` tokens. |
290
- | `toneSelected?` | 'brand' \| 'neutral' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | 'neutral' | Like `tone`, but colored onto the **selected option only** — every unselected
291
- ring stays neutral grey. Applied to every option (an option's own `toneSelected`
292
- wins). Prefer this over `tone` when a resting tinted border would read as a
293
- validation state. |
294
- | `value?` | string | — | Controlled selected value. The group follows this prop and reports a
295
- requested change through `onStateChange`. Leave it undefined for
296
- uncontrolled use. |
270
+ | `status?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' | 'neutral' | Validation/feedback tone for the group `hint` — recolors it (same tone set as `Input`'s `status`). Use `critical` for an error message, etc.; omit for the neutral default. |
271
+ | `tone?` | 'brand' \| 'neutral' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | 'neutral' | Mark tone applied to every option (an option's own `tone` wins), or any literal CSS color for a one-off custom tone. Colors the selected-ring fill + dot *and* the unselected ring border. Named tones track light/dark mode. Use `toneSelected` instead to tone only the selected option and leave the rest neutral. The option text stays neutral — recolor it in plain CSS via the `--text-N-{tone}` tokens. |
272
+ | `toneSelected?` | 'brand' \| 'neutral' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | 'neutral' | Like `tone`, but colored onto the **selected option only** — every unselected ring stays neutral grey. Applied to every option (an option's own `toneSelected` wins). Prefer this over `tone` when a resting tinted border would read as a validation state. |
273
+ | `value?` | string | — | Controlled selected value. The group follows this prop and reports a requested change through `onStateChange`. Leave it undefined for uncontrolled use. |
274
+
275
+ ## Web Component
297
276
 
298
277
  Use the web component directly when you are not using React or Preact and a native control does not fit.
299
278
 
@@ -326,6 +305,8 @@ treatments.
326
305
  </div>
327
306
  ```
328
307
 
308
+ ## Styling
309
+
329
310
  Reach for the props first: **`tone`** colors the mark in every state,
330
311
  **`toneSelected`** only the selected option (any CSS color for a custom tone, set on
331
312
  the group or a single option — derives the full curve in oklch), **`size`** the
@@ -358,7 +358,7 @@ const recency: SelectFacetCustom<Recency> = {
358
358
  // concrete date (or take the day as-is) and filter your rows with it
359
359
  ```
360
360
 
361
- ### Props
361
+ ## Component props
362
362
 
363
363
  | Prop | Type | Default | Description |
364
364
  |------|------|---------|-------------|
@@ -370,33 +370,15 @@ const recency: SelectFacetCustom<Recency> = {
370
370
  | `icon?` | IconShape | filter | Default trigger's leading icon. |
371
371
  | `label?` | string | Filter | Default trigger's button label. |
372
372
  | `offset?` | number | 4 | Gap in pixels between the trigger and the filter menu. |
373
- | `onValueChange?` | (value, attrs) => void | — | Fires after any facet changes. `value` is the whole new record (a facet key →
374
- that facet's value; a cleared facet is absent). `attrs` says what changed:
375
- `{ facet, kind, value }` for a single facet edit, or `{ all: true }` for the
376
- "Clear all" row — narrow on `'all' in attrs` before reading `facet`. |
377
- | `placement?` | 'left' \| 'right' \| 'bottom' \| 'top' \| 'bottom-start' \| 'bottom-end' \| 'top-start' \| 'top-end' \| 'right-start' \| 'right-end' \| 'left-start' \| 'left-end' | bottom-start | Preferred placement of the root filter menu relative to its trigger. The
378
- menu auto-flips vertically and clamps horizontally when needed. |
373
+ | `onValueChange?` | (value, attrs) => void | — | Fires after any facet changes. `value` is the whole new record (a facet key → that facet's value; a cleared facet is absent). `attrs` says what changed: `{ facet, kind, value }` for a single facet edit, or `{ all: true }` for the "Clear all" row — narrow on `'all' in attrs` before reading `facet`. |
374
+ | `placement?` | 'left' \| 'right' \| 'bottom' \| 'top' \| 'bottom-start' \| 'bottom-end' \| 'top-start' \| 'top-end' \| 'right-start' \| 'right-end' \| 'left-start' \| 'left-end' | bottom-start | Preferred placement of the root filter menu relative to its trigger. The menu auto-flips vertically and clamps horizontally when needed. |
379
375
  | `priority?` | 'primary' \| 'secondary' | secondary | Default trigger's button priority. |
380
- | `renderTrigger?` | (state) => ReactNode | — | Replaces the default `Button` with a trigger returned from this function.
381
- Receives a `SelectFacetedTriggerState`. Return exactly one focusable element:
382
- the menu is positioned relative to that element and opens when it is clicked.
383
- Add `aria-haspopup="menu"` and `aria-expanded={state.open}` to the returned
384
- element, on a role that supports them (an Anta `Button` already carries
385
- `role="button"`; otherwise add `role="combobox"`). `className`, `style`, and
386
- other trigger props apply only to the default Button, so add styling and
387
- attributes to the returned element. |
388
- | `searchable?` | boolean | — | Adds a search field at the top of the root menu. It searches the options of
389
- every `single` and `multiple` facet in one list. For example, "alice" can
390
- appear under application-defined Assignee and Owner facets. `text` and
391
- `custom` facets remain available when the search is empty. Each facet uses
392
- its `filter` function when supplied, or the built-in substring match. |
376
+ | `renderTrigger?` | (state) => ReactNode | — | Replaces the default `Button` with a trigger returned from this function. Receives a `SelectFacetedTriggerState`. Return exactly one focusable element: the menu is positioned relative to that element and opens when it is clicked. Add `aria-haspopup="menu"` and `aria-expanded={state.open}` to the returned element, on a role that supports them (an Anta `Button` already carries `role="button"`; otherwise add `role="combobox"`). `className`, `style`, and other trigger props apply only to the default Button, so add styling and attributes to the returned element. |
377
+ | `searchable?` | boolean | — | Adds a search field at the top of the root menu. It searches the options of every `single` and `multiple` facet in one list. For example, "alice" can appear under application-defined Assignee and Owner facets. `text` and `custom` facets remain available when the search is empty. Each facet uses its `filter` function when supplied, or the built-in substring match. |
393
378
  | `searchPlaceholder?` | string | Filter… | Placeholder for the global search field. |
394
379
  | `size?` | 'small' \| 'medium' \| 'large' | medium | Default trigger's button size. |
395
- | `toneSelected?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | — | Tone applied to a selected option row in the facet flyouts (label, selected
396
- tint, and the check / checkbox indicator). A named tone or a custom CSS
397
- color, matching `Select`'s `toneSelected`. Defaults to a neutral selection. |
398
- | `value?` | SelectFacetedValue | — | Controlled value record, keyed by facet. When provided, update it through
399
- `onValueChange`. Leave it undefined for uncontrolled use. |
380
+ | `toneSelected?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | — | Tone applied to a selected option row in the facet flyouts (label, selected tint, and the check / checkbox indicator). A named tone or a custom CSS color, matching `Select`'s `toneSelected`. Defaults to a neutral selection. |
381
+ | `value?` | SelectFacetedValue | — | Controlled value record, keyed by facet. When provided, update it through `onValueChange`. Leave it undefined for uncontrolled use. |
400
382
 
401
383
  ### The `SelectFacet` type
402
384