@antadesign/anta 0.3.27 → 0.3.29
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.
- package/NOTICES.md +32 -1
- package/dist/anta_helpers.d.ts +25 -0
- package/dist/anta_helpers.js +28 -0
- package/dist/box-observation.d.ts +8 -0
- package/dist/box-observation.js +41 -0
- package/dist/box-types.d.ts +2 -0
- package/dist/bundle.css +1 -1
- package/dist/bundle.js +77 -46
- package/dist/components/Banner.js +2 -2
- package/dist/components/Box.d.ts +28 -12
- package/dist/components/Box.js +18 -11
- package/dist/components/Breadcrumbs.css +1 -1
- package/dist/components/Button.js +2 -2
- package/dist/components/Calendar.js +3 -5
- package/dist/components/Card.js +2 -2
- package/dist/components/Checkbox.d.ts +12 -13
- package/dist/components/Checkbox.js +5 -13
- package/dist/components/Expander.js +2 -2
- package/dist/components/Input.d.ts +8 -2
- package/dist/components/Input.js +6 -2
- package/dist/components/InputAutocomplete.js +3 -12
- package/dist/components/InputDate.css +1 -1
- package/dist/components/InputDate.js +2 -0
- package/dist/components/InputTime.js +2 -2
- package/dist/components/Loader.js +2 -1
- package/dist/components/Menu.d.ts +4 -4
- package/dist/components/Menu.js +1 -1
- package/dist/components/MenuItem.d.ts +7 -8
- package/dist/components/MenuItem.js +4 -4
- package/dist/components/Panel.d.ts +17 -0
- package/dist/components/Panel.js +22 -0
- package/dist/components/Progress.js +2 -2
- package/dist/components/RadioGroup.d.ts +14 -14
- package/dist/components/RadioGroup.js +7 -23
- package/dist/components/Select.d.ts +26 -21
- package/dist/components/Select.js +51 -38
- package/dist/components/SelectFaceted.d.ts +24 -11
- package/dist/components/SelectFaceted.js +65 -44
- package/dist/components/Slider.js +3 -3
- package/dist/components/Steps.css +1 -1
- package/dist/components/Switch.d.ts +10 -11
- package/dist/components/Switch.js +3 -11
- package/dist/components/TabPanel.d.ts +3 -8
- package/dist/components/TabPanel.js +2 -1
- package/dist/components/Tabs.d.ts +2 -2
- package/dist/components/Tabs.js +4 -3
- package/dist/components/Tag.d.ts +7 -9
- package/dist/components/Tag.js +2 -2
- package/dist/components/Text.js +2 -2
- package/dist/components/Title.js +2 -2
- package/dist/components/Tooltip.d.ts +1 -1
- package/dist/components/Tooltip.js +1 -1
- package/dist/components/select-parts.css +1 -1
- package/dist/elements/a-avatar.css +1 -1
- package/dist/elements/a-banner.css +1 -1
- package/dist/elements/a-box.css +1 -1
- package/dist/elements/a-box.d.ts +6 -6
- package/dist/elements/a-box.js +74 -23
- package/dist/elements/a-button.css +1 -1
- package/dist/elements/a-button.d.ts +16 -10
- package/dist/elements/a-button.js +64 -1
- package/dist/elements/a-calendar.css +1 -1
- package/dist/elements/a-capture.css +1 -1
- package/dist/elements/a-card.css +1 -1
- package/dist/elements/a-checkbox.css +1 -1
- package/dist/elements/a-checkbox.d.ts +3 -0
- package/dist/elements/a-checkbox.js +17 -1
- package/dist/elements/a-copy.css +1 -1
- package/dist/elements/a-dialog.css +1 -1
- package/dist/elements/a-expander.css +1 -1
- package/dist/elements/a-expander.js +1 -0
- package/dist/elements/a-icon.css +1 -1
- package/dist/elements/a-icon.shapes.css +1 -1
- package/dist/elements/a-input-time.css +1 -1
- package/dist/elements/a-input-time.d.ts +8 -0
- package/dist/elements/a-input-time.js +120 -15
- package/dist/elements/a-input.css +1 -1
- package/dist/elements/a-input.d.ts +27 -1
- package/dist/elements/a-input.js +180 -41
- package/dist/elements/a-loader.css +1 -1
- package/dist/elements/a-menu-group.css +1 -1
- package/dist/elements/a-menu-item.css +1 -1
- package/dist/elements/a-menu-item.js +1 -0
- package/dist/elements/a-menu-separator.css +1 -1
- package/dist/elements/a-menu.css +1 -1
- package/dist/elements/a-menu.d.ts +8 -13
- package/dist/elements/a-menu.js +146 -45
- package/dist/elements/a-panel.css +1 -0
- package/dist/elements/a-panel.d.ts +16 -0
- package/dist/elements/a-panel.js +153 -0
- package/dist/elements/a-progress.css +1 -1
- package/dist/elements/a-radio-group.css +1 -1
- package/dist/elements/a-radio.css +1 -1
- package/dist/elements/a-radio.d.ts +4 -0
- package/dist/elements/a-radio.js +18 -0
- package/dist/elements/a-slider.css +1 -1
- package/dist/elements/a-slider.d.ts +1 -0
- package/dist/elements/a-slider.js +3 -6
- package/dist/elements/a-switch.css +1 -1
- package/dist/elements/a-switch.d.ts +3 -0
- package/dist/elements/a-switch.js +17 -1
- package/dist/elements/a-tab.css +1 -1
- package/dist/elements/a-tabpanel.css +1 -1
- package/dist/elements/a-tabs.css +1 -1
- package/dist/elements/a-tag.css +1 -1
- package/dist/elements/a-text.css +1 -1
- package/dist/elements/a-text.js +1 -0
- package/dist/elements/a-title.css +1 -1
- package/dist/elements/a-toast.css +1 -1
- package/dist/elements/a-toaster.css +1 -1
- package/dist/elements/a-tooltip.css +1 -1
- package/dist/elements/a-tooltip.d.ts +3 -4
- package/dist/elements/a-tooltip.js +30 -8
- package/dist/elements/focus.d.ts +9 -0
- package/dist/elements/focus.js +41 -0
- package/dist/elements/index.d.ts +1 -0
- package/dist/elements/index.js +3 -0
- package/dist/elements/panel-focus.d.ts +10 -0
- package/dist/elements/panel-focus.js +86 -0
- package/dist/elements/shadow-aria.d.ts +10 -0
- package/dist/elements/shadow-aria.js +155 -0
- package/dist/general_types.d.ts +70 -42
- package/dist/generate-icons.mjs +3 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +2 -0
- package/dist/jsx-runtime.d.ts +10 -1
- package/dist/reset.css +1 -1
- package/dist/theme-antithesis.css +1 -0
- package/dist/theme-antune.css +1 -0
- package/dist/tokens.css +1 -1
- package/docs/accessibility.md +4 -2
- package/docs/changelog.md +135 -0
- package/docs/colors.md +7 -7
- package/docs/comparison.md +2 -2
- package/docs/components/box.md +98 -34
- package/docs/components/breadcrumbs.md +75 -6
- package/docs/components/button.md +2 -2
- package/docs/components/capture.md +8 -8
- package/docs/components/checkbox.md +21 -16
- package/docs/components/input-autocomplete.md +6 -0
- package/docs/components/input-date.md +8 -2
- package/docs/components/input.md +99 -40
- package/docs/components/menu.md +5 -6
- package/docs/components/panel.md +189 -0
- package/docs/components/radio.md +16 -13
- package/docs/components/select-faceted.md +58 -2
- package/docs/components/select.md +72 -59
- package/docs/components/steps.md +4 -4
- package/docs/components/switch.md +16 -10
- package/docs/components/tabs.md +21 -8
- package/docs/components/tag.md +5 -3
- package/docs/components/tooltip.md +6 -4
- package/docs/index.md +1 -0
- package/docs/install-config.md +23 -12
- package/docs/normalization.md +6 -4
- package/docs/theming.md +95 -17
- package/package.json +8 -4
- package/dist/components/InputDate.module.css +0 -1
- package/dist/components/Select.module.css +0 -1
- package/dist/components/SelectFaceted.module.css +0 -1
- package/dist/search-index.json +0 -0
- package/dist/theme-anta.css +0 -1
package/docs/components/input.md
CHANGED
|
@@ -9,9 +9,9 @@ for styling through `::part(input)`.
|
|
|
9
9
|
## Size
|
|
10
10
|
|
|
11
11
|
```tsx
|
|
12
|
-
<Input size="small" label="Small" placeholder="24px tall" />
|
|
13
|
-
<Input size="medium" label="Medium" placeholder="28px tall" /> {/* default */}
|
|
14
|
-
<Input size="large" label="Large" placeholder="32px tall" />
|
|
12
|
+
<Input size="small" label="Small" placeholder="24px tall" autoComplete="off" />
|
|
13
|
+
<Input size="medium" label="Medium" placeholder="28px tall" autoComplete="off" /> {/* default */}
|
|
14
|
+
<Input size="large" label="Large" placeholder="32px tall" autoComplete="off" />
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Three sizes (`medium` is the default). The field **height**, the **type scale**
|
|
@@ -27,8 +27,8 @@ Anta's text scale, so a field lines up with same-size `Text` / `Button`.
|
|
|
27
27
|
## Label and hint
|
|
28
28
|
|
|
29
29
|
```tsx
|
|
30
|
-
<Input label="Display name" defaultValue="Ada Lovelace" />
|
|
31
|
-
<Input label="API key" hint="Find this in Settings → Developers." placeholder="sk-…" />
|
|
30
|
+
<Input label="Display name" defaultValue="Ada Lovelace" autoComplete="name" />
|
|
31
|
+
<Input label="API key" hint="Find this in Settings → Developers." placeholder="sk-…" autoComplete="off" />
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
`label` sits above the field; `hint` is neutral helper text below it
|
|
@@ -40,12 +40,18 @@ mirrors the label text into `aria-label`, since a native `<label for>` can't
|
|
|
40
40
|
cross the shadow boundary. The `hint` (or error) is mirrored the same way into
|
|
41
41
|
`aria-description`, so a screen reader announces the message too.
|
|
42
42
|
|
|
43
|
+
Standard `role` and `aria-*` attributes on `<Input>` or raw `<a-input>` are
|
|
44
|
+
applied to the native input, textarea, or button inside its shadow root. This
|
|
45
|
+
keeps the focused native control as the single accessible control. If you query
|
|
46
|
+
the DOM directly, read those semantics from `a-input.control` rather than from
|
|
47
|
+
the custom-element host.
|
|
48
|
+
|
|
43
49
|
## Tooltip
|
|
44
50
|
|
|
45
51
|
```tsx
|
|
46
52
|
{/* Drop a <Tooltip> in as a child — it attaches to the field and shows on hover,
|
|
47
53
|
exactly like a tooltip on any other element. */}
|
|
48
|
-
<Input label="API key" placeholder="sk-…">
|
|
54
|
+
<Input label="API key" placeholder="sk-…" autoComplete="off">
|
|
49
55
|
<Tooltip>Find this in Settings → Developers.</Tooltip>
|
|
50
56
|
</Input>
|
|
51
57
|
```
|
|
@@ -60,12 +66,12 @@ content.)
|
|
|
60
66
|
|
|
61
67
|
```tsx
|
|
62
68
|
{/* No status = neutral helper text */}
|
|
63
|
-
<Input label="Display name" defaultValue="Ada Lovelace" hint="This is your public name." />
|
|
64
|
-
<Input label="Workspace" defaultValue="acme" status="info" hint="Lowercase letters and dashes only." />
|
|
65
|
-
<Input label="Username" defaultValue="ada" status="success" hint="Username is available." />
|
|
66
|
-
<Input label="Password" type="password" defaultValue="hunter2" status="warning" hint="Weak — add more characters." />
|
|
67
|
-
<Input label="Email" defaultValue="not-an-email" status="critical" hint="Enter a valid email address." />
|
|
68
|
-
<Input label="Plan" defaultValue="Pro" status="brand" hint="You're on the Pro plan." />
|
|
69
|
+
<Input label="Display name" defaultValue="Ada Lovelace" hint="This is your public name." autoComplete="name" />
|
|
70
|
+
<Input label="Workspace" defaultValue="acme" status="info" hint="Lowercase letters and dashes only." autoComplete="off" />
|
|
71
|
+
<Input label="Username" defaultValue="ada" status="success" hint="Username is available." autoComplete="username" />
|
|
72
|
+
<Input label="Password" type="password" defaultValue="hunter2" status="warning" hint="Weak — add more characters." autoComplete="new-password" />
|
|
73
|
+
<Input label="Email" type="email" defaultValue="not-an-email" status="critical" hint="Enter a valid email address." autoComplete="email" />
|
|
74
|
+
<Input label="Plan" defaultValue="Pro" status="brand" hint="You're on the Pro plan." autoComplete="off" />
|
|
69
75
|
```
|
|
70
76
|
|
|
71
77
|
`status` tints the border and the `hint`, and prefixes a glyph — `critical`,
|
|
@@ -92,15 +98,16 @@ with `statusIcon={false}`.
|
|
|
92
98
|
```tsx
|
|
93
99
|
{/* clearable — the clear button shows once there's a value */}
|
|
94
100
|
{/* dimActions — adornments rest quiet, brighten when the field is hovered/focused */}
|
|
95
|
-
<Input label="Search" placeholder="Search…" clearable dimActions defaultValue="design tokens" />
|
|
101
|
+
<Input label="Search" placeholder="Search…" clearable dimActions defaultValue="design tokens" autoComplete="off" />
|
|
96
102
|
|
|
97
103
|
{/* type="search" is a shorthand for the leading search icon + clearable below */}
|
|
98
|
-
<Input label="Search" type="search" placeholder="Search…" />
|
|
104
|
+
<Input label="Search" type="search" placeholder="Search…" autoComplete="off" />
|
|
99
105
|
|
|
100
106
|
{/* leading / trailing take any node — see the live playground above */}
|
|
101
|
-
<Input label="Search" leading={<Icon shape="search" />} clearable dimActions />
|
|
107
|
+
<Input label="Search" leading={<Icon shape="search" />} clearable dimActions autoComplete="off" />
|
|
102
108
|
<Input
|
|
103
109
|
label="Date"
|
|
110
|
+
autoComplete="off"
|
|
104
111
|
leading={<Icon shape="calendar" />}
|
|
105
112
|
trailing={<Button priority="quaternary" size="small" icon="eye" aria-label="Pick" />}
|
|
106
113
|
dimActions
|
|
@@ -149,6 +156,7 @@ const [reveal, setReveal] = useState(false)
|
|
|
149
156
|
|
|
150
157
|
<Input
|
|
151
158
|
label="Password"
|
|
159
|
+
autoComplete="current-password"
|
|
152
160
|
type={reveal ? 'text' : 'password'}
|
|
153
161
|
defaultValue="hunter2"
|
|
154
162
|
dimActions
|
|
@@ -176,10 +184,10 @@ Firefox. So Anta keeps the native field.
|
|
|
176
184
|
|
|
177
185
|
```tsx
|
|
178
186
|
{/* Grows with content, capped at 6 rows, then scrolls */}
|
|
179
|
-
<Input multiline maxRows={6} label="Bio (autogrows)" placeholder="Tell us…" />
|
|
187
|
+
<Input multiline maxRows={6} label="Bio (autogrows)" placeholder="Tell us…" autoComplete="off" />
|
|
180
188
|
|
|
181
189
|
{/* Constant height of 3 rows */}
|
|
182
|
-
<Input multiline rows={3} label="Notes (fixed 3 rows)" placeholder="…" />
|
|
190
|
+
<Input multiline rows={3} label="Notes (fixed 3 rows)" placeholder="…" autoComplete="off" />
|
|
183
191
|
```
|
|
184
192
|
|
|
185
193
|
`multiline` renders a `<textarea>` under the same API. With **no `rows`** it
|
|
@@ -195,11 +203,11 @@ browser.
|
|
|
195
203
|
|
|
196
204
|
```tsx
|
|
197
205
|
// Uncontrolled: the element updates its value after input.
|
|
198
|
-
<Input label="Name" defaultValue="Ada" onChange={(e) => log(e.target.value)} />
|
|
206
|
+
<Input label="Name" defaultValue="Ada" autoComplete="name" onChange={(e) => log(e.target.value)} />
|
|
199
207
|
|
|
200
208
|
// Controlled: application state supplies the value.
|
|
201
209
|
const [v, setV] = useState('')
|
|
202
|
-
<Input label="Name" value={v} onInput={(e) => setV(e.target.value)} />
|
|
210
|
+
<Input label="Name" value={v} autoComplete="name" onInput={(e) => setV(e.target.value)} />
|
|
203
211
|
```
|
|
204
212
|
|
|
205
213
|
Like a native input: pass `defaultValue` for **uncontrolled**, or `value` +
|
|
@@ -240,7 +248,7 @@ value-based, also has only `onValueChange`.)
|
|
|
240
248
|
|
|
241
249
|
```tsx
|
|
242
250
|
<form onSubmit={handle}>
|
|
243
|
-
<Input name="email" label="Email" type="email" required clearable />
|
|
251
|
+
<Input name="email" label="Email" type="email" autoComplete="email" required clearable />
|
|
244
252
|
<Button tone="brand" type="submit" label="Sign up" />
|
|
245
253
|
<Button priority="tertiary" type="reset" label="Reset" />
|
|
246
254
|
</form>
|
|
@@ -254,12 +262,12 @@ restores the field to its `defaultValue` via `formResetCallback`.
|
|
|
254
262
|
## Types and validation
|
|
255
263
|
|
|
256
264
|
```tsx
|
|
257
|
-
<Input type="text" label="Text" />
|
|
258
|
-
<Input type="email" label="Email" />
|
|
259
|
-
<Input type="password" label="Password" />
|
|
260
|
-
<Input type="tel" label="Tel" />
|
|
261
|
-
<Input type="url" label="URL" />
|
|
262
|
-
<Input type="number" label="Number" min={0} max={100} />
|
|
265
|
+
<Input type="text" label="Text" autoComplete="off" />
|
|
266
|
+
<Input type="email" label="Email" autoComplete="email" />
|
|
267
|
+
<Input type="password" label="Password" autoComplete="current-password" />
|
|
268
|
+
<Input type="tel" label="Tel" autoComplete="tel" />
|
|
269
|
+
<Input type="url" label="URL" autoComplete="url" />
|
|
270
|
+
<Input type="number" label="Number" autoComplete="off" min={0} max={100} />
|
|
263
271
|
```
|
|
264
272
|
|
|
265
273
|
`type` accepts `text` (default), `email`, `password`, `tel`, `url`, and
|
|
@@ -267,6 +275,12 @@ restores the field to its `defaultValue` via `formResetCallback`.
|
|
|
267
275
|
constraint validation. (`search` is omitted on purpose — see above.)
|
|
268
276
|
`number` also takes `min` / `max` / `step`.
|
|
269
277
|
|
|
278
|
+
Set `autoComplete` to describe the field's purpose. `email`, `tel`, and `url`
|
|
279
|
+
derive their matching token from `type`. Other purposes need an explicit token,
|
|
280
|
+
such as `name`, `username`, `current-password`, `new-password`, or
|
|
281
|
+
`one-time-code`. Use `autoComplete="off"` for examples and application fields
|
|
282
|
+
that do not represent autofill data.
|
|
283
|
+
|
|
270
284
|
For a **controlled** numeric field — one whose `value` you hold in state — prefer
|
|
271
285
|
`type="text"` with `inputMode="decimal"` (or `"numeric"`) over `type="number"`. A
|
|
272
286
|
number input runs value sanitization, so a mid-typing `"1."` or `"1.0"` reports an
|
|
@@ -296,10 +310,10 @@ function onSubmit(e) {
|
|
|
296
310
|
}
|
|
297
311
|
|
|
298
312
|
<form noValidate onSubmit={onSubmit}>
|
|
299
|
-
<Input name="email" type="email" required hint={errors.email}
|
|
313
|
+
<Input name="email" type="email" autoComplete="email" required hint={errors.email}
|
|
300
314
|
status={errors.email ? 'critical' : undefined}
|
|
301
315
|
onInput={() => setErrors(p => ({ ...p, email: undefined }))} />
|
|
302
|
-
<Input name="age" type="number" min="18" max="120" hint={errors.age}
|
|
316
|
+
<Input name="age" type="number" autoComplete="off" min="18" max="120" hint={errors.age}
|
|
303
317
|
status={errors.age ? 'critical' : undefined}
|
|
304
318
|
onInput={() => setErrors(p => ({ ...p, age: undefined }))} />
|
|
305
319
|
<Button type="submit" label="Submit" />
|
|
@@ -345,7 +359,7 @@ Re-template the host to two columns and drop each part into one. The hint stays
|
|
|
345
359
|
under the field by landing in the same column.
|
|
346
360
|
|
|
347
361
|
```tsx
|
|
348
|
-
<Input className="label-side" label="Display name" hint="Shown on your profile" />
|
|
362
|
+
<Input className="label-side" label="Display name" hint="Shown on your profile" autoComplete="name" />
|
|
349
363
|
|
|
350
364
|
{/* .label-side is just for the demo — use your own selector */}
|
|
351
365
|
<style>{`
|
|
@@ -369,9 +383,9 @@ left-edge lines up. Each hint still tucks under its own field.
|
|
|
369
383
|
|
|
370
384
|
```tsx
|
|
371
385
|
<div className="aligned-form">
|
|
372
|
-
<Input label="Email" type="email" hint="We'll never share it." />
|
|
373
|
-
<Input label="Confirm password" type="password" />
|
|
374
|
-
<Input label="PIN" />
|
|
386
|
+
<Input label="Email" type="email" autoComplete="email" hint="We'll never share it." />
|
|
387
|
+
<Input label="Confirm password" type="password" autoComplete="new-password" />
|
|
388
|
+
<Input label="PIN" autoComplete="one-time-code" />
|
|
375
389
|
</div>
|
|
376
390
|
|
|
377
391
|
<style>{`
|
|
@@ -391,7 +405,7 @@ unit. Two columns (field, then hint); the label spans the top, and the hint
|
|
|
391
405
|
centers against the field.
|
|
392
406
|
|
|
393
407
|
```tsx
|
|
394
|
-
<Input className="hint-right" label="Username" hint="3–20 characters" placeholder="ada" />
|
|
408
|
+
<Input className="hint-right" label="Username" hint="3–20 characters" placeholder="ada" autoComplete="username" />
|
|
395
409
|
|
|
396
410
|
<style>{`
|
|
397
411
|
a-input.hint-right {
|
|
@@ -412,10 +426,10 @@ wraps to fewer columns as it narrows. Resize the preview to see it reflow.
|
|
|
412
426
|
|
|
413
427
|
```tsx
|
|
414
428
|
<div className="wrap-fields">
|
|
415
|
-
<Input label="First name" />
|
|
416
|
-
<Input label="Last name" />
|
|
417
|
-
<Input label="Email" type="email" />
|
|
418
|
-
<Input label="Phone" type="tel" />
|
|
429
|
+
<Input label="First name" autoComplete="given-name" />
|
|
430
|
+
<Input label="Last name" autoComplete="family-name" />
|
|
431
|
+
<Input label="Email" type="email" autoComplete="email" />
|
|
432
|
+
<Input label="Phone" type="tel" autoComplete="tel" />
|
|
419
433
|
</div>
|
|
420
434
|
|
|
421
435
|
<style>{`
|
|
@@ -430,6 +444,7 @@ wraps to fewer columns as it narrows. Resize the preview to see it reflow.
|
|
|
430
444
|
|------|------|---------|-------------|
|
|
431
445
|
| `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`. |
|
|
432
446
|
| `autoFocus?` | boolean | — | Focus this field when its containing `Dialog` opens. |
|
|
447
|
+
| `button?` | boolean | — | Render the field surface with a native button as its focus target. This is available for Input-styled popup triggers; the value becomes the button text and the placeholder is shown while it is empty. |
|
|
433
448
|
| `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
449
|
| `clearable?` | boolean | — | Show a clear button as the first trailing item once the field has a value. |
|
|
435
450
|
| `defaultValue?` | string | — | Initial value for the uncontrolled case. |
|
|
@@ -457,7 +472,7 @@ wraps to fewer columns as it narrows. Resize the preview to see it reflow.
|
|
|
457
472
|
| `placeholder?` | string | — | Placeholder shown when empty. |
|
|
458
473
|
| `readOnly?` | boolean | — | Make the field read-only. |
|
|
459
474
|
| `required?` | boolean | — | Mark the field required (drives native validity). |
|
|
460
|
-
| `role?` | string | — | ARIA `role` for the field — e.g. `combobox` when the input drives a suggestion `listbox` (see `InputAutocomplete`). Left unset by default. |
|
|
475
|
+
| `role?` | string | — | ARIA `role` for the field — e.g. `combobox` when the input drives a suggestion `listbox` (see `InputAutocomplete`). The custom element delegates it, together with standard `aria-*` props, to the focused native shadow control. Left unset by default. |
|
|
461
476
|
| `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
477
|
| `rows?` | number | — | Fixed visible row count — a constant-height `<textarea>` (implies `multiline`). |
|
|
463
478
|
| `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). |
|
|
@@ -478,7 +493,7 @@ Use the web component directly when you are not using React or Preact and a nati
|
|
|
478
493
|
Slots hold the label, hint, and controls around the native field.
|
|
479
494
|
|
|
480
495
|
```html
|
|
481
|
-
<a-input name="email" placeholder="you@example.com">
|
|
496
|
+
<a-input name="email" type="email" autocomplete="email" placeholder="you@example.com">
|
|
482
497
|
<span slot="label">Email</span>
|
|
483
498
|
<span slot="hint">We only use it for account notices.</span>
|
|
484
499
|
</a-input>
|
|
@@ -524,7 +539,7 @@ Reach for the props first: **`status`** sets a validation tone (border + message
|
|
|
524
539
|
dimensions. The focus ring is the global [`--focus-ring`](../colors.md#focus-ring).
|
|
525
540
|
|
|
526
541
|
```tsx
|
|
527
|
-
<Input tone="#e0457b" label="Custom accent" />
|
|
542
|
+
<Input tone="#e0457b" label="Custom accent" autoComplete="off" />
|
|
528
543
|
```
|
|
529
544
|
|
|
530
545
|
For everything else, `<a-input>` is shadow-DOM — style its **parts** with plain
|
|
@@ -563,5 +578,49 @@ a-input.fancy::part(field) {
|
|
|
563
578
|
padding-inline: 20px; /* roomier */
|
|
564
579
|
}
|
|
565
580
|
a-input.fancy::part(input) { color: light-dark(#ffffff, #1a1a1a); }
|
|
581
|
+
a-input.fancy::part(input)::placeholder {
|
|
582
|
+
color: light-dark(#b8b8b8, #666666);
|
|
583
|
+
opacity: 1;
|
|
584
|
+
}
|
|
566
585
|
a-input.fancy:focus-within::part(field) { background: light-dark(#161616, #ffffff); }
|
|
567
586
|
```
|
|
587
|
+
|
|
588
|
+
### Placeholder contrast
|
|
589
|
+
|
|
590
|
+
If the label, marking, context, or hint leaves the field's purpose unclear,
|
|
591
|
+
a stronger placeholder can help. Keep an accessible name and put essential
|
|
592
|
+
instructions in a persistent label or hint.
|
|
593
|
+
|
|
594
|
+
Antune uses `--text-4` for placeholders. These ratios compare the default neutral
|
|
595
|
+
Input backgrounds with the example's literal color:
|
|
596
|
+
|
|
597
|
+
| Style | Placeholder | Background | Contrast |
|
|
598
|
+
| --- | --- | --- | --- |
|
|
599
|
+
| Antune light theme | `#7a737c` | `#ffffff` | 4.59:1 |
|
|
600
|
+
| Antune dark theme | `#7c737c` | `#000000` | 4.60:1 |
|
|
601
|
+
| Example | `#767676` | `#ffffff` | 4.54:1 |
|
|
602
|
+
|
|
603
|
+
`#767676` is the lightest opaque 8-bit neutral gray that passes 4.5:1 on white.
|
|
604
|
+
W3C recommends [sufficient placeholder contrast](https://www.w3.org/WAI/tutorials/forms/instructions/#styling).
|
|
605
|
+
Redundant placeholders remain debated in the
|
|
606
|
+
[meeting discussion](https://www.w3.org/2025/04/16-matf-minutes.html) and
|
|
607
|
+
[open W3C issue](https://github.com/w3c/wcag/issues/4343); there is no agreed exemption.
|
|
608
|
+
|
|
609
|
+
This demo uses `#767676` on white in every state and theme.
|
|
610
|
+
Replace `.placeholder-contrast` with your own class.
|
|
611
|
+
|
|
612
|
+
```tsx
|
|
613
|
+
<Input className="placeholder-contrast" label="Email" type="email" autoComplete="email" placeholder="you@example.com" />
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
```css
|
|
617
|
+
a-input.placeholder-contrast::part(field) { background: #ffffff; }
|
|
618
|
+
a-input.placeholder-contrast::part(input) { color: #161616; }
|
|
619
|
+
a-input.placeholder-contrast::part(input)::placeholder {
|
|
620
|
+
color: #767676;
|
|
621
|
+
opacity: 1;
|
|
622
|
+
}
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
Check contrast separately if you use a different background.
|
|
626
|
+
Native inputs and textareas use `::placeholder` without `::part(input)`.
|
package/docs/components/menu.md
CHANGED
|
@@ -49,9 +49,8 @@ action. Pass **any CSS color** for a one-off custom tone — its hue and chroma
|
|
|
49
49
|
kept while the lightness is pinned to match the brand text (resolved through
|
|
50
50
|
`--menu-item-tone-source`).
|
|
51
51
|
|
|
52
|
-
`
|
|
53
|
-
|
|
54
|
-
(`Select` uses it to tone the chosen row).
|
|
52
|
+
`toneScope="selected"` applies `tone` **only while the row is `selected`** — an
|
|
53
|
+
unselected row stays neutral. `Select` uses the same pair to tone chosen rows.
|
|
55
54
|
|
|
56
55
|
```tsx
|
|
57
56
|
<Menu>
|
|
@@ -416,7 +415,7 @@ navigating links.
|
|
|
416
415
|
| `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
416
|
| `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
417
|
| `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
|
-
| `
|
|
418
|
+
| `toneScope?` | 'all' \| 'selected' | 'all' | Apply `tone` to every row state, or only while the row is selected. In `selected` scope, an unselected row and its checkbox/radio indicator stay neutral. |
|
|
420
419
|
| `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. |
|
|
421
420
|
|
|
422
421
|
## Keyboard
|
|
@@ -462,7 +461,7 @@ or do nothing to reject. (Submenus are always uncontrolled, regardless of `open`
|
|
|
462
461
|
| `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
462
|
| `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
463
|
| `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
|
|
464
|
+
| `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 an authored option `id`, or `null`. Built-in Anta Inputs receive the active option through a direct ARIA element relationship; this remains available to custom fields and observers. Passed straight through to the element. |
|
|
466
465
|
| `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
466
|
| `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
467
|
| `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. |
|
|
@@ -495,7 +494,7 @@ or do nothing to reject. (Submenus are always uncontrolled, regardless of `open`
|
|
|
495
494
|
| `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
495
|
| `target?` | string | — | Anchor target, e.g. `'_blank'`. |
|
|
497
496
|
| `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
|
-
| `
|
|
497
|
+
| `toneScope?` | 'all' \| 'selected' | 'all' | Apply `tone` to every row state, or only while the row is selected. In `selected` scope, an unselected row and its checkbox/radio indicator stay neutral. |
|
|
499
498
|
| `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
499
|
|
|
501
500
|
## Web Component
|
|
@@ -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
|
+
```
|
package/docs/components/radio.md
CHANGED
|
@@ -57,7 +57,8 @@ one. Any literal CSS color works too, for a one-off custom tone.
|
|
|
57
57
|
|
|
58
58
|
```tsx
|
|
59
59
|
<RadioGroup
|
|
60
|
-
|
|
60
|
+
tone="brand"
|
|
61
|
+
toneScope="selected"
|
|
61
62
|
defaultValue="a"
|
|
62
63
|
options={[
|
|
63
64
|
{ value: 'a', label: 'Option A' },
|
|
@@ -66,10 +67,10 @@ one. Any literal CSS color works too, for a one-off custom tone.
|
|
|
66
67
|
/>
|
|
67
68
|
```
|
|
68
69
|
|
|
69
|
-
`
|
|
70
|
-
neutral grey.
|
|
71
|
-
as a validation error.
|
|
72
|
-
override
|
|
70
|
+
`toneScope="selected"` applies the group's `tone` to the **selected option only** —
|
|
71
|
+
every unselected ring stays neutral grey. Use it when a resting tinted border would
|
|
72
|
+
read as a validation error. Set the scope on the group, or on a single option to
|
|
73
|
+
override the group.
|
|
73
74
|
|
|
74
75
|
To color the **label and hint**, there's no prop — add a `color` rule on the
|
|
75
76
|
option's `a-radio-label` / `a-radio-hint` with a `--text-N-{tone}` value.
|
|
@@ -164,7 +165,9 @@ Under the hood the split is deliberate. The `<a-radio-group>` web component
|
|
|
164
165
|
**never mutates the DOM** — it coordinates entirely off-DOM: selection is set as a
|
|
165
166
|
property on each `<a-radio>` (which reflects it into `:state(selected)` +
|
|
166
167
|
`aria-checked` via its own `ElementInternals`), the form value goes through
|
|
167
|
-
`ElementInternals`, and focus is tracked with `aria-activedescendant`.
|
|
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
|
|
168
171
|
**roving `tabindex`** — which option Tab lands on — is the one piece rendered
|
|
169
172
|
declaratively, by the `RadioGroup` wrapper, from the current value. That's why
|
|
170
173
|
selection is owned by the element but the wrapper holds the value: it needs it to
|
|
@@ -268,8 +271,8 @@ element — `Input`, and the composed `Select` — skip it and expose only
|
|
|
268
271
|
| `orientation?` | 'vertical' \| 'horizontal' | 'vertical' | Layout + arrow-key axis. |
|
|
269
272
|
| `size?` | 'small' \| 'medium' \| 'large' | 'medium' | Size applied to every option (an option's own `size` wins). |
|
|
270
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. |
|
|
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.
|
|
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. |
|
|
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. |
|
|
273
276
|
| `value?` | string | — | Controlled selected value. The group follows this prop and reports a requested change through `onStateChange`. Leave it undefined for uncontrolled use. |
|
|
274
277
|
|
|
275
278
|
## Web Component
|
|
@@ -294,22 +297,22 @@ For a standard HTML form, add `data-anta` to each native radio. Radios that shar
|
|
|
294
297
|
a `name` retain the browser's selection, keyboard behavior, labels, and form
|
|
295
298
|
submission while using Anta's control surface.
|
|
296
299
|
|
|
297
|
-
`size`, `tone`, and `tone-
|
|
300
|
+
`size`, `tone`, and `tone-scope` use the matching RadioGroup option
|
|
298
301
|
treatments.
|
|
299
302
|
|
|
300
303
|
```html
|
|
301
304
|
<div style="display: grid; gap: 8px">
|
|
302
305
|
<label><input data-anta type="radio" name="plan" value="starter" size="small" tone="brand"> Starter</label>
|
|
303
|
-
<label><input data-anta type="radio" name="plan" value="pro" tone
|
|
306
|
+
<label><input data-anta type="radio" name="plan" value="pro" tone="success" tone-scope="selected" checked> Pro</label>
|
|
304
307
|
<label><input data-anta type="radio" name="plan" value="enterprise" size="large" tone="warning"> Enterprise</label>
|
|
305
308
|
</div>
|
|
306
309
|
```
|
|
307
310
|
|
|
308
311
|
## Styling
|
|
309
312
|
|
|
310
|
-
Reach for the props first: **`tone`**
|
|
311
|
-
|
|
312
|
-
the group or
|
|
313
|
+
Reach for the props first: **`tone`** chooses the mark color and **`toneScope`**
|
|
314
|
+
chooses whether it applies in all states or only to the selected option (any CSS
|
|
315
|
+
color can be set on the group or one option and derives the full curve in oklch), **`size`** the
|
|
313
316
|
dimensions + type. To tint the label + hint, add a `color` rule on the option's
|
|
314
317
|
`a-radio-label` / `a-radio-hint` with the `--text-N-{tone}` values — there's no
|
|
315
318
|
text-tone prop. The focus ring is the global [`--focus-ring`](../colors.md#focus-ring).
|