@antadesign/anta 0.3.28 → 0.3.30

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 (142) hide show
  1. package/NOTICES.md +32 -1
  2. package/dist/anta_helpers.d.ts +21 -0
  3. package/dist/anta_helpers.js +24 -0
  4. package/dist/box-observation.d.ts +8 -0
  5. package/dist/box-observation.js +41 -0
  6. package/dist/box-types.d.ts +2 -0
  7. package/dist/bundle.css +1 -1
  8. package/dist/bundle.js +66 -29
  9. package/dist/components/Box.d.ts +28 -12
  10. package/dist/components/Box.js +18 -11
  11. package/dist/components/Breadcrumbs.css +1 -1
  12. package/dist/components/Breadcrumbs.d.ts +1 -1
  13. package/dist/components/Button.d.ts +3 -3
  14. package/dist/components/ButtonCopy.d.ts +1 -1
  15. package/dist/components/Calendar.js +3 -5
  16. package/dist/components/Checkbox.d.ts +2 -2
  17. package/dist/components/Input.d.ts +8 -2
  18. package/dist/components/Input.js +4 -0
  19. package/dist/components/InputAutocomplete.js +3 -12
  20. package/dist/components/InputDate.css +1 -1
  21. package/dist/components/InputDate.js +2 -0
  22. package/dist/components/Menu.d.ts +4 -4
  23. package/dist/components/Menu.js +1 -1
  24. package/dist/components/Panel.d.ts +17 -0
  25. package/dist/components/Panel.js +22 -0
  26. package/dist/components/RadioGroup.d.ts +2 -1
  27. package/dist/components/Select.d.ts +14 -14
  28. package/dist/components/Select.js +47 -35
  29. package/dist/components/SelectFaceted.d.ts +17 -6
  30. package/dist/components/SelectFaceted.js +61 -41
  31. package/dist/components/Steps.css +1 -1
  32. package/dist/components/Switch.d.ts +3 -3
  33. package/dist/components/TabPanel.d.ts +3 -8
  34. package/dist/components/TabPanel.js +2 -1
  35. package/dist/components/Tabs.d.ts +2 -2
  36. package/dist/components/Tabs.js +2 -1
  37. package/dist/components/Tooltip.d.ts +1 -1
  38. package/dist/components/Tooltip.js +1 -1
  39. package/dist/components/select-parts.css +1 -1
  40. package/dist/elements/a-avatar.css +1 -1
  41. package/dist/elements/a-banner.css +1 -1
  42. package/dist/elements/a-box.css +1 -1
  43. package/dist/elements/a-box.d.ts +6 -6
  44. package/dist/elements/a-box.js +76 -23
  45. package/dist/elements/a-button.css +1 -1
  46. package/dist/elements/a-button.d.ts +9 -1
  47. package/dist/elements/a-button.js +64 -1
  48. package/dist/elements/a-calendar.css +1 -1
  49. package/dist/elements/a-capture.css +1 -1
  50. package/dist/elements/a-card.css +1 -1
  51. package/dist/elements/a-checkbox.css +1 -1
  52. package/dist/elements/a-checkbox.d.ts +3 -0
  53. package/dist/elements/a-checkbox.js +17 -1
  54. package/dist/elements/a-copy.css +1 -1
  55. package/dist/elements/a-dialog.css +1 -1
  56. package/dist/elements/a-expander.css +1 -1
  57. package/dist/elements/a-icon.css +1 -1
  58. package/dist/elements/a-icon.shapes.css +1 -1
  59. package/dist/elements/a-icon.shapes.d.ts +1 -1
  60. package/dist/elements/a-input-time.css +1 -1
  61. package/dist/elements/a-input-time.d.ts +8 -0
  62. package/dist/elements/a-input-time.js +115 -8
  63. package/dist/elements/a-input.css +1 -1
  64. package/dist/elements/a-input.d.ts +27 -1
  65. package/dist/elements/a-input.js +176 -35
  66. package/dist/elements/a-loader.css +1 -1
  67. package/dist/elements/a-menu-group.css +1 -1
  68. package/dist/elements/a-menu-item.css +1 -1
  69. package/dist/elements/a-menu-item.js +1 -0
  70. package/dist/elements/a-menu-separator.css +1 -1
  71. package/dist/elements/a-menu.css +1 -1
  72. package/dist/elements/a-menu.d.ts +8 -13
  73. package/dist/elements/a-menu.js +146 -45
  74. package/dist/elements/a-panel.css +1 -0
  75. package/dist/elements/a-panel.d.ts +16 -0
  76. package/dist/elements/a-panel.js +153 -0
  77. package/dist/elements/a-progress.css +1 -1
  78. package/dist/elements/a-radio-group.css +1 -1
  79. package/dist/elements/a-radio.css +1 -1
  80. package/dist/elements/a-radio.d.ts +4 -0
  81. package/dist/elements/a-radio.js +18 -0
  82. package/dist/elements/a-slider.css +1 -1
  83. package/dist/elements/a-slider.d.ts +1 -0
  84. package/dist/elements/a-slider.js +3 -3
  85. package/dist/elements/a-switch.css +1 -1
  86. package/dist/elements/a-switch.d.ts +3 -0
  87. package/dist/elements/a-switch.js +17 -1
  88. package/dist/elements/a-tab.css +1 -1
  89. package/dist/elements/a-tabpanel.css +1 -1
  90. package/dist/elements/a-tabs.css +1 -1
  91. package/dist/elements/a-tag.css +1 -1
  92. package/dist/elements/a-text.css +1 -1
  93. package/dist/elements/a-title.css +1 -1
  94. package/dist/elements/a-toast.css +1 -1
  95. package/dist/elements/a-toaster.css +1 -1
  96. package/dist/elements/a-tooltip.css +1 -1
  97. package/dist/elements/a-tooltip.js +30 -7
  98. package/dist/elements/focus.d.ts +9 -0
  99. package/dist/elements/focus.js +41 -0
  100. package/dist/elements/index.d.ts +1 -0
  101. package/dist/elements/index.js +3 -0
  102. package/dist/elements/panel-focus.d.ts +10 -0
  103. package/dist/elements/panel-focus.js +86 -0
  104. package/dist/elements/shadow-aria.d.ts +10 -0
  105. package/dist/elements/shadow-aria.js +155 -0
  106. package/dist/general_types.d.ts +48 -19
  107. package/dist/generate-icons.mjs +3 -0
  108. package/dist/index.d.ts +4 -1
  109. package/dist/index.js +2 -0
  110. package/dist/jsx-runtime.d.ts +20 -1
  111. package/dist/jsx-runtime.js +12 -0
  112. package/dist/reset.css +1 -1
  113. package/dist/theme-antithesis.css +1 -1
  114. package/dist/theme-antune.css +1 -1
  115. package/dist/toaster.d.ts +1 -1
  116. package/dist/tokens.css +1 -1
  117. package/docs/accessibility.md +4 -2
  118. package/docs/changelog.md +89 -4
  119. package/docs/colors.md +6 -6
  120. package/docs/components/box.md +98 -34
  121. package/docs/components/breadcrumbs.md +33 -0
  122. package/docs/components/button.md +2 -2
  123. package/docs/components/capture.md +8 -8
  124. package/docs/components/checkbox.md +9 -4
  125. package/docs/components/input-autocomplete.md +6 -0
  126. package/docs/components/input-date.md +8 -2
  127. package/docs/components/input.md +99 -40
  128. package/docs/components/menu.md +3 -3
  129. package/docs/components/panel.md +189 -0
  130. package/docs/components/radio.md +4 -2
  131. package/docs/components/select-faceted.md +57 -2
  132. package/docs/components/select.md +63 -51
  133. package/docs/components/steps.md +4 -4
  134. package/docs/components/switch.md +12 -6
  135. package/docs/components/tabs.md +21 -8
  136. package/docs/components/tooltip.md +6 -4
  137. package/docs/index.md +2 -0
  138. package/docs/install-config.md +5 -4
  139. package/docs/normalization.md +3 -1
  140. package/docs/packages/plot.md +3771 -0
  141. package/docs/theming.md +1 -1
  142. package/package.json +16 -9
