@tempoxyz/ds 0.3.9 → 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 (128) 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/dist/web/components/OtpField.css +1 -1
  121. package/dist/web/components/OtpField.d.ts +1 -1
  122. package/dist/web/components/OtpField.d.ts.map +1 -1
  123. package/dist/web/components/OtpField.js +3 -4
  124. package/dist/web/components/OtpField.js.map +1 -1
  125. package/dist/web/components/OtpField.js.zyzz.json +1 -1
  126. package/dist/web/index.js.zyzz.json +1 -1
  127. package/package.json +1 -1
  128. package/src/web/components/OtpField.tsx +2 -24
@@ -0,0 +1,981 @@
1
+ # ListGroup
2
+
3
+ Stacks rows for details, pickers, checklists, and settings.
4
+
5
+ [View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/ListGroup.tsx)
6
+
7
+ Figma: [Content/List Group](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-1837), [Content/List Row](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-1666) (`ListGroup.Row`), [Content/Prompt Row](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-3419) (`ListGroup.Row`)
8
+
9
+ ## Overview
10
+
11
+ ListGroup lays out key-value facts, pickable items, and checklists, and groups them plain, boxed, or spaced. Plain rows line up with the content around them.
12
+
13
+ ## Examples
14
+
15
+ ### Basic
16
+
17
+ ```tsx
18
+ import { ListGroup, style } from '@tempoxyz/ds'
19
+
20
+ export const fill = true
21
+ export function Demo() {
22
+ return (
23
+ <ListGroup.Root {...styles.rows()}>
24
+ <ListGroup.Row>
25
+ <ListGroup.Label>Network</ListGroup.Label>
26
+ Tempo
27
+ </ListGroup.Row>
28
+ <ListGroup.Row>
29
+ <ListGroup.Label>Chain ID</ListGroup.Label>
30
+ 4217
31
+ </ListGroup.Row>
32
+ <ListGroup.Row>
33
+ <ListGroup.Label>Network fee</ListGroup.Label>
34
+ $0.0001
35
+ </ListGroup.Row>
36
+ <ListGroup.Row>
37
+ <ListGroup.Label>Status</ListGroup.Label>
38
+ Confirmed
39
+ </ListGroup.Row>
40
+ </ListGroup.Root>
41
+ )
42
+ }
43
+
44
+ namespace styles {
45
+ export const rows = style({ maxWidth: '24rem !custom' })
46
+ }
47
+ ```
48
+
49
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Basic.tsx)
50
+
51
+ ### Variant
52
+
53
+ Set `variant` on `ListGroup.Root` to `plain` (default), `boxed`, or `spaced`. Name the group with a `ListGroup.Heading` right before it.
54
+
55
+ ```tsx
56
+ import { ListGroup, style } from '@tempoxyz/ds'
57
+ import { Check } from '@tempoxyz/ds/icons'
58
+
59
+ export function Demo() {
60
+ return (
61
+ <div {...styles.grid()}>
62
+ <div>
63
+ <ListGroup.Heading>Fees</ListGroup.Heading>
64
+ <ListGroup.Root variant="boxed">
65
+ <ListGroup.Row>
66
+ <ListGroup.Label>Network fee</ListGroup.Label>
67
+ 0.001 USDC
68
+ </ListGroup.Row>
69
+ <ListGroup.Row>
70
+ <ListGroup.Label>Paid in</ListGroup.Label>
71
+ USDC
72
+ </ListGroup.Row>
73
+ <ListGroup.Row>
74
+ <ListGroup.Label>Arrives</ListGroup.Label>
75
+ Seconds
76
+ </ListGroup.Row>
77
+ </ListGroup.Root>
78
+ </div>
79
+ <div>
80
+ <ListGroup.Heading>Fees</ListGroup.Heading>
81
+ <ListGroup.Root variant="plain">
82
+ <ListGroup.Row>
83
+ <ListGroup.Label>Network fee</ListGroup.Label>
84
+ 0.001 USDC
85
+ </ListGroup.Row>
86
+ <ListGroup.Row>
87
+ <ListGroup.Label>Paid in</ListGroup.Label>
88
+ USDC
89
+ </ListGroup.Row>
90
+ <ListGroup.Row>
91
+ <ListGroup.Label>Arrives</ListGroup.Label>
92
+ Seconds
93
+ </ListGroup.Row>
94
+ </ListGroup.Root>
95
+ </div>
96
+ <div>
97
+ <ListGroup.Heading>Benefits</ListGroup.Heading>
98
+ <ListGroup.Root variant="spaced">
99
+ <ListGroup.Row>
100
+ <Check {...styles.check()} />
101
+ Never get locked out if you lose email access
102
+ </ListGroup.Row>
103
+ <ListGroup.Row>
104
+ <Check {...styles.check()} />
105
+ Approve with your device, no codes
106
+ </ListGroup.Row>
107
+ <ListGroup.Row>
108
+ <Check {...styles.check()} />
109
+ Stays on your device. Tempo never sees it
110
+ </ListGroup.Row>
111
+ </ListGroup.Root>
112
+ </div>
113
+ </div>
114
+ )
115
+ }
116
+
117
+ namespace styles {
118
+ export const check = style({ color: 'content.positive', flexShrink: '0 !custom', fontSize: '16' })
119
+
120
+ export const grid = style({
121
+ alignItems: 'start',
122
+ display: 'grid',
123
+ gap: '24',
124
+ gridTemplateColumns: 'repeat(auto-fill, minmax(20rem, 1fr)) !custom',
125
+ })
126
+ }
127
+ ```
128
+
129
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Variant.tsx)
130
+
131
+ ### Title and Description
132
+
133
+ Stack a `ListGroup.Title` over a `ListGroup.Description` in `ListGroup.Info`, after an [Avatar](/docs/components/avatar). The row grows to 64px.
134
+
135
+ ```tsx
136
+ import { Avatar, ListGroup, style } from '@tempoxyz/ds'
137
+ import { Wallet } from '@tempoxyz/ds/icons'
138
+
139
+ export function Demo() {
140
+ return (
141
+ <div {...styles.list()}>
142
+ <ListGroup.Heading>Recent</ListGroup.Heading>
143
+ <ListGroup.Root dividers={false}>
144
+ <ListGroup.Row action="navigation" onClick={() => {}}>
145
+ <Avatar size="32">
146
+ <Avatar.Fallback>MR</Avatar.Fallback>
147
+ </Avatar>
148
+ <ListGroup.Info>
149
+ <ListGroup.Title>Maya Rao</ListGroup.Title>
150
+ <ListGroup.Description>maya@studio.co</ListGroup.Description>
151
+ </ListGroup.Info>
152
+ </ListGroup.Row>
153
+ <ListGroup.Row action="navigation" onClick={() => {}}>
154
+ <Avatar size="32">
155
+ <Avatar.Fallback>AC</Avatar.Fallback>
156
+ </Avatar>
157
+ <ListGroup.Info>
158
+ <ListGroup.Title>Acme Co.</ListGroup.Title>
159
+ <ListGroup.Description>0x5d21…7e9a</ListGroup.Description>
160
+ </ListGroup.Info>
161
+ </ListGroup.Row>
162
+ <ListGroup.Row action="navigation" onClick={() => {}}>
163
+ <Avatar size="32">
164
+ <Avatar.Fallback>
165
+ <Wallet />
166
+ </Avatar.Fallback>
167
+ </Avatar>
168
+ <ListGroup.Info>
169
+ <ListGroup.Title>Savings wallet</ListGroup.Title>
170
+ <ListGroup.Description>0x19be…c044</ListGroup.Description>
171
+ </ListGroup.Info>
172
+ </ListGroup.Row>
173
+ </ListGroup.Root>
174
+ </div>
175
+ )
176
+ }
177
+
178
+ namespace styles {
179
+ export const list = style({ maxWidth: '25rem !custom', width: '100% !custom' })
180
+ }
181
+ ```
182
+
183
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Items.tsx)
184
+
185
+ ### Large Info
186
+
187
+ Set `size="large"` on `ListGroup.Info` for the row that introduces a boxed group. It sets a 16px title over a monospace address.
188
+
189
+ ```tsx
190
+ import { Button, formatHex, ListGroup, style } from '@tempoxyz/ds'
191
+
192
+ const address = '0x7a3f2c1e9b4d5a6f7c8e9d0a1b2c3d4e5fb8e091c2'
193
+
194
+ export function Demo() {
195
+ return (
196
+ <ListGroup.Root variant="boxed" {...styles.rows()}>
197
+ <ListGroup.Row>
198
+ <span aria-hidden="true" {...styles.art()} />
199
+ <ListGroup.Info size="large">
200
+ <ListGroup.Title>Tempo Wallet</ListGroup.Title>
201
+ <ListGroup.Description>{formatHex(address, { min: 6 })}</ListGroup.Description>
202
+ </ListGroup.Info>
203
+ <Button
204
+ aria-label="Copy wallet address"
205
+ onClick={() => void navigator.clipboard.writeText(address)}
206
+ size="xsmall"
207
+ variant="secondary"
208
+ >
209
+ Copy
210
+ </Button>
211
+ </ListGroup.Row>
212
+ <ListGroup.Row>
213
+ <ListGroup.Label>Network</ListGroup.Label>
214
+ Tempo Mainnet
215
+ </ListGroup.Row>
216
+ <ListGroup.Row>
217
+ <ListGroup.Label>Signed in as</ListGroup.Label>
218
+ piri@tempo.xyz
219
+ </ListGroup.Row>
220
+ <ListGroup.Row>
221
+ <ListGroup.Label>Signers</ListGroup.Label>1 · email
222
+ </ListGroup.Row>
223
+ </ListGroup.Root>
224
+ )
225
+ }
226
+
227
+ namespace styles {
228
+ export const art = style({
229
+ backgroundImage: 'linear-gradient(#1f1a14, #eda866, #261f1a) !custom',
230
+ borderRadius: 'xs',
231
+ flexShrink: '0 !custom',
232
+ height: '40',
233
+ width: '40',
234
+ })
235
+
236
+ export const rows = style({ maxWidth: '25rem !custom', width: '100% !custom' })
237
+ }
238
+ ```
239
+
240
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Entity.tsx)
241
+
242
+ ### Networks
243
+
244
+ Lead with a [NetworkLogo](/docs/components/network-logo), and list supported assets in [AssetChip](/docs/components/asset-chip) elements inside a `ListGroup.Chips` under the title.
245
+
246
+ ```tsx
247
+ import { AssetChip, ListGroup, NetworkLogo, style } from '@tempoxyz/ds'
248
+
249
+ const networks = [
250
+ { name: 'Base', network: 'base' },
251
+ { name: 'Ethereum', network: 'ethereum' },
252
+ { name: 'Polygon', network: 'polygon' },
253
+ ] as const
254
+
255
+ export function Demo() {
256
+ return (
257
+ <ListGroup.Root dividers={false} {...styles.rows()}>
258
+ {networks.map((entry) => (
259
+ <ListGroup.Row action="navigation" key={entry.network} onClick={() => {}}>
260
+ <NetworkLogo network={entry.network} size="large" />
261
+ <ListGroup.Info>
262
+ <ListGroup.Title>{entry.name}</ListGroup.Title>
263
+ <ListGroup.Chips>
264
+ <AssetChip>USDC</AssetChip>
265
+ <AssetChip>USDT</AssetChip>
266
+ </ListGroup.Chips>
267
+ </ListGroup.Info>
268
+ </ListGroup.Row>
269
+ ))}
270
+ </ListGroup.Root>
271
+ )
272
+ }
273
+
274
+ namespace styles {
275
+ export const rows = style({ maxWidth: '25rem !custom', width: '100% !custom' })
276
+ }
277
+ ```
278
+
279
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Networks.tsx)
280
+
281
+ ### Meter
282
+
283
+ Lead with an [AgentLogo](/docs/components/agent-logo) and end with a `ListGroup.Meter` to show how much of a limit each agent used. The bars line up from row to row.
284
+
285
+ ```tsx
286
+ /** Demonstrates ListGroup rows that show how much of a spending limit each agent used. @module */
287
+ import { AgentLogo, ListGroup, style } from '@tempoxyz/ds'
288
+
289
+ const agents = [
290
+ { agent: 'claude', limit: 10, name: 'Claude', spent: 6.6 },
291
+ { agent: 'codex', limit: 25, name: 'Codex', spent: 4 },
292
+ { agent: 'cursor', limit: 5, name: 'Cursor', spent: 4.75 },
293
+ ] as const
294
+
295
+ export function Demo() {
296
+ return (
297
+ <div {...styles.rows()}>
298
+ <ListGroup.Heading>Agents</ListGroup.Heading>
299
+ <ListGroup.Root>
300
+ {agents.map((entry) => (
301
+ <ListGroup.Row key={entry.agent}>
302
+ <AgentLogo agent={entry.agent} />
303
+ <ListGroup.Info>
304
+ <ListGroup.Title>{entry.name}</ListGroup.Title>
305
+ </ListGroup.Info>
306
+ <ListGroup.Meter
307
+ aria-label={`${entry.name} spending limit used`}
308
+ getAriaValueText={() => `${entry.spent.toFixed(2)} of ${entry.limit} USDC`}
309
+ max={entry.limit}
310
+ value={entry.spent}
311
+ >
312
+ {entry.spent.toFixed(2)} USDC
313
+ </ListGroup.Meter>
314
+ </ListGroup.Row>
315
+ ))}
316
+ </ListGroup.Root>
317
+ </div>
318
+ )
319
+ }
320
+
321
+ namespace styles {
322
+ export const rows = style({ maxWidth: '25rem !custom', width: '100% !custom' })
323
+ }
324
+ ```
325
+
326
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Meter.tsx)
327
+
328
+ ### Stacked Value
329
+
330
+ Put a `ListGroup.Description` above the value in `ListGroup.Info` for a single fact.
331
+
332
+ ```tsx
333
+ import { ListGroup, style } from '@tempoxyz/ds'
334
+
335
+ export function Demo() {
336
+ return (
337
+ <ListGroup.Root variant="spaced" {...styles.rows()}>
338
+ <ListGroup.Row>
339
+ <ListGroup.Info>
340
+ <ListGroup.Description>Signing with</ListGroup.Description>
341
+ This device
342
+ </ListGroup.Info>
343
+ </ListGroup.Row>
344
+ </ListGroup.Root>
345
+ )
346
+ }
347
+
348
+ namespace styles {
349
+ export const rows = style({ maxWidth: '25rem !custom', width: '100% !custom' })
350
+ }
351
+ ```
352
+
353
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Fact.tsx)
354
+
355
+ ### Action
356
+
357
+ Set `action="navigation"` on a `ListGroup.Row` that opens something, which adds a chevron. Pass `render` to make it a link, or `onClick` to make it a button.
358
+
359
+ ```tsx
360
+ import { ListGroup, style } from '@tempoxyz/ds'
361
+
362
+ export function Demo() {
363
+ return (
364
+ <ListGroup.Root {...styles.rows()}>
365
+ <ListGroup.Row action="navigation" render={<a href="#action" />}>
366
+ Activity
367
+ </ListGroup.Row>
368
+ <ListGroup.Row action="navigation" render={<a href="#action" />}>
369
+ Access keys
370
+ </ListGroup.Row>
371
+ <ListGroup.Row onClick={() => {}}>Sign out</ListGroup.Row>
372
+ </ListGroup.Root>
373
+ )
374
+ }
375
+
376
+ namespace styles {
377
+ export const rows = style({ maxWidth: '24rem !custom', width: '100% !custom' })
378
+ }
379
+ ```
380
+
381
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Action.tsx)
382
+
383
+ ### Radio
384
+
385
+ Give each row a `value` inside a `ListGroup.RadioGroup` to pick one option. Each row is a bordered radio. Set `disabled` on a row to keep it visible but unavailable.
386
+
387
+ ```tsx
388
+ /** Demonstrates ListGroup rows that pick one account in a ListGroup.RadioGroup. @module */
389
+ import { Avatar, ListGroup, style } from '@tempoxyz/ds'
390
+
391
+ const accounts = [
392
+ { detail: 'maya@studio.co', disabled: false, initials: 'MR', name: 'Maya Rao', value: 'maya' },
393
+ { detail: 'ops@acme.co', disabled: false, initials: 'AC', name: 'Acme Co.', value: 'acme' },
394
+ {
395
+ detail: 'Signed out',
396
+ disabled: true,
397
+ initials: 'SW',
398
+ name: 'Savings wallet',
399
+ value: 'savings',
400
+ },
401
+ ] as const
402
+
403
+ export function Demo() {
404
+ return (
405
+ <ListGroup.RadioGroup aria-label="Account" defaultValue="maya" {...styles.rows()}>
406
+ {accounts.map((entry) => (
407
+ <ListGroup.Row disabled={entry.disabled} key={entry.value} value={entry.value}>
408
+ <Avatar size="32">
409
+ <Avatar.Fallback>{entry.initials}</Avatar.Fallback>
410
+ </Avatar>
411
+ <ListGroup.Info>
412
+ <ListGroup.Title>{entry.name}</ListGroup.Title>
413
+ <ListGroup.Description>{entry.detail}</ListGroup.Description>
414
+ </ListGroup.Info>
415
+ </ListGroup.Row>
416
+ ))}
417
+ </ListGroup.RadioGroup>
418
+ )
419
+ }
420
+
421
+ namespace styles {
422
+ export const rows = style({ maxWidth: '25rem !custom', width: '100% !custom' })
423
+ }
424
+ ```
425
+
426
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Radio.tsx)
427
+
428
+ ### Option
429
+
430
+ Set `variant="option"` on a `ListGroup.RadioGroup` for the options of a picker. The selected row fills, without a border or a ring.
431
+
432
+ ```tsx
433
+ /** Demonstrates ListGroup option rows that pick one account in a picker. @module */
434
+ import { Avatar, ListGroup, style } from '@tempoxyz/ds'
435
+
436
+ const accounts = [
437
+ { detail: 'maya@studio.co', initials: 'MR', name: 'Maya Rao', value: 'maya' },
438
+ { detail: 'ops@acme.co', initials: 'AC', name: 'Acme Co.', value: 'acme' },
439
+ ] as const
440
+
441
+ export function Demo() {
442
+ return (
443
+ <ListGroup.RadioGroup
444
+ aria-label="Account"
445
+ defaultValue="maya"
446
+ variant="option"
447
+ {...styles.rows()}
448
+ >
449
+ {accounts.map((entry) => (
450
+ <ListGroup.Row key={entry.value} value={entry.value}>
451
+ <Avatar size="32">
452
+ <Avatar.Fallback>{entry.initials}</Avatar.Fallback>
453
+ </Avatar>
454
+ <ListGroup.Info>
455
+ <ListGroup.Title>{entry.name}</ListGroup.Title>
456
+ <ListGroup.Description>{entry.detail}</ListGroup.Description>
457
+ </ListGroup.Info>
458
+ </ListGroup.Row>
459
+ ))}
460
+ </ListGroup.RadioGroup>
461
+ )
462
+ }
463
+
464
+ namespace styles {
465
+ export const rows = style({ maxWidth: '25rem !custom', width: '100% !custom' })
466
+ }
467
+ ```
468
+
469
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Option.tsx)
470
+
471
+ ### Dividers
472
+
473
+ A list group draws a divider above every row after the first. Set `dividers={false}` on `ListGroup.Root` to drop them.
474
+
475
+ ```tsx
476
+ import { ListGroup, style } from '@tempoxyz/ds'
477
+
478
+ export function Demo() {
479
+ return (
480
+ <div {...styles.grid()}>
481
+ <ListGroup.Root>
482
+ <ListGroup.Row>
483
+ <ListGroup.Label>Network</ListGroup.Label>
484
+ Tempo
485
+ </ListGroup.Row>
486
+ <ListGroup.Row>
487
+ <ListGroup.Label>Chain ID</ListGroup.Label>
488
+ 4217
489
+ </ListGroup.Row>
490
+ </ListGroup.Root>
491
+ <ListGroup.Root dividers={false}>
492
+ <ListGroup.Row>
493
+ <ListGroup.Label>TokenLogo</ListGroup.Label>
494
+ USDT · TRC-20
495
+ </ListGroup.Row>
496
+ <ListGroup.Row>
497
+ <ListGroup.Label>Rate</ListGroup.Label>1 USDT = 1 USD
498
+ </ListGroup.Row>
499
+ </ListGroup.Root>
500
+ </div>
501
+ )
502
+ }
503
+
504
+ namespace styles {
505
+ export const grid = style({
506
+ alignItems: 'start',
507
+ display: 'grid',
508
+ gap: '24',
509
+ gridTemplateColumns: 'repeat(auto-fill, minmax(20rem, 1fr)) !custom',
510
+ })
511
+ }
512
+ ```
513
+
514
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/ListGroup/Dividers.tsx)
515
+
516
+ ## Anatomy
517
+
518
+ Import the component and assemble its parts.
519
+
520
+ ```tsx
521
+ import { ListGroup } from '@tempoxyz/ds'
522
+
523
+ <ListGroup.Heading />
524
+ <ListGroup.Root>
525
+ <ListGroup.Row>
526
+ <ListGroup.Icon />
527
+ <ListGroup.Label />
528
+ </ListGroup.Row>
529
+ <ListGroup.Row>
530
+ <ListGroup.Info>
531
+ <ListGroup.Title />
532
+ <ListGroup.Description />
533
+ <ListGroup.Chips />
534
+ </ListGroup.Info>
535
+ <ListGroup.Meter />
536
+ </ListGroup.Row>
537
+ </ListGroup.Root>
538
+ <ListGroup.RadioGroup>
539
+ <ListGroup.Row />
540
+ </ListGroup.RadioGroup>
541
+ <ListGroup>
542
+ <ListGroup.Row />
543
+ </ListGroup>
544
+ ```
545
+
546
+ ## Guidelines
547
+
548
+ ### When to Use
549
+
550
+ * Show the facts a person checks as label and value pairs, such as the amount, fee, and recipient of a payment.
551
+ * Let a person pick one contact, wallet, network, or vault from a short list.
552
+ * Show how much of a spending limit each agent used, with `ListGroup.Meter`.
553
+ * List up to three benefits in an onboarding step with `variant="spaced"`.
554
+ * List settings or pages to open, such as Access keys and Connected apps.
555
+
556
+ ### When Not to Use
557
+
558
+ * Use [Table](/docs/components/table) to compare many records across the same fields.
559
+ * Use [Accordion](/docs/components/accordion) to reveal details under a heading, such as a fee breakdown or help answers.
560
+ * Use [Menu](/docs/components/menu) for a list of actions that opens from a button.
561
+ * Use [SidebarMenu](/docs/components/sidebar-menu) for the main navigation of an app.
562
+ * Use [Field](/docs/components/field) with form controls when people edit the values.
563
+ * Use [RadioGroup](/docs/components/radio-group) to pick one short text option in a form, without an avatar or a description.
564
+
565
+ ### Pick a Variant by What the Group Holds
566
+
567
+ * **Boxed (boxed):** For reviews and receipts: what goes out, what comes in, fees, and key changes.
568
+ * **Plain (plain):** For pickers and recent contacts, where a person taps a row to choose it.
569
+ * **Spaced (spaced):** For checklists, benefits, and short facts that need no dividers between them.
570
+
571
+ ### Use One Boxed Group per Topic
572
+
573
+ Give each topic its own boxed group, and name it by what it holds, not by the screen it sits on.
574
+
575
+ **Do:** Name each group by its contents, such as Assets out and Fees.
576
+
577
+ ```tsx
578
+ <div style={{ display: 'grid', gap: 16, width: 280 }}>
579
+ <div>
580
+ <ListGroup.Heading>Assets out</ListGroup.Heading>
581
+ <ListGroup.Root variant="boxed">
582
+ <ListGroup.Row>
583
+ <ListGroup.Label>Amount</ListGroup.Label>
584
+ 120.00 USDC
585
+ </ListGroup.Row>
586
+ <ListGroup.Row>
587
+ <ListGroup.Label>To</ListGroup.Label>
588
+ Maya Rao
589
+ </ListGroup.Row>
590
+ </ListGroup.Root>
591
+ </div>
592
+ <div>
593
+ <ListGroup.Heading>Fees</ListGroup.Heading>
594
+ <ListGroup.Root variant="boxed">
595
+ <ListGroup.Row>
596
+ <ListGroup.Label>Network fee</ListGroup.Label>
597
+ 0.001 USDC
598
+ </ListGroup.Row>
599
+ </ListGroup.Root>
600
+ </div>
601
+ </div>
602
+ ```
603
+
604
+ **Don't:** Mix topics in one group named after the screen.
605
+
606
+ ```tsx
607
+ <div style={{ width: 280 }}>
608
+ <ListGroup.Heading>Review send</ListGroup.Heading>
609
+ <ListGroup.Root variant="boxed">
610
+ <ListGroup.Row>
611
+ <ListGroup.Label>Amount</ListGroup.Label>
612
+ 120.00 USDC
613
+ </ListGroup.Row>
614
+ <ListGroup.Row>
615
+ <ListGroup.Label>To</ListGroup.Label>
616
+ Maya Rao
617
+ </ListGroup.Row>
618
+ <ListGroup.Row>
619
+ <ListGroup.Label>Network fee</ListGroup.Label>
620
+ 0.001 USDC
621
+ </ListGroup.Row>
622
+ </ListGroup.Root>
623
+ </div>
624
+ ```
625
+
626
+ ### Put the Label First
627
+
628
+ `ListGroup.Label` keeps its width and pushes the value to the end of the row. Format values with [Amount](/docs/components/amount), [Hex](/docs/components/hex), and [Badge](/docs/components/badge).
629
+
630
+ **Do:** Pair each `ListGroup.Label` with a formatted value, such as an `Amount` or a `Hex`.
631
+
632
+ ```tsx
633
+ /**
634
+ * Do example for "Put the Label First" on the ListGroup page.
635
+ * Pair each `ListGroup.Label` with a formatted value, such as an `Amount` or a `Hex`.
636
+ * @module
637
+ */
638
+ import { Amount, Badge, Hex, ListGroup, style } from '@tempoxyz/ds'
639
+
640
+ /** Renders the static preview, which the guideline card shows inert. */
641
+ export function Demo() {
642
+ return (
643
+ <ListGroup {...styles.rows()}>
644
+ <ListGroup.Row>
645
+ <ListGroup.Label>Amount</ListGroup.Label>
646
+ <Amount symbol="USDC" value="48.50" />
647
+ </ListGroup.Row>
648
+ <ListGroup.Row>
649
+ <ListGroup.Label>Network fee</ListGroup.Label>
650
+ <Amount symbol="USDC" value="0.0004" />
651
+ </ListGroup.Row>
652
+ <ListGroup.Row>
653
+ <ListGroup.Label>Recipient</ListGroup.Label>
654
+ <span {...styles.value()}>
655
+ <Hex value="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" />
656
+ </span>
657
+ </ListGroup.Row>
658
+ <ListGroup.Row>
659
+ <ListGroup.Label>Status</ListGroup.Label>
660
+ <Badge>Confirmed</Badge>
661
+ </ListGroup.Row>
662
+ </ListGroup>
663
+ )
664
+ }
665
+
666
+ namespace styles {
667
+ export const rows = style({ width: '100% !custom' })
668
+
669
+ // Hex shrinks to its container, so the cap goes on a wrapper.
670
+ export const value = style({ display: 'inline-flex', maxWidth: '10rem !custom', minWidth: '0' })
671
+ }
672
+ ```
673
+
674
+ **Don't:** Use a `Table` with Field and Value columns for one record.
675
+
676
+ ```tsx
677
+ /**
678
+ * Don't example for "Put the Label First" on the ListGroup page.
679
+ * Don't use a `Table` with Field and Value columns for one record.
680
+ * @module
681
+ */
682
+ import { Table } from '@tempoxyz/ds'
683
+
684
+ const fields = [
685
+ ['Amount', '48.50 USDC'],
686
+ ['Network fee', '0.0004 USDC'],
687
+ ['Recipient', '0xd8dA…6045'],
688
+ ['Status', 'Confirmed'],
689
+ ] as const
690
+
691
+ /** Renders the static preview, which the guideline card shows inert. */
692
+ export function Demo() {
693
+ return (
694
+ <Table size="small">
695
+ <Table.Head>
696
+ <Table.Row>
697
+ <Table.HeaderCell>Field</Table.HeaderCell>
698
+ <Table.HeaderCell>Value</Table.HeaderCell>
699
+ </Table.Row>
700
+ </Table.Head>
701
+ <Table.Body>
702
+ {fields.map(([field, value]) => (
703
+ <Table.Row key={field}>
704
+ <Table.Cell>{field}</Table.Cell>
705
+ <Table.Cell>{value}</Table.Cell>
706
+ </Table.Row>
707
+ ))}
708
+ </Table.Body>
709
+ </Table>
710
+ )
711
+ }
712
+ ```
713
+
714
+ ### Shorten Addresses in Rows
715
+
716
+ A [Hex](/docs/components/hex) fills the space beside its label, so a long address crowds the label and ends short of the other values. Wrap it in an element capped at about `10rem`, and add `copy`. Before a payment, show the full recipient outside the rows, as the [Hex](/docs/components/hex) page shows.
717
+
718
+ **Do:** Cap the width of a `Hex` so it ends with the other values.
719
+
720
+ ```tsx
721
+ /**
722
+ * Do example for "Shorten Addresses in ListGroup" on the ListGroup page.
723
+ * Cap the width of a `Hex` so it ends with the other values.
724
+ * @module
725
+ */
726
+ import { Amount, Hex, ListGroup, style } from '@tempoxyz/ds'
727
+
728
+ /** Renders the static preview, which the guideline card shows inert. */
729
+ export function Demo() {
730
+ return (
731
+ <ListGroup {...styles.rows()}>
732
+ <ListGroup.Row>
733
+ <ListGroup.Label>Amount</ListGroup.Label>
734
+ <Amount symbol="USDC" value="48.50" />
735
+ </ListGroup.Row>
736
+ <ListGroup.Row>
737
+ <ListGroup.Label>Recipient</ListGroup.Label>
738
+ <span {...styles.value()}>
739
+ <Hex copy value="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" />
740
+ </span>
741
+ </ListGroup.Row>
742
+ </ListGroup>
743
+ )
744
+ }
745
+
746
+ namespace styles {
747
+ export const rows = style({ width: '100% !custom' })
748
+
749
+ // Hex shrinks to its container, so the cap goes on a wrapper.
750
+ export const value = style({ display: 'inline-flex', maxWidth: '10rem !custom', minWidth: '0' })
751
+ }
752
+ ```
753
+
754
+ **Don't:** Let a `Hex` fill the row up to its label.
755
+
756
+ ```tsx
757
+ /**
758
+ * Don't example for "Shorten Addresses in ListGroup" on the ListGroup page.
759
+ * Don't let a `Hex` fill the row up to its label.
760
+ * @module
761
+ */
762
+ import { Amount, Hex, ListGroup, style } from '@tempoxyz/ds'
763
+
764
+ /** Renders the static preview, which the guideline card shows inert. */
765
+ export function Demo() {
766
+ return (
767
+ <ListGroup {...styles.rows()}>
768
+ <ListGroup.Row>
769
+ <ListGroup.Label>Amount</ListGroup.Label>
770
+ <Amount symbol="USDC" value="48.50" />
771
+ </ListGroup.Row>
772
+ <ListGroup.Row>
773
+ <ListGroup.Label>Recipient</ListGroup.Label>
774
+ <Hex copy value="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" />
775
+ </ListGroup.Row>
776
+ </ListGroup>
777
+ )
778
+ }
779
+
780
+ namespace styles {
781
+ export const rows = style({ width: '100% !custom' })
782
+ }
783
+ ```
784
+
785
+ ### Pick an Action by What the Row Does
786
+
787
+ * **None (none):** For read-only details, such as Chain ID. With `onClick`, the row becomes a button.
788
+ * **Navigation (navigation):** Opens another page and shows a trailing chevron. Renders a button, or a link through `render`.
789
+ * **Copy (Button):** Copies a value. Keep the row static, which fills on hover, and end it with an `xsmall` [Button](/docs/components/button).
790
+
791
+ ### Keep Pressable Rows Free of Other Controls
792
+
793
+ A pressable row is one button or link. If a row holds its own control, such as a [Switch](/docs/components/switch), leave `action` and `onClick` off and place the control at the end. The row fills on hover, and the control stays the only target.
794
+
795
+ **Do:** Place the control at the end of a static row.
796
+
797
+ ```tsx
798
+ /**
799
+ * Do example for "Keep Pressable ListGroup Free of Other Controls" on the ListGroup page.
800
+ * Place the control at the end of a static row.
801
+ * @module
802
+ */
803
+ import { ListGroup, style, Switch } from '@tempoxyz/ds'
804
+
805
+ /** Renders the static preview, which the guideline card shows inert. */
806
+ export function Demo() {
807
+ return (
808
+ <ListGroup {...styles.rows()}>
809
+ <ListGroup.Row action="navigation">Access keys</ListGroup.Row>
810
+ <ListGroup.Row>
811
+ <ListGroup.Label>Payment emails</ListGroup.Label>
812
+ <Switch aria-label="Payment emails" defaultChecked />
813
+ </ListGroup.Row>
814
+ </ListGroup>
815
+ )
816
+ }
817
+
818
+ namespace styles {
819
+ export const rows = style({ width: '100% !custom' })
820
+ }
821
+ ```
822
+
823
+ **Don't:** Nest a control in a row with an `action`.
824
+
825
+ ```tsx
826
+ /**
827
+ * Don't example for "Keep Pressable ListGroup Free of Other Controls" on the ListGroup page.
828
+ * Don't nest a control in a row with an `action`.
829
+ * @module
830
+ */
831
+ import { ListGroup, style, Switch } from '@tempoxyz/ds'
832
+
833
+ /** Renders the static preview, which the guideline card shows inert. */
834
+ export function Demo() {
835
+ return (
836
+ <ListGroup {...styles.rows()}>
837
+ <ListGroup.Row action="navigation">Access keys</ListGroup.Row>
838
+ <ListGroup.Row action="navigation">
839
+ <ListGroup.Label>Payment emails</ListGroup.Label>
840
+ <Switch aria-label="Payment emails" defaultChecked />
841
+ </ListGroup.Row>
842
+ </ListGroup>
843
+ )
844
+ }
845
+
846
+ namespace styles {
847
+ export const rows = style({ width: '100% !custom' })
848
+ }
849
+ ```
850
+
851
+ ## Content
852
+
853
+ * **Row text:** Name the destination or setting, such as Access keys, not Manage. The chevron is hidden from screen readers.
854
+ * **Labels:** Short nouns without colons, such as Network fee.
855
+ * **Descriptions:** One line of current state in `ListGroup.Description`, such as 2 active keys.
856
+ * **Group headings:** Name what the group holds, such as Fees, Assets out, or Contacts.
857
+ * **Checklists:** Three benefits at most, each a short line, such as Approve with your device, no codes.
858
+ * **Order:** Keep the same labels in the same order wherever a record appears, such as a review and its receipt.
859
+
860
+ **Do:** Name the page each row opens.
861
+
862
+ ```tsx
863
+ /**
864
+ * Do example for the Content section on the ListGroup page.
865
+ * Name the page each row opens.
866
+ * @module
867
+ */
868
+ import { ListGroup, style } from '@tempoxyz/ds'
869
+
870
+ /** Renders the static preview, which the guideline card shows inert. */
871
+ export function Demo() {
872
+ return (
873
+ <ListGroup {...styles.rows()}>
874
+ <ListGroup.Row action="navigation">Access keys</ListGroup.Row>
875
+ <ListGroup.Row action="navigation">Recovery methods</ListGroup.Row>
876
+ <ListGroup.Row action="navigation">Connected apps</ListGroup.Row>
877
+ </ListGroup>
878
+ )
879
+ }
880
+
881
+ namespace styles {
882
+ export const rows = style({ width: '100% !custom' })
883
+ }
884
+ ```
885
+
886
+ **Don't:** Use vague labels such as Manage, View more, or Other.
887
+
888
+ ```tsx
889
+ /**
890
+ * Don't example for the Content section on the ListGroup page.
891
+ * Don't use vague labels such as Manage, View more, or Other.
892
+ * @module
893
+ */
894
+ import { ListGroup, style } from '@tempoxyz/ds'
895
+
896
+ /** Renders the static preview, which the guideline card shows inert. */
897
+ export function Demo() {
898
+ return (
899
+ <ListGroup {...styles.rows()}>
900
+ <ListGroup.Row action="navigation">Manage</ListGroup.Row>
901
+ <ListGroup.Row action="navigation">View more</ListGroup.Row>
902
+ <ListGroup.Row action="navigation">Other</ListGroup.Row>
903
+ </ListGroup>
904
+ )
905
+ }
906
+
907
+ namespace styles {
908
+ export const rows = style({ width: '100% !custom' })
909
+ }
910
+ ```
911
+
912
+ ## Accessibility
913
+
914
+ ### Semantics
915
+
916
+ * **ListGroup.Root (div):** Adds no role. The rows read as plain content.
917
+ * **Row Elements (button):** Pressable rows render a `button`, or the `render` element.
918
+ * **Chevron (aria-hidden):** The navigation chevron is hidden from screen readers.
919
+ * **ListGroup.Label (span):** A key-value row reads as plain text, such as "Network Tempo".
920
+ * **Radio Rows (radio):** A row with a `value` reads as a radio, named by its text.
921
+ * **ListGroup.Meter (meter):** Reads its name and `aria-valuetext`, not the value shown.
922
+ * **Disabled Rows (disabled):** Sets `disabled` or `aria-disabled`, and dims the row.
923
+
924
+ ### Keyboard
925
+
926
+ * **Move Between Rows (Tab):** Moves focus to the next button, link, or radio group.
927
+ * **Activate a Row (Enter):** Activates a button or link row.
928
+ * **Press a Button Row (Space):** Activates a button row.
929
+ * **Pick a Radio Row (Down Arrow):** Moves focus and selection to the next radio row.
930
+
931
+ ### Your Responsibilities
932
+
933
+ * **Render Links for Navigation (render):** Use a real link through `render`, so middle-click, the context menu, and screen reader link lists work.
934
+ * **Head Each Group (ListGroup.Heading):** ListGroup adds no role or name. Pass a heading element to `ListGroup.Heading` through `render`.
935
+ * **Name Radio Groups and Meters (aria-label):** Name each `ListGroup.RadioGroup` and `ListGroup.Meter`, and pass `getAriaValueText` to a meter.
936
+
937
+ ## Related
938
+
939
+ [Table](/docs/components/table)
940
+
941
+ Use for comparing many records.
942
+
943
+ [Accordion](/docs/components/accordion)
944
+
945
+ Use for collapsible sections of content.
946
+
947
+ [Surface](/docs/components/surface)
948
+
949
+ Use for grouping rows in a card.
950
+
951
+ [Amount](/docs/components/amount)
952
+
953
+ Use for values.
954
+
955
+ [Hex](/docs/components/hex)
956
+
957
+ Use for values.
958
+
959
+ [Avatar](/docs/components/avatar)
960
+
961
+ Use for the leading image of a contact row.
962
+
963
+ [NetworkLogo](/docs/components/network-logo)
964
+
965
+ Use for the leading logo of a network row.
966
+
967
+ [AssetChip](/docs/components/asset-chip)
968
+
969
+ Use for the assets a network row supports.
970
+
971
+ [AgentLogo](/docs/components/agent-logo)
972
+
973
+ Use for the leading logo of an agent row.
974
+
975
+ [RadioGroup](/docs/components/radio-group)
976
+
977
+ Use for picking one short text option in a form.
978
+
979
+ [Tables and Lists](/docs/patterns/tables-and-lists)
980
+
981
+ Covers choosing a layout.