@tempoxyz/ds 0.3.10 → 0.3.11

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 (120) hide show
  1. package/dist/docs/AGENTS.md +9 -0
  2. package/dist/docs/components/accordion.md +489 -0
  3. package/dist/docs/components/address-card.md +147 -0
  4. package/dist/docs/components/agent-logo.md +194 -0
  5. package/dist/docs/components/alert-dialog.md +631 -0
  6. package/dist/docs/components/alert.md +448 -0
  7. package/dist/docs/components/amount-input.md +305 -0
  8. package/dist/docs/components/amount.md +794 -0
  9. package/dist/docs/components/app-shell.md +559 -0
  10. package/dist/docs/components/asset-chip.md +294 -0
  11. package/dist/docs/components/autocomplete.md +619 -0
  12. package/dist/docs/components/avatar.md +321 -0
  13. package/dist/docs/components/badge.md +339 -0
  14. package/dist/docs/components/badged-logo.md +116 -0
  15. package/dist/docs/components/banner.md +379 -0
  16. package/dist/docs/components/bar-chart.md +208 -0
  17. package/dist/docs/components/breadcrumbs.md +380 -0
  18. package/dist/docs/components/button-group.md +286 -0
  19. package/dist/docs/components/button.md +650 -0
  20. package/dist/docs/components/calendar.md +375 -0
  21. package/dist/docs/components/callout.md +96 -0
  22. package/dist/docs/components/checkbox-group.md +521 -0
  23. package/dist/docs/components/checkbox.md +467 -0
  24. package/dist/docs/components/choice-group.md +516 -0
  25. package/dist/docs/components/choicebox.md +641 -0
  26. package/dist/docs/components/code.md +100 -0
  27. package/dist/docs/components/combobox.md +939 -0
  28. package/dist/docs/components/container.md +257 -0
  29. package/dist/docs/components/copy-button.md +642 -0
  30. package/dist/docs/components/date-picker.md +514 -0
  31. package/dist/docs/components/dialog.md +1332 -0
  32. package/dist/docs/components/drawer.md +966 -0
  33. package/dist/docs/components/empty.md +340 -0
  34. package/dist/docs/components/field.md +570 -0
  35. package/dist/docs/components/fieldset.md +398 -0
  36. package/dist/docs/components/frame.md +434 -0
  37. package/dist/docs/components/gauge.md +299 -0
  38. package/dist/docs/components/get-started-visual.md +117 -0
  39. package/dist/docs/components/grid.md +288 -0
  40. package/dist/docs/components/hero-visual.md +155 -0
  41. package/dist/docs/components/hex.md +487 -0
  42. package/dist/docs/components/icon-tile.md +292 -0
  43. package/dist/docs/components/identicon.md +371 -0
  44. package/dist/docs/components/inline.md +233 -0
  45. package/dist/docs/components/inset.md +225 -0
  46. package/dist/docs/components/kbd.md +423 -0
  47. package/dist/docs/components/list-group.md +981 -0
  48. package/dist/docs/components/loading-dots.md +237 -0
  49. package/dist/docs/components/menu.md +934 -0
  50. package/dist/docs/components/native-select.md +204 -0
  51. package/dist/docs/components/network-logo.md +248 -0
  52. package/dist/docs/components/number-field.md +543 -0
  53. package/dist/docs/components/otp-field.md +616 -0
  54. package/dist/docs/components/pagination.md +516 -0
  55. package/dist/docs/components/popover.md +544 -0
  56. package/dist/docs/components/progress.md +388 -0
  57. package/dist/docs/components/qr.md +314 -0
  58. package/dist/docs/components/radio-group.md +528 -0
  59. package/dist/docs/components/secret-value.md +406 -0
  60. package/dist/docs/components/select.md +751 -0
  61. package/dist/docs/components/separator.md +379 -0
  62. package/dist/docs/components/sidebar-menu.md +922 -0
  63. package/dist/docs/components/skeleton.md +517 -0
  64. package/dist/docs/components/slider.md +539 -0
  65. package/dist/docs/components/snippet.md +424 -0
  66. package/dist/docs/components/spinner.md +216 -0
  67. package/dist/docs/components/split-bar.md +139 -0
  68. package/dist/docs/components/split-button.md +464 -0
  69. package/dist/docs/components/stack.md +299 -0
  70. package/dist/docs/components/status-chip.md +226 -0
  71. package/dist/docs/components/status-indicator.md +119 -0
  72. package/dist/docs/components/status-row.md +174 -0
  73. package/dist/docs/components/stepper.md +451 -0
  74. package/dist/docs/components/success-hero.md +157 -0
  75. package/dist/docs/components/surface.md +639 -0
  76. package/dist/docs/components/switch.md +472 -0
  77. package/dist/docs/components/table.md +967 -0
  78. package/dist/docs/components/tabs.md +627 -0
  79. package/dist/docs/components/tempo-logo.md +185 -0
  80. package/dist/docs/components/text-input.md +624 -0
  81. package/dist/docs/components/textarea.md +352 -0
  82. package/dist/docs/components/toast.md +535 -0
  83. package/dist/docs/components/token-logo.md +371 -0
  84. package/dist/docs/components/token.md +247 -0
  85. package/dist/docs/components/tooltip.md +490 -0
  86. package/dist/docs/components/verification-code.md +116 -0
  87. package/dist/docs/components/visual.md +249 -0
  88. package/dist/docs/components.md +203 -0
  89. package/dist/docs/foundations/accessibility.md +377 -0
  90. package/dist/docs/foundations/agents.md +150 -0
  91. package/dist/docs/foundations/color.md +522 -0
  92. package/dist/docs/foundations/content/tone.md +194 -0
  93. package/dist/docs/foundations/content/voice.md +194 -0
  94. package/dist/docs/foundations/content/writing-style.md +406 -0
  95. package/dist/docs/foundations/iconography.md +282 -0
  96. package/dist/docs/foundations/intent.md +360 -0
  97. package/dist/docs/foundations/layout.md +508 -0
  98. package/dist/docs/foundations/linter.md +296 -0
  99. package/dist/docs/foundations/motion.md +151 -0
  100. package/dist/docs/foundations/radius-and-padding.md +86 -0
  101. package/dist/docs/foundations/typography.md +266 -0
  102. package/dist/docs/index.md +137 -0
  103. package/dist/docs/introduction/faq.md +132 -0
  104. package/dist/docs/introduction/getting-started.md +377 -0
  105. package/dist/docs/patterns/blockchain-values.md +709 -0
  106. package/dist/docs/patterns/dashboards.md +638 -0
  107. package/dist/docs/patterns/destructive-actions.md +626 -0
  108. package/dist/docs/patterns/empty-states.md +612 -0
  109. package/dist/docs/patterns/errors-and-validation.md +808 -0
  110. package/dist/docs/patterns/feedback-and-status.md +518 -0
  111. package/dist/docs/patterns/forms.md +679 -0
  112. package/dist/docs/patterns/loading.md +630 -0
  113. package/dist/docs/patterns/navigation.md +397 -0
  114. package/dist/docs/patterns/payments.md +765 -0
  115. package/dist/docs/patterns/responsive-layout.md +997 -0
  116. package/dist/docs/patterns/tables-and-lists.md +964 -0
  117. package/dist/docs/products/tempo-console.md +671 -0
  118. package/dist/docs/products/tempo-performance.md +437 -0
  119. package/dist/docs/products/tempo-wallet.md +438 -0
  120. package/package.json +1 -1