@@ -0,0 +1,189 @@
1
+ # Panel
2
+
3
+ `Panel` is a container that can maximize to the browser viewport without moving
4
+ or remounting its children. It fills its parent's width and height and uses
5
+ `overflow: scroll` by default.
6
+
7
+ Give the parent a defined height for Panel to fill vertically. With an
8
+ auto-height parent, percentage height follows normal CSS sizing and the Panel
9
+ can grow with its content. A parent's `min-height` alone does not generally
10
+ establish a height for `height: 100%` to fill.
11
+
12
+ > **Warning:** For now, Panel is a nonvisual utility container only. Supply your
13
+ > own appearance and controls. Built-in chrome, resizing, and folding are not
14
+ > available yet.
15
+
16
+ ## Usage
17
+
18
+ ### Declarative
19
+
20
+ Panel manages its own state. Use `data-custom-event="paneltoggle"` on
21
+ an Anta button to toggle maximization without a click handler.
22
+
23
+ ```tsx title="Declarative Panel"
24
+ import { Box, Button, Input, Panel } from '@antadesign/anta'
25
+
26
+ function Workspace() {
27
+ return (
28
+ <Panel style={{ background: 'var(--bg-3)' }}>
29
+ <Box
30
+ display="flex"
31
+ gap={16}
32
+ padding={16}
33
+ >
34
+ <Input aria-label="Workspace note" placeholder="Write a note" />
35
+ <Button data-custom-event="paneltoggle">
36
+ Maximize / restore
37
+ </Button>
38
+ </Box>
39
+ </Panel>
40
+ )
41
+ }
42
+ ```
43
+
44
+ ### Controlled
45
+
46
+ Pass `maximized` to manage state in your application. The button updates it
47
+ through `onClick`; `onStateChange` accepts requests from declarative controls
48
+ inside the panel.
49
+
50
+ ```tsx title="Controlled Panel"
51
+ import { useState } from 'react'
52
+ import { Box, Button, Input, Panel } from '@antadesign/anta'
53
+
54
+ function Workspace() {
55
+ const [maximized, setMaximized] = useState(false)
56
+
57
+ return (
58
+ <Panel
59
+ style={{ background: 'var(--bg-3)' }}
60
+ maximized={maximized}
61
+ onStateChange={(_event, { next }) => setMaximized(next)}
62
+ >
63
+ <Box
64
+ display="flex"
65
+ gap={16}
66
+ padding={16}
67
+ >
68
+ <Input aria-label="Workspace note" placeholder="Write a note" />
69
+ <Button onClick={() => setMaximized(value => !value)}>
70
+ {maximized ? 'Restore panel' : 'Maximize panel'}
71
+ </Button>
72
+ </Box>
73
+ </Panel>
74
+ )
75
+ }
76
+ ```
77
+
78
+ ## How does this work?
79
+
80
+ Maximizing applies `popover="manual"` to the internal content slot and calls
81
+ `showPopover()` to fill the viewport. Children keep their DOM ancestry, theme,
82
+ and event bubbling. A placeholder preserves the panel's normal layout size.
83
+ Restoring calls `hidePopover()` and returns the same content to normal layout.
84
+
85
+ ```html title="Internal content while maximized"
86
+ <!-- Managed by Panel inside its shadow root. -->
87
+ <slot part="content" popover="manual" tabindex="-1"></slot>
88
+ ```
89
+
90
+ While maximized, Panel traps focus inside its content. Tab and Shift+Tab cycle
91
+ through its controls. Restoring releases the trap and returns focus to the
92
+ previous external control, when available.
93
+
94
+ Provide a keyboard-accessible **Restore** control: Escape and outside clicks
95
+ do not restore the panel. Panel adds no dialog role, background inertness, or
96
+ scroll lock. Use [Dialog](./dialog.md) for modal interactions.
97
+
98
+ Panel fills the browser viewport, without invoking the Fullscreen API.
99
+ Browsers without Popover API support keep it in normal layout.
100
+
101
+ ## Component props
102
+
103
+ For uncontrolled state, omit `maximized` and optionally set `defaultMaximized`.
104
+ Call `event.preventDefault()` synchronously in `onStateChange` to cancel an
105
+ uncontrolled transition. In controlled mode, reject a request by leaving
106
+ `maximized` unchanged.
107
+
108
+ | Prop | Type | Default | Description |
109
+ |------|------|---------|-------------|
110
+ | `defaultMaximized?` | boolean | — | Initial viewport maximization for an uncontrolled panel, read once on connect. |
111
+ | `maximized?` | boolean | — | Fill the browser viewport and contain keyboard focus within the panel. When supplied, the application owns the state. |
112
+ | `onStateChange?` | (event, detail) => void | — | Requested maximization change. Update `maximized` to accept a controlled request, or prevent the event to veto an uncontrolled request. |
113
+
114
+ ## Web component
115
+
116
+ Import `@antadesign/anta/elements` to register the elements in this example.
117
+ The button toggles the nearest panel without JavaScript handlers.
118
+
119
+ ```html title="Declarative web component"
120
+ <a-panel style="background: var(--bg-3)">
121
+ <a-box display="flex" padding="16px" gap="16px" style="--box-padding: 16px; --box-gap: 16px">
122
+ <a-input aria-label="Workspace note" placeholder="Write a note"></a-input>
123
+ <a-button role="button" tabindex="0" data-custom-event="paneltoggle">
124
+ Maximize / restore
125
+ </a-button>
126
+ </a-box>
127
+ </a-panel>
128
+ ```
129
+
130
+ Use `state="normal"` or `state="maximized"` for controlled state, or `default-state="maximized"`
131
+ for an initially maximized, uncontrolled panel.
132
+
133
+ `requestMaximize()` and `requestRestore()` emit a cancelable, non-bubbling
134
+ `statechange` with `{ next, prev }` using those state strings.
135
+ Anta buttons can send `panelmaximizerequest`, `panelrestorerequest`, or
136
+ `paneltoggle` through `data-custom-event` to the nearest Panel.
137
+
138
+ ## Styling
139
+
140
+ Panel defaults to `display: block`, `width: 100%`, `height: 100%`,
141
+ `box-sizing: border-box`, and `overflow: scroll`. Padding and borders stay inside
142
+ its assigned size. It does not set `flex-grow`.
143
+
144
+ Override these through ordinary CSS. Use `height: auto` for content-driven
145
+ height or `overflow: auto` for scrollbars only when content overflows.
146
+ With `overflow: scroll`, scrollbar visibility and reserved space follow the
147
+ browser and operating system's scrollbar settings.
148
+
149
+ Set the background on Panel. Its maximized surface inherits that background,
150
+ so inner content can keep its natural height. Style light-DOM children, such as
151
+ [Box](./box.md) or [Capture](./capture.md), for content layout shared between both states.
152
+ Use `:state(maximized)` for viewport styling and `::part(content)` for the
153
+ maximized scrolling surface. In normal mode, the content slot uses `display: contents`.
154
+ The maximized surface also uses `overflow: scroll`; override
155
+ `::part(content)` to change its overflow separately from the normal Panel.
156
+
157
+ Enter a note, then select **Maximize panel** and **Restore panel**. The input
158
+ keeps its value. The `panel-demo` classes supply this example's appearance;
159
+ replace them with your own selectors.
160
+
161
+ ```html title="A persistent workspace panel"
162
+ <a-panel class="panel-demo">
163
+ <a-box class="panel-demo-content">
164
+ <a-box class="panel-demo-toolbar" display="flex">
165
+ <a-text weight="medium">Workspace preview</a-text>
166
+ <a-button role="button" tabindex="0" data-custom-event="paneltoggle">
167
+ <span class="panel-demo-maximize">Maximize panel</span>
168
+ <span class="panel-demo-restore">Restore panel</span>
169
+ </a-button>
170
+ </a-box>
171
+ <a-capture>
172
+ <a-input placeholder="Write a note before maximizing" aria-label="Workspace note"></a-input>
173
+ <a-text>Panel keeps this Box and Capture in their original DOM location.</a-text>
174
+ </a-capture>
175
+ </a-box>
176
+ </a-panel>
177
+ ```
178
+
179
+ ```css title="Preview layout and appearance"
180
+ .panel-demo { border: 1px solid var(--border-4); border-radius: 8px; background: var(--bg-3); }
181
+ .panel-demo-content { display: flex; flex-direction: column; gap: 20px; padding: 20px; }
182
+ .panel-demo-toolbar { justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
183
+ .panel-demo-content a-capture { display: grid; gap: 16px; }
184
+ .panel-demo-content a-input { width: 100%; }
185
+ .panel-demo-restore { display: none; }
186
+ .panel-demo:state(maximized) .panel-demo-content { padding: max(20px, env(safe-area-inset-top)) max(20px, env(safe-area-inset-right)) max(20px, env(safe-area-inset-bottom)) max(20px, env(safe-area-inset-left)); }
187
+ .panel-demo:state(maximized) .panel-demo-maximize { display: none; }
188
+ .panel-demo:state(maximized) .panel-demo-restore { display: inline; }
189
+ ```
@@ -165,7 +165,9 @@ Under the hood the split is deliberate. The `<a-radio-group>` web component
165
165
  **never mutates the DOM** — it coordinates entirely off-DOM: selection is set as a
166
166
  property on each `<a-radio>` (which reflects it into `:state(selected)` +
167
167
  `aria-checked` via its own `ElementInternals`), the form value goes through
168
- `ElementInternals`, and focus is tracked with `aria-activedescendant`. The
168
+ `ElementInternals`, and focus is tracked with `aria-activedescendant`. Each
169
+ option's light-DOM label and hint also become its accessible name and description
170
+ through direct element references, without generated IDs. The
169
171
  **roving `tabindex`** — which option Tab lands on — is the one piece rendered
170
172
  declaratively, by the `RadioGroup` wrapper, from the current value. That's why
171
173
  selection is owned by the element but the wrapper holds the value: it needs it to
@@ -270,7 +272,7 @@ element — `Input`, and the composed `Select` — skip it and expose only
270
272
  | `size?` | 'small' \| 'medium' \| 'large' | 'medium' | Size applied to every option (an option's own `size` wins). |
271
273
  | `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. |
272
274
  | `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. In the default `all` scope it colors the selected-ring fill + dot and the unselected ring border. Named tones track light/dark mode. Set `toneScope="selected"` 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. |
273
- | `toneScope?` | ToneScope | 'all' | Apply `tone` to every state, or only to the selected option so unselected rings stay neutral. An option's own `toneScope` wins. |
275
+ | `toneScope?` | 'all' \| 'selected' | 'all' | Apply `tone` to every state, or only to the selected option so unselected rings stay neutral. An option's own `toneScope` wins. |
274
276
  | `value?` | string | — | Controlled selected value. The group follows this prop and reports a requested change through `onStateChange`. Leave it undefined for uncontrolled use. |
275
277
 
276
278
  ## Web Component
@@ -14,6 +14,12 @@ application-defined facet keys, not Anta terms. Each facet has one of four
14
14
  Use `value` with `onValueChange` to control it, or use `defaultValue` for an
15
15
  uncontrolled filter.
16
16
 
17
+ Without `searchable`, the trigger opens a menu of facets. With `searchable`, it
18
+ opens a dialog containing the search field and that menu. A filtered option
19
+ facet also opens a dialog containing its search field and options menu. Text and
20
+ custom facets use dialogs for their editable content. An unfiltered option facet
21
+ keeps the menu popup pattern.
22
+
17
23
  Pass `placement` to control where the root facet menu opens relative to its
18
24
  trigger. It uses the same values as `Menu` and still flips or clamps when space
19
25
  is limited. `offset` sets the gap in pixels between the trigger and the menu:
@@ -22,11 +28,32 @@ is limited. `offset` sets the gap in pixels between the trigger and the menu:
22
28
  <SelectFaceted placement="top-end" offset={8} facets={FACETS} />
23
29
  ```
24
30
 
31
+ Open **Metadata** in the first demo to choose key–value tags such as
32
+ **env production** or **region us-east**. Its `renderOption` callback reads
33
+ custom option fields and renders a `Tag` with `label` and `value`. Search
34
+ for **production** to see the same tag in global results.
35
+
25
36
  ```tsx
26
37
  const FACETS = [
27
38
  { key: 'assignee', label: 'Assignee', kind: 'multiple', filter: true, options: people },
28
39
  { key: 'owner', label: 'Owner', kind: 'single', filter: true, options: people },
29
40
  { key: 'status', label: 'Status', kind: 'single', options: [{ value: 'open', label: 'Open' }, /* … */] },
41
+ {
42
+ key: 'metadata',
43
+ label: 'Metadata',
44
+ kind: 'multiple',
45
+ icon: 'tag',
46
+ filter: true,
47
+ options: [
48
+ { value: 'env:production', label: 'env: production', dataKey: 'env', dataValue: 'production' },
49
+ { value: 'env:staging', label: 'env: staging', dataKey: 'env', dataValue: 'staging' },
50
+ { value: 'region:us-east', label: 'region: us-east', dataKey: 'region', dataValue: 'us-east' },
51
+ { value: 'team:platform', label: 'team: platform', dataKey: 'team', dataValue: 'platform' },
52
+ ],
53
+ renderOption: (option) => (
54
+ <Tag label={String(option.dataKey)} value={String(option.dataValue)} />
55
+ ),
56
+ },
30
57
  { key: 'title', label: 'Title contains', kind: 'text' },
31
58
  { key: 'duration', label: 'Min duration', kind: 'custom', summary: (v) => `≥ ${v.min}s`, render: /* … */ },
32
59
  ]
@@ -68,6 +95,7 @@ function Demo() {
68
95
  selection={facet.kind}
69
96
  indicator={facet.kind === 'single' ? 'check' : undefined}
70
97
  options={facet.options}
98
+ renderOption={facet.renderOption}
71
99
  value={value[facet.key]}
72
100
  onValueChange={(v) => setFacet(facet.key, v)}
73
101
  leading={`${facet.label}:`}
@@ -120,6 +148,27 @@ The trigger opens the facet menu. The example renders one editable
120
148
  [Select](./select.md) below it for each active facet. Each Select receives the
121
149
  facet label through `leading`, which displays it as a prefix.
122
150
 
151
+ ### Custom option content
152
+
153
+ Pass `renderOption(option, state)` on a `single` or `multiple` facet to replace
154
+ each option's label, hint, and icon layout. It runs in both the facet flyout and
155
+ global search results. `state` contains `value`, `selected`, and `disabled`.
156
+ Selection handling, indicators, and option styling remain on the row.
157
+ Return `null` to use the default layout for an option.
158
+
159
+ The first demo's **Metadata** facet stores the key and value in custom option
160
+ fields. Pass them to Tag's `label` and `value` props:
161
+
162
+ ```tsx
163
+ renderOption: (option) => (
164
+ <Tag label={String(option.dataKey)} value={String(option.dataValue)} />
165
+ )
166
+ ```
167
+
168
+ Filtering still uses `value`, `label`, and `hint`, or the facet's custom `filter`
169
+ function. Keep searchable text in those fields. Single-facet summaries still
170
+ use the option label, and multiple-facet summaries show the selected count.
171
+
123
172
  ### Value and changes
124
173
 
125
174
  The value is a record keyed by facet key: `{ [facetKey]: value }`. Its shape
@@ -373,12 +422,12 @@ const recency: SelectFacetCustom<Recency> = {
373
422
  | `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
423
  | `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. |
375
424
  | `priority?` | 'primary' \| 'secondary' | secondary | Default trigger's button priority. |
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. |
425
+ | `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={searchable ? 'dialog' : '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
426
  | `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. |
378
427
  | `searchPlaceholder?` | string | Filter… | Placeholder for the global search field. |
379
428
  | `size?` | 'small' \| 'medium' \| 'large' | medium | Default trigger's button size. |
380
429
  | `tone?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | — | Default option-row tone in facet flyouts. An option's own `tone` wins. |
381
- | `toneScope?` | ToneScope | 'all' | Apply the default row tone in every state, or only to selected rows. An option's own `toneScope` wins. |
430
+ | `toneScope?` | 'all' \| 'selected' | 'all' | Apply the default row tone in every state, or only to selected rows. An option's own `toneScope` wins. |
382
431
  | `value?` | SelectFacetedValue | — | Controlled value record, keyed by facet. When provided, update it through `onValueChange`. Leave it undefined for uncontrolled use. |
383
432
 
384
433
  ### The `SelectFacet` type
@@ -391,6 +440,12 @@ properties as [Select](./select.md). They use `kind` in place of Select's
391
440
  `selection`. `text` and `custom` do not use Select options. A `single` facet
392
441
  returns one option value; a `multiple` facet returns an array of option values.
393
442
 
443
+ When `selectAll` is enabled, Alt-clicking an option in a `multiple` facet clears
444
+ the other values and selects that option. Use Option-click on macOS. After a
445
+ 700ms hover, a tooltip beside the row describes the shortcut. Moving to another
446
+ row restarts the delay. An option's `tooltip` replaces that hint; set it
447
+ to `''` to hide the hint.
448
+
394
449
  | Field | Type | Description |
395
450
  |---|---|---|
396
451
  | `key` | `string` | The key for this facet's value in the record. `attrs.facet` returns it. It must be unique in `facets`. |
@@ -4,7 +4,7 @@
4
4
  field shows the selected value. Click the field to open the menu. The menu
5
5
  closes when a user clicks outside it or presses Esc.
6
6
 
7
- Select uses an [Input](./input.md) for its field and a [Menu](./menu.md) for its
7
+ Select uses an [Anta Button](./button.md) for its field and a [Menu](./menu.md) for its
8
8
  dropdown. Pass `options` as an array of strings or objects. Use `value` with
9
9
  **`onValueChange`** to control the selection, or use `defaultValue` for an
10
10
  uncontrolled Select.
@@ -70,7 +70,8 @@ its box goes mixed when only some are on. Pass **`selectAll={false}`** to drop i
70
70
 
71
71
  In a multi-select, **`Alt`+Click** a row to clear every other selection and
72
72
  select that row. On macOS, use Option instead of Alt.
73
- The standard tooltip describes this shortcut when the pointer is over a row.
73
+ After hovering for 700ms, the standard tooltip describes this shortcut beside
74
+ the row. Moving to another row restarts the delay.
74
75
  Set `SelectOption.tooltip` to replace it, or to `''` to hide it. The shortcut
75
76
  is available only when `selectAll` is enabled, so `selectAll={false}` removes it.
76
77
 
@@ -105,12 +106,14 @@ option's **value, label, or hint**. It's **whitespace-flexible** (a typed space
105
106
  matches any run of whitespace) and **bolds the matched substring** in the results.
106
107
  Pass a **function** `(option, query) => boolean` for custom matching (no highlight).
107
108
 
108
- With `filter`, Select behaves as a **combobox**. Opening it focuses the search
109
- field. ↑ and ↓ move through matching options while focus
110
- stays in the field. Enter selects the active option, and Esc
111
- closes the menu. The search field stays visible while the results scroll. A
112
- "No matches" row appears when nothing matches, and the query resets when the
113
- menu closes. In `multiple` mode, `selectAll` affects only the visible matches.
109
+ With `filter`, the trigger opens a dialog containing the search field and an
110
+ options menu. This keeps the textbox outside the menu in the accessibility tree.
111
+ Opening the dialog focuses the search field. ↑ and ↓ move
112
+ through matching options while focus stays in that field. Enter
113
+ selects the active option, and Esc closes the dialog. The search field
114
+ stays visible while the results scroll. A "No matches" row appears when nothing
115
+ matches, and the query resets when the dialog closes. In `multiple` mode,
116
+ `selectAll` affects only the visible matches.
114
117
 
115
118
  ## Value and changes
116
119
 
@@ -229,7 +232,7 @@ section marks the leaf everywhere it occurs (selection is value-keyed).
229
232
  <Select status="info" statusIcon="sparkles" hint="Custom glyph" options={OPTIONS} defaultValue="stream" />
230
233
  ```
231
234
 
232
- The trigger is an [Input](./input.md), so it inherits the field props.
235
+ The trigger is an [Anta Button](./button.md) styled as a field.
233
236
  **`size`** (`small` · `medium` · `large`) sets the height and type scale.
234
237
  **`status`** paints a validation tone on the border, `hint`, and chevron, and
235
238
  prefixes a glyph (`info` / `success` / `warning` / `critical`); **`statusIcon`**
@@ -245,7 +248,7 @@ the glyph and, for `critical`, validity. See the
245
248
  ## Customization
246
249
 
247
250
  Select has no `<a-select>` element. The React and Preact wrapper coordinates an
248
- Input and a Menu. You can use the wrapper, render custom option content with
251
+ Anta Button and a Menu. You can use the wrapper, render custom option content with
249
252
  `renderOption`, replace its selection mark with `renderIndicator`, or build the
250
253
  same interaction from the raw elements.
251
254
 
@@ -259,22 +262,21 @@ uses the same values as `Menu` and still flips or clamps when space is limited.
259
262
 
260
263
  ### Build it from elements
261
264
 
262
- The wrapper renders a read-only `<Input>` and a `<Menu>`. It updates the field
265
+ The wrapper renders a `<Button>` immediately followed by a `<Menu>`, with a label
266
+ and hint inside an `<a-select-field>` layout wrapper. It updates the field
263
267
  and each option's `selected` state from the value, and reports a pick through
264
268
  `onValueChange`. Menu handles positioning, dismissal, and keyboard navigation.
265
269
 
266
270
  The following example shows the equivalent markup without React or Preact. Place
267
- the Menu directly after the Input so it can use that Input as its trigger.
271
+ the Menu directly after the Button so it can use that Button as its trigger.
268
272
 
269
273
  ```html
270
274
  <!-- `.select` scopes this example's CSS. Use an application selector instead. -->
271
275
  <div class="select" style="width: 240px">
272
- <!-- Read-only field trigger; `dim-actions` dims the chevron at rest. The
273
- trailing slot supplies the inline inset (--input-trailing-inset); the
274
- transition on the chevron matches <Select>. -->
275
- <a-input readonly dim-actions value="stream" aria-haspopup="menu">
276
- <a-icon slot="trailing" shape="chevron-down" style="transition: transform 150ms ease"></a-icon>
277
- </a-input>
276
+ <a-button role="button" tabindex="0" aria-label="Field" aria-haspopup="menu" aria-expanded="false">
277
+ <a-button-label>stream</a-button-label>
278
+ <a-icon shape="chevron-down" aria-hidden="true" style="transition: transform 150ms ease"></a-icon>
279
+ </a-button>
278
280
  <a-menu role="menu">
279
281
  <a-menu-item role="menuitemradio" tabindex="0" aria-checked="false" value="output_text"><a-menu-item-label>output_text</a-menu-item-label></a-menu-item>
280
282
  <a-menu-item role="menuitemradio" tabindex="0" aria-checked="true" value="stream" selected><a-menu-item-label>stream</a-menu-item-label></a-menu-item>
@@ -284,12 +286,14 @@ the Menu directly after the Input so it can use that Input as its trigger.
284
286
  <script type="module">
285
287
  import '@antadesign/anta/elements'
286
288
  const root = document.querySelector('.select')
287
- const field = root.querySelector('a-input')
289
+ const field = root.querySelector('a-button')
288
290
  const menu = root.querySelector('a-menu')
289
291
  const chevron = field.querySelector('a-icon')
290
292
  // Flip the chevron with the menu's open state (as <Select> does).
291
293
  menu.addEventListener('statechange', (e) => {
292
- chevron.style.transform = e.detail.next === 'open' ? 'rotate(180deg)' : ''
294
+ const open = e.detail.next === 'open'
295
+ field.setAttribute('aria-expanded', String(open))
296
+ chevron.style.transform = open ? 'rotate(180deg)' : ''
293
297
  })
294
298
  // On pick: mark the row selected + reflect its label into the field.
295
299
  menu.addEventListener('click', (e) => {
@@ -300,12 +304,16 @@ the Menu directly after the Input so it can use that Input as its trigger.
300
304
  el.toggleAttribute('selected', selected)
301
305
  el.setAttribute('aria-checked', String(selected))
302
306
  })
303
- field.value = item.querySelector('a-menu-item-label').textContent
307
+ field.querySelector('a-button-label').textContent = item.querySelector('a-menu-item-label').textContent
304
308
  })
305
309
  </script>
306
310
  ```
307
311
 
308
312
  The React and Preact `Select` wrapper performs those updates for you.
313
+ Because the Menu directly follows the Anta Button, it associates the focused
314
+ field with the popup through a direct ARIA element reference. No popup ID is
315
+ required. If a custom composition supplies its own `aria-controls`, Anta keeps
316
+ that authored relationship.
309
317
 
310
318
  ### Custom option rendering
311
319
 
@@ -461,7 +469,7 @@ empty selection still shows the `placeholder` — and takes the resolved `select
461
469
  options (`selected.length` is the count). Return `undefined` for any case you'd rather
462
470
  leave to the default, as above for the single-label case.
463
471
 
464
- Return a **string**: it flows into the same read-only field the default uses, so a
472
+ Return a **string**: it flows into the same Button label the default uses, so a
465
473
  long summary **ellipsizes at the field's width** the way a long value does
466
474
  (`Engineering, Design, … `). For rich content — chips, several nodes — reach for
467
475
  `renderTrigger` below, which replaces the whole field.
@@ -523,7 +531,8 @@ focusable element, such as the Anta `Button` in this example. Select positions
523
531
  the menu relative to that element and opens it when it is clicked. A fragment,
524
532
  multiple sibling elements, or a non-focusable wrapper prevents Select from finding
525
533
  the trigger. It logs a console warning when that happens. Add
526
- `aria-haspopup="menu"` and `aria-expanded={state.open}` to the element.
534
+ `aria-haspopup={filter ? 'dialog' : 'menu'}` and
535
+ `aria-expanded={state.open}` to the element.
527
536
 
528
537
  `state` includes `open`, `value`, `selected`, `disabled`, and `icon`. `selected`
529
538
  is the resolved option list, so `selected.length` is the multi-select count. The
@@ -633,11 +642,11 @@ scannable list instead of hiding matches behind flyouts.
633
642
  | `defaultValue?` | V \| V[] | — | Initial selected option value for uncontrolled use. |
634
643
  | `disabled?` | boolean | — | Disable the whole select. |
635
644
  | `filter?` | boolean \| (option, query) => boolean | — | Add a search field at the top of the menu that filters the options as you type. `true` uses the built-in matcher — a case-insensitive substring of the option's **value / label / hint**. Pass a **function** `(option, query) => boolean` for custom matching (called per option; return `true` to keep it). |
636
- | `hint?` | string | — | Helper text under the field (Input's `hint`). |
637
- | `icon?` | IconShape | — | Leading icon shown at the left of the field (the default trigger's `Input` `leading` slot). With a custom `renderTrigger`, it's passed through as `state.icon` instead — the consumer places it. |
645
+ | `hint?` | string | — | Helper text under the field. |
646
+ | `icon?` | IconShape | — | Leading icon shown at the left of the field. With a custom `renderTrigger`, it's passed through as `state.icon` instead — the consumer places it. |
638
647
  | `indicator?` | 'none' \| 'check' \| 'radio' | none | The per-row mark for **single**-select: `'none'` (a tint-only highlight), `'check'` (a trailing checkmark on the selected row, keeping the tint — the canonical Select look), or `'radio'` (a leading radio on every row). Multi-select always uses checkboxes. |
639
- | `label?` | string | — | Field label, above the trigger (Input's `label`). |
640
- | `leading?` | ReactNode | — | Content for the default trigger's `leading` slot, such as a key prefix before the value. It replaces the icon derived from `icon`. Include an `<Icon>` in this content when both are needed. Ignored by `renderTrigger`. |
648
+ | `label?` | string | — | Field label, above the trigger. |
649
+ | `leading?` | ReactNode | — | Content before the default trigger's value, such as a key prefix before the value. It replaces the icon derived from `icon`. Include an `<Icon>` in this content when both are needed. Ignored by `renderTrigger`. |
641
650
  | `offset?` | number | 4 | Gap in pixels between the trigger and the options menu. |
642
651
  | `onValueChange?` | (value, attrs) => void | — | Fires after the selection changes, with the new value and a `{ value, option }` snapshot. Select has no discrete element state, so there is no cancelable `onStateChange` (see the Input event-model note). |
643
652
  | `placeholder?` | string | — | Text shown when nothing is selected. |
@@ -645,19 +654,19 @@ scannable list instead of hiding matches behind flyouts.
645
654
  | `renderEmpty?` | (state) => ReactNode | — | Render content in the menu body when the (filtered) option list is empty — a "no results" message, a loading indicator (gated on your own external loading state), or a "create from the query" row. Receives an `EmptyState` (`query`, trimmed). There is no built-in empty message: when omitted, an empty list renders nothing. Whatever you return goes where the option rows would — a plain node is inert; return a `MenuItem` (e.g. a "Create" row) to make it focusable and selectable. |
646
655
  | `renderIndicator?` | (state) => ReactNode | — | Replace each row's selection **mark** with your own node, drawn at the leading edge. The row stays the control (`role` + `aria-checked` from `indicator` / `selection`); only the drawn mark changes, so pair it with an `indicator` (`'check'` / `'radio'`) or `selection="multiple"` for the semantics. Composes with `renderOption`. |
647
656
  | `renderOption?` | (option, state) => ReactNode | — | Replaces the built-in `label`, `hint`, and `icon` layout for each option row. Select still supplies the row container, click handling, ARIA attributes, and selection indicator. Read extra option fields through `SelectOption`'s index signature. `state` contains `value`, `selected`, and `disabled`. Filtering still matches the option's `value`, `label`, and `hint`, but Select cannot highlight matches within the returned content. |
648
- | `renderSummary?` | (selected) => string \| undefined | — | `multiple` only: build the trigger's selection summary text yourself, replacing the built-in "`All` / one label / `N selected`" logic. Receives the resolved selected options (`selected.length` is the count) and runs only while something is selected — an empty selection still shows the `placeholder`. Return a **string**: it flows into the default trigger's read-only field, so a long summary ellipsizes at the field's width just like a long value (`Engineering, Design, … `). Return `undefined` to fall back to the default for that case (e.g. customize only the count, keeping the single-label case built-in). For rich content (chips, multiple nodes) use `renderTrigger`, which replaces the whole field. |
649
- | `renderTrigger?` | (state) => ReactNode | — | Replaces the default field with a trigger returned from this function. Receives `open`, `value`, `selected`, `disabled`, and `icon`. Return exactly one focusable element, such as an Anta `Button`. The menu is positioned relative to that element and opens when it is clicked. Do not return a fragment, multiple siblings, or a non-focusable wrapper. Add `aria-haspopup="menu"` and `aria-expanded={state.open}` to the element, on a role that supports them (an Anta `Button` already carries `role="button"`; otherwise add `role="combobox"`). Field props (`label`, `hint`, `size`, `status`, `placeholder`, and `round`) and `className` / `style` apply only to the default field. Add styling and attributes to the returned element instead. |
657
+ | `renderSummary?` | (selected) => string \| undefined | — | `multiple` only: build the trigger's selection summary text yourself, replacing the built-in "`All` / one label / `N selected`" logic. Receives the resolved selected options (`selected.length` is the count) and runs only while something is selected — an empty selection still shows the `placeholder`. Return a **string**: it flows into the default trigger's Button label, so a long summary ellipsizes at the field's width just like a long value (`Engineering, Design, … `). Return `undefined` to fall back to the default for that case (e.g. customize only the count, keeping the single-label case built-in). For rich content (chips, multiple nodes) use `renderTrigger`, which replaces the whole field. |
658
+ | `renderTrigger?` | (state) => ReactNode | — | Replaces the default field with a trigger returned from this function. Receives `open`, `value`, `selected`, `disabled`, and `icon`. Return exactly one focusable element, such as an Anta `Button`. The menu is positioned relative to that element and opens when it is clicked. Do not return a fragment, multiple siblings, or a non-focusable wrapper. Add `aria-haspopup={filter ? 'dialog' : 'menu'}` and `aria-expanded={state.open}` to the returned button. An Anta `Button` already carries the correct role. Field props (`label`, `hint`, `size`, `status`, `placeholder`, and `round`) and `className` / `style` apply only to the default field. Add styling and attributes to the returned element instead. |
650
659
  | `round?` | boolean \| number \| string | — | Round the field corners — `true` for fully round, or a number / CSS length. |
651
660
  | `selectAll?` | boolean | true | `multiple` only: shows a "Select all" row that toggles every enabled option, or only the visible options when a filter query is active. Its checkbox is mixed when some options are selected. It is on by default. Set it to `false` to remove the row and the Alt/Option-click shortcut that selects only one row. |
652
661
  | `selectAllLabel?` | string | Select all | Label for the `selectAll` row. |
653
662
  | `selection?` | 'single' \| 'multiple' | single | Selection mode. `'single'` (the default) keeps `value` a single value and closes the menu on pick. Switch to `'multiple'` for checkboxes + an array value. |
654
663
  | `size?` | 'small' \| 'medium' \| 'large' | medium | Field size. |
655
- | `status?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' | neutral | Validation/feedback tone for the field (Input's `status`). |
656
- | `statusIcon?` | (string & {}) \| false \| IconShape | — | Glyph shown before the `hint` when `status` is set (Input's `statusIcon`). Each status has a default; pass a shape to override, or `false` to drop it. |
664
+ | `status?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' | neutral | Validation/feedback tone for the field. |
665
+ | `statusIcon?` | (string & {}) \| false \| IconShape | — | Glyph shown before the `hint` when `status` is set. Each status has a default; pass a shape to override, or `false` to drop it. |
657
666
  | `tone?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | — | Default option-row tone. An option's own `tone` wins. A named tone or a custom CSS color. Most visible with tint-based marks (`indicator` `'none'` / `'check'`); with `'radio'` / `'checkbox'` it also tones the indicator. |
658
- | `toneScope?` | ToneScope | 'all' | Apply the default row tone in every state, or only to selected rows. An option's own `toneScope` wins. |
667
+ | `toneScope?` | 'all' \| 'selected' | 'all' | Apply the default row tone in every state, or only to selected rows. An option's own `toneScope` wins. |
659
668
  | `value?` | V \| V[] | — | Controlled value: the selected option's `value`. Update it through `onValueChange`. Leave it undefined for uncontrolled use. |
660
- | `verbose?` | boolean | — | `multiple` only: spell the picks out in the count summary — `3 selected: A, B, C` (labels comma-joined) in place of the bare `3 selected`. Applies to the multi-count case only: `All` stays `All`, a single pick stays its own label, and an empty selection stays the `placeholder`. The list flows into the read-only field, so it ellipsizes at the field's width when long (`3 selected: Engineering, Des… `). `renderSummary` overrides this. |
669
+ | `verbose?` | boolean | — | `multiple` only: spell the picks out in the count summary — `3 selected: A, B, C` (labels comma-joined) in place of the bare `3 selected`. Applies to the multi-count case only: `All` stays `All`, a single pick stays its own label, and an empty selection stays the `placeholder`. The list flows into the Button label, so it ellipsizes at the field's width when long (`3 selected: Engineering, Des… `). `renderSummary` overrides this. |
661
670
 
662
671
  ### The `SelectItem` type
663
672
 
@@ -757,16 +766,16 @@ inline `SelectGroup`, and a flyout `SelectSubmenu` (whose items are themselves
757
766
 
758
767
  Use the web component directly when you are not using React or Preact and a native control does not fit.
759
768
 
760
- `Select` has no host element. Compose a read-only `<a-input>` trigger with an
769
+ `Select` uses a layout wrapper. Compose an `<a-button>` trigger with an
761
770
  `<a-menu>` of options. The menu owns opening and dismissal; the short controller
762
771
  reflects a picked option into the field and selected row.
763
772
 
764
773
  ```html
765
774
  <div data-anta-composition="select" style="display: grid; gap: 4px; width: 280px">
766
- <a-input readonly value="stream">
767
- <span slot="label">Output</span>
768
- <span slot="trailing"><a-icon shape="chevron-down" aria-hidden="true"></a-icon></span>
769
- </a-input>
775
+ <a-button role="button" tabindex="0" aria-label="Output" aria-haspopup="menu" aria-expanded="false">
776
+ <a-button-label>stream</a-button-label>
777
+ <a-icon shape="chevron-down" aria-hidden="true"></a-icon>
778
+ </a-button>
770
779
  <a-menu role="menu">
771
780
  <a-menu-item role="menuitem" tabindex="0" value="output_text"><a-menu-item-label>output_text</a-menu-item-label></a-menu-item>
772
781
  <a-menu-item role="menuitem" tabindex="0" value="stream" selected><a-menu-item-label>stream</a-menu-item-label></a-menu-item>
@@ -776,8 +785,11 @@ reflects a picked option into the field and selected row.
776
785
  import '@antadesign/anta/elements'
777
786
 
778
787
  const root = document.querySelector('[data-anta-composition="select"]')
779
- const field = root.querySelector('a-input')
788
+ const field = root.querySelector('a-button')
780
789
  const menu = root.querySelector('a-menu')
790
+ menu.addEventListener('statechange', (event) => {
791
+ field.setAttribute('aria-expanded', String(event.detail.next === 'open'))
792
+ })
781
793
 
782
794
  menu.addEventListener('click', (event) => {
783
795
  const item = event.target instanceof Element ? event.target.closest('a-menu-item') : null
@@ -786,7 +798,7 @@ reflects a picked option into the field and selected row.
786
798
  const selected = option === item
787
799
  option.toggleAttribute('selected', selected)
788
800
  })
789
- field.value = item.querySelector('a-menu-item-label').textContent.trim()
801
+ field.querySelector('a-button-label').textContent = item.querySelector('a-menu-item-label').textContent.trim()
790
802
  })
791
803
  </script>
792
804
  ```
@@ -824,17 +836,18 @@ corners.
824
836
 
825
837
  ## Styling
826
838
 
827
- Select is an [Input](./input.md) plus a [Menu](./menu.md), so it
828
- inherits both surfaces' hooks. Reach for props first, then plain CSS or `::part` for
839
+ Select composes an [Anta Button](./button.md) and a [Menu](./menu.md). Reach for props first, then plain CSS or `::part` for
829
840
  the rest. Don't override an element's internal `--*` output tokens.
830
841
 
831
842
  **Selection color** routes through `tone`: set it per option or on the Select as a
832
843
  default, then use `toneScope="selected"` for chosen rows only. It takes a named tone or any CSS color (a custom color keeps
833
844
  its hue, with lightness pinned to the brand text).
834
845
 
835
- **The field** is styled through Input's props (`size`, `status`, `round`) and its
836
- `::part`s; set its width with `style` / `className` (forwarded to the field). **The
837
- popover** takes Menu's `::part(menu)`.
846
+ **The field** takes `size`, `status`, and `round`. Set its width with `style` /
847
+ `className`, forwarded to the `<a-select-field>` wrapper. Style the trigger through
848
+ `a-select-field > a-button` and its `a-button-label`. The trigger uses
849
+ `:focus-visible` for its focus ring and ellipsizes long values. **The popover**
850
+ takes Menu's `::part(menu)`.
838
851
 
839
852
  ```tsx
840
853
  // Tone the selection; give the trigger a width.
@@ -844,16 +857,15 @@ popover** takes Menu's `::part(menu)`.
844
857
  **Borderless trigger.** The field's border is an inset `box-shadow`, so you can drop
845
858
  it *at rest* and let the standard hover / focus shadow return on its own; no need to
846
859
  re-declare it. Pair that with hiding the chevron for a field that reads as plain text
847
- until you reach for it. Hide the chevron by targeting the trailing `a-icon` (its slot
848
- wrapper is inline-`display:contents`, so hit the icon itself).
860
+ until you reach for it. Hide the chevron by targeting `a-select-chevron`.
849
861
 
850
862
  ```css
851
863
  /* Drop the resting border-shadow; the standard hover / focus shadow still shows. */
852
- .ghost-select a-input:not(:hover):not(:focus-within)::part(field) {
864
+ .ghost-select a-select-field > a-button:not(:hover):not(:focus-visible) {
853
865
  box-shadow: none;
854
866
  }
855
867
  /* Center the chosen label in the field. */
856
- .ghost-select a-input::part(input) { text-align: center; }
857
- /* The chevron's slot wrapper is inline display:contents; hide the icon itself. */
858
- .ghost-select a-input [slot="trailing"] a-icon { display: none; }
868
+ .ghost-select a-select-field > a-button > a-button-label { text-align: center; }
869
+ /* Hide the chevron. */
870
+ .ghost-select a-select-chevron { display: none; }
859
871
  ```