@@ -0,0 +1,528 @@
1
+ # RadioGroup
2
+
3
+ Lets you pick a single option from a short list.
4
+
5
+ [View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/RadioGroup.tsx)
6
+
7
+ Figma: [Radio Group](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=3506-3363) (`RadioGroup.Root`)
8
+
9
+ ## Overview
10
+
11
+ Arrow keys move focus and selection together.
12
+
13
+ ## Examples
14
+
15
+ ### Basic
16
+
17
+ ```tsx
18
+ import { Field, RadioGroup } from '@tempoxyz/ds'
19
+
20
+ function Choice(props: { label: string; value: string }) {
21
+ return (
22
+ <Field.Root>
23
+ <Field.Item>
24
+ <RadioGroup.Item value={props.value} />
25
+ <Field.Label>{props.label}</Field.Label>
26
+ </Field.Item>
27
+ </Field.Root>
28
+ )
29
+ }
30
+
31
+ export function Demo() {
32
+ return (
33
+ <RadioGroup.Root aria-label="Plan" defaultValue="pro">
34
+ <Choice label="Free" value="free" />
35
+ <Choice label="Pro" value="pro" />
36
+ <Choice label="Team" value="team" />
37
+ </RadioGroup.Root>
38
+ )
39
+ }
40
+ ```
41
+
42
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/RadioGroup/Basic.tsx)
43
+
44
+ ### Size
45
+
46
+ Set `size` on `RadioGroup.Root` to `small`, `medium` (default), or `large`.
47
+
48
+ ```tsx
49
+ import { Field, RadioGroup, style } from '@tempoxyz/ds'
50
+
51
+ function Choice(props: { label: string; value: string }) {
52
+ return (
53
+ <Field.Root>
54
+ <Field.Item>
55
+ <RadioGroup.Item value={props.value} />
56
+ <Field.Label>{props.label}</Field.Label>
57
+ </Field.Item>
58
+ </Field.Root>
59
+ )
60
+ }
61
+
62
+ export function Demo() {
63
+ return (
64
+ <div {...styles.row()}>
65
+ {(['small', 'medium', 'large'] as const).map((size) => (
66
+ <RadioGroup.Root aria-label={`Plan ${size}`} defaultValue="pro" key={size} size={size}>
67
+ <Choice label={`Free (${size})`} value="free" />
68
+ <Choice label={`Pro (${size})`} value="pro" />
69
+ </RadioGroup.Root>
70
+ ))}
71
+ </div>
72
+ )
73
+ }
74
+
75
+ namespace styles {
76
+ export const row = style({ display: 'flex', flexWrap: 'wrap', gap: '48' })
77
+ }
78
+ ```
79
+
80
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/RadioGroup/Size.tsx)
81
+
82
+ ### Disabled
83
+
84
+ Set `disabled` on a `RadioGroup.Item` to disable one option, or on `RadioGroup.Root` to disable the group.
85
+
86
+ ```tsx
87
+ import { Field, RadioGroup, style } from '@tempoxyz/ds'
88
+
89
+ function Choice(props: { disabled?: boolean; label: string; value: string }) {
90
+ return (
91
+ <Field.Root>
92
+ <Field.Item>
93
+ <RadioGroup.Item disabled={props.disabled} value={props.value} />
94
+ <Field.Label>{props.label}</Field.Label>
95
+ </Field.Item>
96
+ </Field.Root>
97
+ )
98
+ }
99
+
100
+ export function Demo() {
101
+ return (
102
+ <div {...styles.row()}>
103
+ <RadioGroup.Root aria-label="Plan" defaultValue="pro">
104
+ <Choice label="Free" value="free" />
105
+ <Choice label="Pro" value="pro" />
106
+ <Choice disabled label="Team" value="team" />
107
+ </RadioGroup.Root>
108
+ <RadioGroup.Root aria-label="Disabled plan" defaultValue="pro" disabled>
109
+ <Choice label="Free" value="free" />
110
+ <Choice label="Pro" value="pro" />
111
+ </RadioGroup.Root>
112
+ </div>
113
+ )
114
+ }
115
+
116
+ namespace styles {
117
+ export const row = style({ display: 'flex', flexWrap: 'wrap', gap: '48' })
118
+ }
119
+ ```
120
+
121
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/RadioGroup/Disabled.tsx)
122
+
123
+ ## Anatomy
124
+
125
+ Import the component and assemble its parts.
126
+
127
+ ```tsx
128
+ import { Field, RadioGroup } from '@tempoxyz/ds'
129
+
130
+ <RadioGroup.Root>
131
+ <Field.Root>
132
+ <Field.Item>
133
+ <RadioGroup.Item />
134
+ <Field.Label />
135
+ </Field.Item>
136
+ </Field.Root>
137
+ </RadioGroup.Root>
138
+ ```
139
+
140
+ ## Guidelines
141
+
142
+ ### When to Use
143
+
144
+ * Pick one of two to five options that people compare side by side.
145
+ * Choose a setting with a few fixed values, such as a payout schedule or API key permissions.
146
+
147
+ ### When Not to Use
148
+
149
+ * Use [Checkbox](/docs/components/checkbox) or [CheckboxGroup](/docs/components/checkbox-group) for several picks, or for a single yes or no.
150
+ * Use [Select](/docs/components/select) for more than five options, or when space is tight.
151
+ * Use [ChoiceGroup](/docs/components/choice-group) to switch a view or mode, such as 24H and 7D.
152
+ * Use [Choicebox](/docs/components/choicebox) when each option needs an icon or a description.
153
+ * Use [Switch](/docs/components/switch) for a single setting that applies immediately.
154
+
155
+ ### Ask One Question
156
+
157
+ Name the group with its question: point `aria-labelledby` at visible text, set `aria-label`, or use a `Fieldset.Legend`.
158
+
159
+ **Do:** Make every option answer the group's question.
160
+
161
+ ```tsx
162
+ /**
163
+ * Do example for "Ask One Question" on the RadioGroup page.
164
+ * Make every option answer the group's question.
165
+ * @module
166
+ */
167
+ import { Field, Fieldset, RadioGroup } from '@tempoxyz/ds'
168
+
169
+ const options = ['Read only', 'Read and write', 'Admin']
170
+
171
+ /** Renders the static preview, which the guideline card shows inert. */
172
+ export function Demo() {
173
+ return (
174
+ <Fieldset.Root>
175
+ <Fieldset.Legend>Who can use this key?</Fieldset.Legend>
176
+ <RadioGroup.Root defaultValue="Read only">
177
+ {options.map((option) => (
178
+ <Field.Root key={option}>
179
+ <Field.Item>
180
+ <RadioGroup.Item value={option} />
181
+ <Field.Label>{option}</Field.Label>
182
+ </Field.Item>
183
+ </Field.Root>
184
+ ))}
185
+ </RadioGroup.Root>
186
+ </Fieldset.Root>
187
+ )
188
+ }
189
+ ```
190
+
191
+ **Don't:** Mix in an option that answers a different question.
192
+
193
+ ```tsx
194
+ /**
195
+ * Don't example for "Ask One Question" on the RadioGroup page.
196
+ * Don't mix in an option that answers a different question.
197
+ * @module
198
+ */
199
+ import { Field, Fieldset, RadioGroup } from '@tempoxyz/ds'
200
+
201
+ const options = ['Read only', 'Expires in 30 days', 'Admin']
202
+
203
+ /** Renders the static preview, which the guideline card shows inert. */
204
+ export function Demo() {
205
+ return (
206
+ <Fieldset.Root>
207
+ <Fieldset.Legend>Who can use this key?</Fieldset.Legend>
208
+ <RadioGroup.Root defaultValue="Read only">
209
+ {options.map((option) => (
210
+ <Field.Root key={option}>
211
+ <Field.Item>
212
+ <RadioGroup.Item value={option} />
213
+ <Field.Label>{option}</Field.Label>
214
+ </Field.Item>
215
+ </Field.Root>
216
+ ))}
217
+ </RadioGroup.Root>
218
+ </Fieldset.Root>
219
+ )
220
+ }
221
+ ```
222
+
223
+ ### Preselect Only a Safe Default
224
+
225
+ **Do:** Preselect the option with the least access, such as Read only.
226
+
227
+ ```tsx
228
+ /**
229
+ * Do example for "Preselect Only a Safe Default" on the RadioGroup page.
230
+ * Preselect the option with the least access, such as Read only.
231
+ * @module
232
+ */
233
+ import { Field, RadioGroup } from '@tempoxyz/ds'
234
+
235
+ /** Renders the static preview, which the guideline card shows inert. */
236
+ export function Demo() {
237
+ return (
238
+ <RadioGroup.Root aria-label="Key permissions" defaultValue="read">
239
+ <Field.Root>
240
+ <Field.Item>
241
+ <RadioGroup.Item value="read" />
242
+ <Field.Label>Read only</Field.Label>
243
+ </Field.Item>
244
+ </Field.Root>
245
+ <Field.Root>
246
+ <Field.Item>
247
+ <RadioGroup.Item value="write" />
248
+ <Field.Label>Read and write</Field.Label>
249
+ </Field.Item>
250
+ </Field.Root>
251
+ </RadioGroup.Root>
252
+ )
253
+ }
254
+ ```
255
+
256
+ **Don't:** Preselect an option that grants more access or moves funds.
257
+
258
+ ```tsx
259
+ /**
260
+ * Don't example for "Preselect Only a Safe Default" on the RadioGroup page.
261
+ * Don't preselect an option that grants more access or moves funds.
262
+ * @module
263
+ */
264
+ import { Field, RadioGroup } from '@tempoxyz/ds'
265
+
266
+ /** Renders the static preview, which the guideline card shows inert. */
267
+ export function Demo() {
268
+ return (
269
+ <RadioGroup.Root aria-label="Key permissions" defaultValue="write">
270
+ <Field.Root>
271
+ <Field.Item>
272
+ <RadioGroup.Item value="read" />
273
+ <Field.Label>Read only</Field.Label>
274
+ </Field.Item>
275
+ </Field.Root>
276
+ <Field.Root>
277
+ <Field.Item>
278
+ <RadioGroup.Item value="write" />
279
+ <Field.Label>Read and write</Field.Label>
280
+ </Field.Item>
281
+ </Field.Root>
282
+ </RadioGroup.Root>
283
+ )
284
+ }
285
+ ```
286
+
287
+ If no option is safe, leave the group empty, set `required`, and ask for a choice on submit. A group cannot return to empty after a pick. If no choice is a valid answer, add an option such as None.
288
+
289
+ ### Order Options Logically
290
+
291
+ With no natural order, list the most common option first. Keep the same order wherever the question appears.
292
+
293
+ **Do:** List time periods in order, from Daily to Monthly.
294
+
295
+ ```tsx
296
+ /**
297
+ * Do example for "Order Options Logically" on the RadioGroup page.
298
+ * List time periods in order, from Daily to Monthly.
299
+ * @module
300
+ */
301
+ import { Field, RadioGroup } from '@tempoxyz/ds'
302
+
303
+ /** Renders the static preview, which the guideline card shows inert. */
304
+ export function Demo() {
305
+ return (
306
+ <RadioGroup.Root aria-label="Payout schedule" defaultValue="weekly">
307
+ {['Daily', 'Weekly', 'Monthly'].map((label) => (
308
+ <Field.Root key={label}>
309
+ <Field.Item>
310
+ <RadioGroup.Item value={label.toLowerCase()} />
311
+ <Field.Label>{label}</Field.Label>
312
+ </Field.Item>
313
+ </Field.Root>
314
+ ))}
315
+ </RadioGroup.Root>
316
+ )
317
+ }
318
+ ```
319
+
320
+ **Don't:** Mix the order of options that have a sequence.
321
+
322
+ ```tsx
323
+ /**
324
+ * Don't example for "Order Options Logically" on the RadioGroup page.
325
+ * Don't mix the order of options that have a sequence.
326
+ * @module
327
+ */
328
+ import { Field, RadioGroup } from '@tempoxyz/ds'
329
+
330
+ /** Renders the static preview, which the guideline card shows inert. */
331
+ export function Demo() {
332
+ return (
333
+ <RadioGroup.Root aria-label="Payout schedule" defaultValue="weekly">
334
+ {['Monthly', 'Daily', 'Weekly'].map((label) => (
335
+ <Field.Root key={label}>
336
+ <Field.Item>
337
+ <RadioGroup.Item value={label.toLowerCase()} />
338
+ <Field.Label>{label}</Field.Label>
339
+ </Field.Item>
340
+ </Field.Root>
341
+ ))}
342
+ </RadioGroup.Root>
343
+ )
344
+ }
345
+ ```
346
+
347
+ ### Keep Options in a Column
348
+
349
+ **Do:** Keep the default column, so each radio lines up with its label.
350
+
351
+ ```tsx
352
+ /**
353
+ * Do example for "Keep Options in a Column" on the RadioGroup page.
354
+ * Keep the default column, so each radio lines up with its label.
355
+ * @module
356
+ */
357
+ import { Field, RadioGroup } from '@tempoxyz/ds'
358
+
359
+ const options = ['Read only', 'Read and write', 'Admin']
360
+
361
+ /** Renders the static preview, which the guideline card shows inert. */
362
+ export function Demo() {
363
+ return (
364
+ <RadioGroup.Root aria-label="Key permissions" defaultValue="Read only">
365
+ {options.map((option) => (
366
+ <Field.Root key={option}>
367
+ <Field.Item>
368
+ <RadioGroup.Item value={option} />
369
+ <Field.Label>{option}</Field.Label>
370
+ </Field.Item>
371
+ </Field.Root>
372
+ ))}
373
+ </RadioGroup.Root>
374
+ )
375
+ }
376
+ ```
377
+
378
+ **Don't:** Lay radios out in a wrapping row.
379
+
380
+ ```tsx
381
+ /**
382
+ * Don't example for "Keep Options in a Column" on the RadioGroup page.
383
+ * Don't lay radios out in a wrapping row.
384
+ * @module
385
+ */
386
+ import { Field, RadioGroup, style } from '@tempoxyz/ds'
387
+
388
+ const options = ['Read only', 'Read and write', 'Admin']
389
+
390
+ /** Renders the static preview, which the guideline card shows inert. */
391
+ export function Demo() {
392
+ return (
393
+ <RadioGroup.Root aria-label="Key permissions" defaultValue="Read only" {...styles.row()}>
394
+ {options.map((option) => (
395
+ <Field.Root key={option}>
396
+ <Field.Item>
397
+ <RadioGroup.Item value={option} />
398
+ <Field.Label>{option}</Field.Label>
399
+ </Field.Item>
400
+ </Field.Root>
401
+ ))}
402
+ </RadioGroup.Root>
403
+ )
404
+ }
405
+
406
+ namespace styles {
407
+ export const row = style({ columnGap: '12', flexDirection: 'row', flexWrap: 'wrap', rowGap: '8' })
408
+ }
409
+ ```
410
+
411
+ ### Label Every Option
412
+
413
+ Set `disabled` on an item only with a nearby reason, such as Requires a verified domain.
414
+
415
+ **Do:** Wrap each radio in a `Field.Item` with a `Field.Label`, and add a `Field.Description` after it.
416
+
417
+ ```tsx
418
+ /**
419
+ * Do example for "Label Every Option" on the RadioGroup page.
420
+ * Wrap each radio in a `Field.Item` with a `Field.Label`, and add a `Field.Description` after it.
421
+ * @module
422
+ */
423
+ import { Field, RadioGroup } from '@tempoxyz/ds'
424
+
425
+ /** Renders the static preview, which the guideline card shows inert. */
426
+ export function Demo() {
427
+ return (
428
+ <RadioGroup.Root aria-label="Payout schedule" defaultValue="weekly">
429
+ <Field.Root>
430
+ <Field.Item>
431
+ <RadioGroup.Item value="weekly" />
432
+ <Field.Label>Weekly</Field.Label>
433
+ </Field.Item>
434
+ <Field.Description>Every Monday in USDC.</Field.Description>
435
+ </Field.Root>
436
+ <Field.Root>
437
+ <Field.Item>
438
+ <RadioGroup.Item value="monthly" />
439
+ <Field.Label>Monthly</Field.Label>
440
+ </Field.Item>
441
+ <Field.Description>On the first day of each month.</Field.Description>
442
+ </Field.Root>
443
+ </RadioGroup.Root>
444
+ )
445
+ }
446
+ ```
447
+
448
+ **Don't:** Place plain text beside a radio. A press on it selects nothing.
449
+
450
+ ```tsx
451
+ /**
452
+ * Don't example for "Label Every Option" on the RadioGroup page.
453
+ * Don't place plain text beside a radio. A press on it selects nothing.
454
+ * @module
455
+ */
456
+ import { RadioGroup, style } from '@tempoxyz/ds'
457
+
458
+ /** Renders the static preview, which the guideline card shows inert. */
459
+ export function Demo() {
460
+ return (
461
+ <RadioGroup.Root aria-label="Payout schedule" defaultValue="weekly">
462
+ <div {...styles.option()}>
463
+ <RadioGroup.Item value="weekly" />
464
+ <span>Weekly: every Monday in USDC.</span>
465
+ </div>
466
+ <div {...styles.option()}>
467
+ <RadioGroup.Item value="monthly" />
468
+ <span>Monthly: on the first day of each month.</span>
469
+ </div>
470
+ </RadioGroup.Root>
471
+ )
472
+ }
473
+
474
+ namespace styles {
475
+ export const option = style({ alignItems: 'center', display: 'flex', gap: '8' })
476
+ }
477
+ ```
478
+
479
+ ## Content
480
+
481
+ * **Option labels:** Keep them short and parallel, such as Read only and Read and write. Don't repeat words from the question in every option.
482
+
483
+ ## Accessibility
484
+
485
+ RadioGroup follows the [WAI-ARIA Authoring Practices radio group pattern](https://www.w3.org/WAI/ARIA/apg/patterns/radio/).
486
+
487
+ ### Semantics
488
+
489
+ * **RadioGroup.Root (radiogroup):** Groups the radios. Screen readers announce them as one set.
490
+ * **RadioGroup.Item (radio):** Announced with its checked state. A hidden native input submits the value under the group's `name`.
491
+ * **Disabled Items (aria-disabled):** A disabled item, or every item in a disabled group, stays visible but dimmed.
492
+ * **Checked State (dot):** The checked radio fills in and shows a mark, so selection does not rely on color alone.
493
+ * **Target Size (16, 20, 24px):** The hit area reaches 10px past each radio, and a label in `Field.Item` adds to it.
494
+
495
+ ### Keyboard
496
+
497
+ * **Enter the Group (Tab):** Moves focus into the group, onto the checked radio. The group is one Tab stop.
498
+ * **Leave the Group (Shift+Tab):** Moves focus out of the group.
499
+ * **Select the Next Radio (Down or Right Arrow):** Moves focus to the next radio and selects it, wrapping from last to first.
500
+ * **Select the Previous Radio (Up or Left Arrow):** Moves focus to the previous radio and selects it, wrapping from first to last.
501
+
502
+ Arrow keys skip disabled radios.
503
+
504
+ ### Your Responsibilities
505
+
506
+ * **Keep Changes Cheap (onValueChange):** Arrow keys select as they move. Avoid costly work on each change.
507
+
508
+ ## Related
509
+
510
+ [ChoiceGroup](/docs/components/choice-group)
511
+
512
+ Use for switching views and modes.
513
+
514
+ [Choicebox](/docs/components/choicebox)
515
+
516
+ Use for options with icons or descriptions.
517
+
518
+ [Select](/docs/components/select)
519
+
520
+ Use for longer lists.
521
+
522
+ [Checkbox](/docs/components/checkbox)
523
+
524
+ Use for independent choices.
525
+
526
+ [Fieldset](/docs/components/fieldset)
527
+
528
+ Use for naming a group with a legend.