@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,1332 @@
1
+ # Dialog
2
+
3
+ Opens a modal popup over the page.
4
+
5
+ [View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/Dialog.tsx)
6
+
7
+ Figma: [Modal/Header](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-1876) (`Dialog.Header`), [Modal/Wallet Modal](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-1902) (`Dialog.Popup`)
8
+
9
+ ## Overview
10
+
11
+ A dialog is a modal popup over a dimmed backdrop. It stacks a header, a body and a footer of actions at one of three sizes. It traps focus and returns it to the trigger on close.
12
+
13
+ ## Examples
14
+
15
+ ### Basic
16
+
17
+ Set `closeButton` on `Dialog.Popup` to add a visible close control.
18
+
19
+ ```tsx
20
+ import { Button, ButtonGroup, Dialog, Field, TextInput } from '@tempoxyz/ds'
21
+
22
+ export function Demo() {
23
+ return (
24
+ <Dialog.Root>
25
+ <Dialog.Trigger render={<Button variant="secondary" />}>Rename wallet</Dialog.Trigger>
26
+ <Dialog.Popup closeButton>
27
+ <Dialog.Header>
28
+ <Dialog.Title>Rename wallet</Dialog.Title>
29
+ <Dialog.Description>The name is only visible to you.</Dialog.Description>
30
+ </Dialog.Header>
31
+ <Dialog.Body>
32
+ <Field.Root>
33
+ <Field.Label>Name</Field.Label>
34
+ <TextInput.Group>
35
+ <TextInput.Control defaultValue="Main wallet" />
36
+ </TextInput.Group>
37
+ </Field.Root>
38
+ </Dialog.Body>
39
+ <ButtonGroup>
40
+ <Dialog.Close render={<Button size="large" variant="secondary" />}>Cancel</Dialog.Close>
41
+ <Dialog.Close render={<Button size="large" />}>Save name</Dialog.Close>
42
+ </ButtonGroup>
43
+ </Dialog.Popup>
44
+ </Dialog.Root>
45
+ )
46
+ }
47
+ ```
48
+
49
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Basic.tsx)
50
+
51
+ ### Size
52
+
53
+ Set `size` on `Dialog.Popup` to `small`, `medium` (default) or `large`. The size sets the width, radius, padding and title size together.
54
+
55
+ ```tsx
56
+ import { Button, ButtonGroup, Dialog, Inline } from '@tempoxyz/ds'
57
+
58
+ export function Demo() {
59
+ return (
60
+ <Inline gap="12" wrap>
61
+ {(['small', 'medium', 'large'] as const).map((size) => (
62
+ <Dialog.Root key={size}>
63
+ <Dialog.Trigger render={<Button variant="secondary" />}>Open {size}</Dialog.Trigger>
64
+ <Dialog.Popup closeButton size={size}>
65
+ <Dialog.Header>
66
+ <Dialog.Title>Send USDC</Dialog.Title>
67
+ <Dialog.Description>Payments on Tempo settle in seconds.</Dialog.Description>
68
+ </Dialog.Header>
69
+ <ButtonGroup>
70
+ <Dialog.Close render={<Button size="large" variant="secondary" />}>
71
+ Cancel
72
+ </Dialog.Close>
73
+ <Button size="large">Continue</Button>
74
+ </ButtonGroup>
75
+ </Dialog.Popup>
76
+ </Dialog.Root>
77
+ ))}
78
+ </Inline>
79
+ )
80
+ }
81
+ ```
82
+
83
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Size.tsx)
84
+
85
+ ### Header Layout
86
+
87
+ Set `layout` on `Dialog.Header` to `inline` (default), `stacked`, `centered` or `hero`. A `Dialog.Nav` holds `Dialog.Back` and a [Stepper](/docs/components/stepper) above the title.
88
+
89
+ ```tsx
90
+ import { Button, ButtonGroup, Dialog, IconTile, Inline, Stepper } from '@tempoxyz/ds'
91
+ import { Check } from '@tempoxyz/ds/icons'
92
+
93
+ export function Demo() {
94
+ return (
95
+ <Inline gap="12" wrap>
96
+ <Dialog.Root>
97
+ <Dialog.Trigger render={<Button variant="secondary" />}>Inline</Dialog.Trigger>
98
+ <Dialog.Popup closeButton>
99
+ <Dialog.Header layout="inline">
100
+ <Dialog.Title>Send USDC</Dialog.Title>
101
+ <Dialog.Description>Payments on Tempo settle in seconds.</Dialog.Description>
102
+ </Dialog.Header>
103
+ <ButtonGroup>
104
+ <Button size="large">Review</Button>
105
+ </ButtonGroup>
106
+ </Dialog.Popup>
107
+ </Dialog.Root>
108
+ <Dialog.Root>
109
+ <Dialog.Trigger render={<Button variant="secondary" />}>Stacked</Dialog.Trigger>
110
+ <Dialog.Popup closeButton>
111
+ <Dialog.Header layout="stacked">
112
+ <Dialog.Nav>
113
+ <Dialog.Back />
114
+ <Stepper.Root aria-label="Send USDC" count={4} value={2} />
115
+ </Dialog.Nav>
116
+ <Dialog.Title>Choose a recipient</Dialog.Title>
117
+ <Dialog.Description>Step 2 of 4</Dialog.Description>
118
+ </Dialog.Header>
119
+ <ButtonGroup>
120
+ <Button size="large">Continue</Button>
121
+ </ButtonGroup>
122
+ </Dialog.Popup>
123
+ </Dialog.Root>
124
+ <Dialog.Root>
125
+ <Dialog.Trigger render={<Button variant="secondary" />}>Centered</Dialog.Trigger>
126
+ <Dialog.Popup closeButton size="small">
127
+ <Dialog.Header layout="centered">
128
+ <Dialog.Nav />
129
+ <IconTile intent="positive">
130
+ <Check />
131
+ </IconTile>
132
+ <Dialog.Title>Passkey ready</Dialog.Title>
133
+ <Dialog.Description>You can approve payments with Touch ID.</Dialog.Description>
134
+ </Dialog.Header>
135
+ <ButtonGroup>
136
+ <Dialog.Close render={<Button size="large" />}>Got it</Dialog.Close>
137
+ </ButtonGroup>
138
+ </Dialog.Popup>
139
+ </Dialog.Root>
140
+ </Inline>
141
+ )
142
+ }
143
+ ```
144
+
145
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Layout.tsx)
146
+
147
+ ## Use Cases
148
+
149
+ Each screen pairs a size and a header layout with parts from the library.
150
+
151
+ ### Confirmation
152
+
153
+ Confirms a destructive or irreversible action in an [AlertDialog](/docs/components/alert-dialog).
154
+
155
+ ```tsx
156
+ import { Alert, AlertDialog, Button, ButtonGroup } from '@tempoxyz/ds'
157
+
158
+ export function Demo() {
159
+ return (
160
+ <AlertDialog.Root>
161
+ <AlertDialog.Trigger render={<Button />}>Revoke all connections</AlertDialog.Trigger>
162
+ <AlertDialog.Popup closeButton>
163
+ <AlertDialog.Header>
164
+ <AlertDialog.Title>Revoke all connections?</AlertDialog.Title>
165
+ <AlertDialog.Description>
166
+ Apps and agents lose access to Personal right away.
167
+ </AlertDialog.Description>
168
+ </AlertDialog.Header>
169
+ <AlertDialog.Body>
170
+ <Alert intent="negative">
171
+ <Alert.Icon />
172
+ <Alert.Content>
173
+ <Alert.Title>Can’t be undone</Alert.Title>4 apps and 1 agent will be disconnected.
174
+ </Alert.Content>
175
+ </Alert>
176
+ </AlertDialog.Body>
177
+ <ButtonGroup>
178
+ <AlertDialog.Close render={<Button size="large" variant="secondary" />}>
179
+ Cancel
180
+ </AlertDialog.Close>
181
+ <AlertDialog.Close render={<Button size="large" />}>Revoke all</AlertDialog.Close>
182
+ </ButtonGroup>
183
+ </AlertDialog.Popup>
184
+ </AlertDialog.Root>
185
+ )
186
+ }
187
+ ```
188
+
189
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Confirmation.tsx)
190
+
191
+ ### Acknowledgment
192
+
193
+ Reports that a change finished, with one way to move on.
194
+
195
+ ```tsx
196
+ import { Button, ButtonGroup, Dialog, IconTile } from '@tempoxyz/ds'
197
+ import { Check } from '@tempoxyz/ds/icons'
198
+
199
+ export function Demo() {
200
+ return (
201
+ <Dialog.Root>
202
+ <Dialog.Trigger render={<Button variant="secondary" />}>Add passkey</Dialog.Trigger>
203
+ <Dialog.Popup closeButton size="small">
204
+ <Dialog.Header layout="centered">
205
+ <Dialog.Nav />
206
+ <IconTile intent="positive">
207
+ <Check />
208
+ </IconTile>
209
+ <Dialog.Title>Passkey ready</Dialog.Title>
210
+ <Dialog.Description>
211
+ You can approve payments with Touch ID from now on.
212
+ </Dialog.Description>
213
+ </Dialog.Header>
214
+ <ButtonGroup>
215
+ <Dialog.Close render={<Button size="large" />}>Got it</Dialog.Close>
216
+ </ButtonGroup>
217
+ </Dialog.Popup>
218
+ </Dialog.Root>
219
+ )
220
+ }
221
+ ```
222
+
223
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Acknowledgment.tsx)
224
+
225
+ ### Form
226
+
227
+ Collects what a task needs, such as an amount and a recipient, before a review.
228
+
229
+ ```tsx
230
+ import {
231
+ Alert,
232
+ AmountInput,
233
+ Button,
234
+ ButtonGroup,
235
+ Dialog,
236
+ Field,
237
+ TextInput,
238
+ Token,
239
+ } from '@tempoxyz/ds'
240
+ import { Clipboard } from '@tempoxyz/ds/icons'
241
+
242
+ export function Demo() {
243
+ return (
244
+ <Dialog.Root>
245
+ <Dialog.Trigger render={<Button variant="secondary" />}>Send USDC</Dialog.Trigger>
246
+ <Dialog.Popup closeButton>
247
+ <Dialog.Header>
248
+ <Dialog.Title>Send USDC</Dialog.Title>
249
+ <Dialog.Description>Payments on Tempo settle in seconds.</Dialog.Description>
250
+ </Dialog.Header>
251
+ <Dialog.Body>
252
+ <TextInput.Group>
253
+ <TextInput.Control aria-label="Recipient" placeholder="Address, email or contact" />
254
+ <TextInput.Suffix>
255
+ <Clipboard />
256
+ </TextInput.Suffix>
257
+ </TextInput.Group>
258
+ <Field.Root>
259
+ <AmountInput.Root defaultValue="120.00">
260
+ <Field.Label>You send</Field.Label>
261
+ <AmountInput.Control />
262
+ <Token address="0x20c000000000000000000000b9537d11c60e8b50" symbol="USDC" />
263
+ <Field.Description>Balance 2,480.32</Field.Description>
264
+ </AmountInput.Root>
265
+ </Field.Root>
266
+ <Alert intent="warning">
267
+ <Alert.Icon />
268
+ <Alert.Content>
269
+ <Alert.Title>First time sending here</Alert.Title>
270
+ Check the first and last 4 characters with the recipient.
271
+ </Alert.Content>
272
+ </Alert>
273
+ </Dialog.Body>
274
+ <ButtonGroup>
275
+ <Button size="large">Review</Button>
276
+ </ButtonGroup>
277
+ </Dialog.Popup>
278
+ </Dialog.Root>
279
+ )
280
+ }
281
+ ```
282
+
283
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Form.tsx)
284
+
285
+ ### Onboarding
286
+
287
+ Introduces a feature and its benefits before setup.
288
+
289
+ ```tsx
290
+ import { Button, ButtonGroup, Dialog, IconTile, ListGroup, Stepper, style } from '@tempoxyz/ds'
291
+ import { Check, Passkey } from '@tempoxyz/ds/icons'
292
+
293
+ const benefits = [
294
+ 'Never get locked out if you lose email access',
295
+ 'Approve with your device, no codes',
296
+ 'Stays on your device. Tempo never sees it',
297
+ ]
298
+
299
+ export function Demo() {
300
+ return (
301
+ <Dialog.Root>
302
+ <Dialog.Trigger render={<Button variant="secondary" />}>Set up a passkey</Dialog.Trigger>
303
+ <Dialog.Popup closeButton>
304
+ <Dialog.Header layout="centered">
305
+ <Dialog.Nav>
306
+ <Stepper.Root aria-label="Passkey setup" count={4} value={1} />
307
+ </Dialog.Nav>
308
+ <IconTile>
309
+ <Passkey />
310
+ </IconTile>
311
+ <Dialog.Title>Add a passkey</Dialog.Title>
312
+ <Dialog.Description>A second way in that never needs a code.</Dialog.Description>
313
+ </Dialog.Header>
314
+ <Dialog.Body>
315
+ <ListGroup.Root variant="spaced">
316
+ {benefits.map((benefit) => (
317
+ <ListGroup.Row key={benefit}>
318
+ <Check {...styles.check()} />
319
+ {benefit}
320
+ </ListGroup.Row>
321
+ ))}
322
+ </ListGroup.Root>
323
+ </Dialog.Body>
324
+ <ButtonGroup orientation="vertical">
325
+ <Button size="large">Create passkey</Button>
326
+ <Dialog.Close render={<Button size="large" variant="tertiary" />}>Not now</Dialog.Close>
327
+ </ButtonGroup>
328
+ </Dialog.Popup>
329
+ </Dialog.Root>
330
+ )
331
+ }
332
+
333
+ namespace styles {
334
+ export const check = style({ color: 'content.positive', flexShrink: '0 !custom', fontSize: '16' })
335
+ }
336
+ ```
337
+
338
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Onboarding.tsx)
339
+
340
+ ### Picker
341
+
342
+ Searches a known set and closes on a row press, so it needs no footer.
343
+
344
+ ```tsx
345
+ import { Avatar, Button, Dialog, ListGroup, TextInput } from '@tempoxyz/ds'
346
+ import { Search, Wallet } from '@tempoxyz/ds/icons'
347
+ import { useState } from 'react'
348
+
349
+ const recipients = [
350
+ { detail: 'maya@studio.co', initials: 'MR', name: 'Maya Rao' },
351
+ { detail: '0x5d21…7e9a', initials: 'AC', name: 'Acme Co.' },
352
+ { detail: '0x19be…c044', initials: undefined, name: 'Savings wallet' },
353
+ ]
354
+
355
+ export function Demo() {
356
+ const [open, setOpen] = useState(false)
357
+
358
+ return (
359
+ <Dialog.Root onOpenChange={setOpen} open={open}>
360
+ <Dialog.Trigger render={<Button variant="secondary" />}>Choose a recipient</Dialog.Trigger>
361
+ <Dialog.Popup closeButton>
362
+ <Dialog.Header layout="stacked">
363
+ <Dialog.Nav>
364
+ <Dialog.Back onClick={() => setOpen(false)} />
365
+ </Dialog.Nav>
366
+ <Dialog.Title>Choose a recipient</Dialog.Title>
367
+ </Dialog.Header>
368
+ <Dialog.Body>
369
+ <TextInput.Group>
370
+ <TextInput.Prefix>
371
+ <Search />
372
+ </TextInput.Prefix>
373
+ <TextInput.Control
374
+ aria-label="Search recipients"
375
+ placeholder="Name, email or address"
376
+ />
377
+ </TextInput.Group>
378
+ <div>
379
+ <ListGroup.Heading>Recent</ListGroup.Heading>
380
+ <ListGroup.Root dividers={false}>
381
+ {recipients.map((recipient) => (
382
+ <ListGroup.Row
383
+ action="navigation"
384
+ key={recipient.name}
385
+ onClick={() => setOpen(false)}
386
+ >
387
+ <Avatar size="32">
388
+ <Avatar.Fallback>{recipient.initials ?? <Wallet />}</Avatar.Fallback>
389
+ </Avatar>
390
+ <ListGroup.Info>
391
+ <ListGroup.Title>{recipient.name}</ListGroup.Title>
392
+ <ListGroup.Description>{recipient.detail}</ListGroup.Description>
393
+ </ListGroup.Info>
394
+ </ListGroup.Row>
395
+ ))}
396
+ </ListGroup.Root>
397
+ </div>
398
+ </Dialog.Body>
399
+ </Dialog.Popup>
400
+ </Dialog.Root>
401
+ )
402
+ }
403
+ ```
404
+
405
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Picker.tsx)
406
+
407
+ ### Announcement
408
+
409
+ Shows a new feature once with a visual, and never blocks a task.
410
+
411
+ ```tsx
412
+ import { Button, ButtonGroup, Dialog, style } from '@tempoxyz/ds'
413
+
414
+ export function Demo() {
415
+ return (
416
+ <Dialog.Root>
417
+ <Dialog.Trigger render={<Button variant="secondary" />}>What’s new</Dialog.Trigger>
418
+ <Dialog.Popup closeButton>
419
+ <Dialog.Header>
420
+ <Dialog.Title>Earn on idle USDC</Dialog.Title>
421
+ <Dialog.Description>
422
+ Choose a vault and an amount. Nothing moves until you do.
423
+ </Dialog.Description>
424
+ </Dialog.Header>
425
+ <Dialog.Body>
426
+ <div aria-hidden="true" {...styles.visual()} />
427
+ </Dialog.Body>
428
+ <ButtonGroup orientation="vertical">
429
+ <Button size="large">See vaults</Button>
430
+ <Dialog.Close render={<Button size="large" variant="tertiary" />}>Not now</Dialog.Close>
431
+ </ButtonGroup>
432
+ </Dialog.Popup>
433
+ </Dialog.Root>
434
+ )
435
+ }
436
+
437
+ namespace styles {
438
+ // Stands in for a 2:1 feature image.
439
+ export const visual = style({
440
+ aspectRatio: '2 !custom',
441
+ backgroundImage: 'linear-gradient(#0a1217, #17384a) !custom',
442
+ borderRadius: 'sm',
443
+ flexShrink: '0 !custom',
444
+ })
445
+ }
446
+ ```
447
+
448
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Announcement.tsx)
449
+
450
+ ### Conversion
451
+
452
+ Moves value between networks or tokens and shows the route before a quote.
453
+
454
+ ```tsx
455
+ import { AmountInput, Button, ButtonGroup, Dialog, Field, ListGroup, Token } from '@tempoxyz/ds'
456
+
457
+ const usdc = '0x20c000000000000000000000b9537d11c60e8b50'
458
+
459
+ export function Demo() {
460
+ return (
461
+ <Dialog.Root>
462
+ <Dialog.Trigger render={<Button variant="secondary" />}>Move to Tempo</Dialog.Trigger>
463
+ <Dialog.Popup closeButton>
464
+ <Dialog.Header>
465
+ <Dialog.Title>Move to Tempo</Dialog.Title>
466
+ </Dialog.Header>
467
+ <Dialog.Body>
468
+ <Field.Root>
469
+ <AmountInput.Root defaultValue="2500.00">
470
+ <Field.Label>From · Ethereum</Field.Label>
471
+ <AmountInput.Control />
472
+ <Token address={usdc} symbol="USDC" />
473
+ <Field.Description>Balance 4,120.00</Field.Description>
474
+ </AmountInput.Root>
475
+ </Field.Root>
476
+ <Field.Root>
477
+ <AmountInput.Root defaultValue="2500.00">
478
+ <Field.Label>To · Tempo</Field.Label>
479
+ <AmountInput.Control readOnly />
480
+ <Token address={usdc} symbol="USDC" />
481
+ <Field.Description>Arrives in ~2 min</Field.Description>
482
+ </AmountInput.Root>
483
+ </Field.Root>
484
+ <ListGroup.Root variant="boxed">
485
+ <ListGroup.Row>
486
+ <ListGroup.Label>Rate</ListGroup.Label>1 USDC = 1 USDC
487
+ </ListGroup.Row>
488
+ <ListGroup.Row>
489
+ <ListGroup.Label>Fee</ListGroup.Label>
490
+ 0.42 USDC
491
+ </ListGroup.Row>
492
+ </ListGroup.Root>
493
+ </Dialog.Body>
494
+ <ButtonGroup>
495
+ <Button size="large">View quote</Button>
496
+ </ButtonGroup>
497
+ </Dialog.Popup>
498
+ </Dialog.Root>
499
+ )
500
+ }
501
+ ```
502
+
503
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Conversion.tsx)
504
+
505
+ ### Review
506
+
507
+ Lays out every asset, fee and key change before a signature, at the `large` size.
508
+
509
+ ```tsx
510
+ import { Button, ButtonGroup, Dialog, ListGroup, Stepper } from '@tempoxyz/ds'
511
+
512
+ const groups = [
513
+ {
514
+ rows: [
515
+ ['Amount', '120.00 USDC'],
516
+ ['To', 'Maya Rao'],
517
+ ],
518
+ title: 'Assets out',
519
+ },
520
+ {
521
+ rows: [
522
+ ['Network fee', '0.001 USDC'],
523
+ ['Paid in', 'USDC'],
524
+ ],
525
+ title: 'Fees',
526
+ },
527
+ {
528
+ rows: [
529
+ ['TokenLogo approvals', 'None'],
530
+ ['Signers', 'No change'],
531
+ ],
532
+ title: 'Approvals and key changes',
533
+ },
534
+ ]
535
+
536
+ export function Demo() {
537
+ return (
538
+ <Dialog.Root>
539
+ <Dialog.Trigger render={<Button variant="secondary" />}>Review send</Dialog.Trigger>
540
+ <Dialog.Popup closeButton size="large">
541
+ <Dialog.Header layout="stacked">
542
+ <Dialog.Nav>
543
+ <Dialog.Back />
544
+ <Stepper.Root aria-label="Send USDC" count={4} value={3} />
545
+ </Dialog.Nav>
546
+ <Dialog.Title>Review send</Dialog.Title>
547
+ <Dialog.Description>Check every line before you approve.</Dialog.Description>
548
+ </Dialog.Header>
549
+ <Dialog.Body>
550
+ {groups.map((group) => (
551
+ <div key={group.title}>
552
+ <ListGroup.Heading>{group.title}</ListGroup.Heading>
553
+ <ListGroup.Root variant="boxed">
554
+ {group.rows.map(([label, value]) => (
555
+ <ListGroup.Row key={label}>
556
+ <ListGroup.Label>{label}</ListGroup.Label>
557
+ {value}
558
+ </ListGroup.Row>
559
+ ))}
560
+ </ListGroup.Root>
561
+ </div>
562
+ ))}
563
+ </Dialog.Body>
564
+ <ButtonGroup>
565
+ <Button size="large">Confirm send</Button>
566
+ </ButtonGroup>
567
+ </Dialog.Popup>
568
+ </Dialog.Root>
569
+ )
570
+ }
571
+ ```
572
+
573
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Review.tsx)
574
+
575
+ ### Progress
576
+
577
+ Holds the person in place while a signer or the network responds.
578
+
579
+ ```tsx
580
+ import { Button, ButtonGroup, Dialog, Spinner, style } from '@tempoxyz/ds'
581
+
582
+ export function Demo() {
583
+ return (
584
+ <Dialog.Root>
585
+ <Dialog.Trigger render={<Button variant="secondary" />}>Sign in</Dialog.Trigger>
586
+ <Dialog.Popup closeButton size="small">
587
+ <Dialog.Header layout="centered">
588
+ <Dialog.Nav>
589
+ <Dialog.Back />
590
+ </Dialog.Nav>
591
+ <Dialog.Title>Approve with your sign-in</Dialog.Title>
592
+ <Dialog.Description>Check the prompt from your Privy signer.</Dialog.Description>
593
+ </Dialog.Header>
594
+ <Dialog.Body {...styles.center()}>
595
+ <Spinner aria-label="Waiting for approval" />
596
+ </Dialog.Body>
597
+ <ButtonGroup>
598
+ <Button disabled size="large">
599
+ Waiting for approval
600
+ </Button>
601
+ </ButtonGroup>
602
+ </Dialog.Popup>
603
+ </Dialog.Root>
604
+ )
605
+ }
606
+
607
+ namespace styles {
608
+ export const center = style({ alignItems: 'center' })
609
+ }
610
+ ```
611
+
612
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Progress.tsx)
613
+
614
+ ### Success
615
+
616
+ Confirms a settled payment with a receipt and the next step.
617
+
618
+ ```tsx
619
+ import { Button, ButtonGroup, Dialog, IconTile, ListGroup } from '@tempoxyz/ds'
620
+ import { Check } from '@tempoxyz/ds/icons'
621
+
622
+ export function Demo() {
623
+ return (
624
+ <Dialog.Root>
625
+ <Dialog.Trigger render={<Button variant="secondary" />}>Send $120.00</Dialog.Trigger>
626
+ <Dialog.Popup closeButton size="small">
627
+ <Dialog.Header layout="centered">
628
+ <Dialog.Nav />
629
+ <IconTile intent="positive">
630
+ <Check />
631
+ </IconTile>
632
+ <Dialog.Title>$120.00 sent</Dialog.Title>
633
+ <Dialog.Description>Maya Rao has it. Settled in seconds.</Dialog.Description>
634
+ </Dialog.Header>
635
+ <Dialog.Body>
636
+ <ListGroup.Root variant="boxed">
637
+ <ListGroup.Row>
638
+ <ListGroup.Label>To</ListGroup.Label>
639
+ Maya Rao
640
+ </ListGroup.Row>
641
+ <ListGroup.Row>
642
+ <ListGroup.Label>Fee</ListGroup.Label>
643
+ 0.001 USDC
644
+ </ListGroup.Row>
645
+ <ListGroup.Row>
646
+ <ListGroup.Label>Transaction</ListGroup.Label>
647
+ 0x6b1d…f20e
648
+ </ListGroup.Row>
649
+ </ListGroup.Root>
650
+ </Dialog.Body>
651
+ <ButtonGroup>
652
+ <Dialog.Close render={<Button size="large" variant="secondary" />}>
653
+ Send another
654
+ </Dialog.Close>
655
+ <Dialog.Close render={<Button size="large" />}>Done</Dialog.Close>
656
+ </ButtonGroup>
657
+ </Dialog.Popup>
658
+ </Dialog.Root>
659
+ )
660
+ }
661
+ ```
662
+
663
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/Success.tsx)
664
+
665
+ ### Account Ready
666
+
667
+ Confirms a new account and shows the details the person needs next.
668
+
669
+ ```tsx
670
+ import { Button, ButtonGroup, Dialog, formatHex, ListGroup, style } from '@tempoxyz/ds'
671
+
672
+ const address = '0x7a3f2c1e9b4d5a6f7c8e9d0a1b2c3d4e5fb8e091c2'
673
+
674
+ export function Demo() {
675
+ return (
676
+ <Dialog.Root>
677
+ <Dialog.Trigger render={<Button variant="secondary" />}>Create wallet</Dialog.Trigger>
678
+ <Dialog.Popup closeButton>
679
+ <Dialog.Header>
680
+ <Dialog.Title>Account ready</Dialog.Title>
681
+ <Dialog.Description>Wallet ready. Add a passkey as a backup.</Dialog.Description>
682
+ </Dialog.Header>
683
+ <Dialog.Body>
684
+ <ListGroup.Root variant="boxed">
685
+ <ListGroup.Row>
686
+ <span aria-hidden="true" {...styles.art()} />
687
+ <ListGroup.Info size="large">
688
+ <ListGroup.Title>Tempo Wallet</ListGroup.Title>
689
+ <ListGroup.Description>{formatHex(address, { min: 6 })}</ListGroup.Description>
690
+ </ListGroup.Info>
691
+ <Button
692
+ aria-label="Copy wallet address"
693
+ onClick={() => navigator.clipboard.writeText(address)}
694
+ size="xsmall"
695
+ variant="secondary"
696
+ >
697
+ Copy
698
+ </Button>
699
+ </ListGroup.Row>
700
+ <ListGroup.Row>
701
+ <ListGroup.Label>Network</ListGroup.Label>
702
+ Tempo Mainnet
703
+ </ListGroup.Row>
704
+ <ListGroup.Row>
705
+ <ListGroup.Label>Signed in as</ListGroup.Label>
706
+ piri@tempo.xyz
707
+ </ListGroup.Row>
708
+ <ListGroup.Row>
709
+ <ListGroup.Label>Signers</ListGroup.Label>1 · email
710
+ </ListGroup.Row>
711
+ </ListGroup.Root>
712
+ </Dialog.Body>
713
+ <ButtonGroup orientation="vertical">
714
+ <Button size="large">Add a passkey</Button>
715
+ <Dialog.Close render={<Button size="large" variant="tertiary" />}>
716
+ Go to dashboard
717
+ </Dialog.Close>
718
+ </ButtonGroup>
719
+ </Dialog.Popup>
720
+ </Dialog.Root>
721
+ )
722
+ }
723
+
724
+ namespace styles {
725
+ // Stands in for the wallet's artwork.
726
+ export const art = style({
727
+ backgroundImage: 'linear-gradient(#1f1a14, #eda866, #261f1a) !custom',
728
+ borderRadius: 'xs',
729
+ flexShrink: '0 !custom',
730
+ height: '40',
731
+ width: '40',
732
+ })
733
+ }
734
+ ```
735
+
736
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/AccountReady.tsx)
737
+
738
+ ### Receive Networks
739
+
740
+ Picks the network to receive on before the address appears.
741
+
742
+ ```tsx
743
+ import {
744
+ AssetChip,
745
+ Button,
746
+ ButtonGroup,
747
+ Dialog,
748
+ Inline,
749
+ ListGroup,
750
+ NetworkLogo,
751
+ } from '@tempoxyz/ds'
752
+
753
+ const networks = [
754
+ { name: 'Base', network: 'base' },
755
+ { name: 'Ethereum', network: 'ethereum' },
756
+ { name: 'HyperEVM', network: 'hyperevm' },
757
+ { name: 'Polygon', network: 'polygon' },
758
+ { name: 'Solana', network: 'solana' },
759
+ ] as const
760
+
761
+ export function Demo() {
762
+ return (
763
+ <Dialog.Root>
764
+ <Dialog.Trigger render={<Button variant="secondary" />}>Receive</Dialog.Trigger>
765
+ <Dialog.Popup closeButton>
766
+ <Dialog.Header>
767
+ <Dialog.Title>Receive crypto</Dialog.Title>
768
+ <Dialog.Description>Send supported stablecoins to this address.</Dialog.Description>
769
+ </Dialog.Header>
770
+ <Dialog.Body>
771
+ <ListGroup.Root dividers={false}>
772
+ {networks.map((entry) => (
773
+ <ListGroup.Row key={entry.network} onClick={() => {}}>
774
+ <NetworkLogo network={entry.network} size="large" />
775
+ <ListGroup.Info>
776
+ <ListGroup.Title>{entry.name}</ListGroup.Title>
777
+ <Inline gap="4">
778
+ <AssetChip>USDC</AssetChip>
779
+ <AssetChip>USDT</AssetChip>
780
+ </Inline>
781
+ </ListGroup.Info>
782
+ </ListGroup.Row>
783
+ ))}
784
+ </ListGroup.Root>
785
+ </Dialog.Body>
786
+ <ButtonGroup>
787
+ <Dialog.Close render={<Button size="large" />}>Done</Dialog.Close>
788
+ </ButtonGroup>
789
+ </Dialog.Popup>
790
+ </Dialog.Root>
791
+ )
792
+ }
793
+ ```
794
+
795
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/ReceiveNetworks.tsx)
796
+
797
+ ### Receive Address
798
+
799
+ Shows the address and its QR code for the chosen network, with a copy action.
800
+
801
+ ```tsx
802
+ import { Button, ButtonGroup, Dialog, formatHex, ListGroup, Qr, style } from '@tempoxyz/ds'
803
+
804
+ const address = '0x7a3f2c1e9b4d5a6f7c8e9d0a1b2c3d4e6b8e91c2'
805
+
806
+ export function Demo() {
807
+ return (
808
+ <Dialog.Root>
809
+ <Dialog.Trigger render={<Button variant="secondary" />}>Receive on Ethereum</Dialog.Trigger>
810
+ <Dialog.Popup closeButton>
811
+ <Dialog.Header layout="stacked">
812
+ <Dialog.Nav>
813
+ <Dialog.Back />
814
+ </Dialog.Nav>
815
+ <Dialog.Title>Receive crypto</Dialog.Title>
816
+ <Dialog.Description>Send supported stablecoins to this address.</Dialog.Description>
817
+ </Dialog.Header>
818
+ <Dialog.Body>
819
+ <Qr aria-label="Ethereum address QR code" size="small" value={address} {...styles.qr()} />
820
+ <ListGroup.Root variant="boxed">
821
+ <ListGroup.Row>
822
+ <ListGroup.Info size="large">
823
+ <ListGroup.Title>Your address</ListGroup.Title>
824
+ <ListGroup.Description>{formatHex(address, { min: 12 })}</ListGroup.Description>
825
+ </ListGroup.Info>
826
+ <Button
827
+ aria-label="Copy address"
828
+ onClick={() => navigator.clipboard.writeText(address)}
829
+ size="xsmall"
830
+ variant="secondary"
831
+ >
832
+ Copy
833
+ </Button>
834
+ </ListGroup.Row>
835
+ <ListGroup.Row>
836
+ <ListGroup.Label>Network to send from</ListGroup.Label>
837
+ Ethereum
838
+ </ListGroup.Row>
839
+ </ListGroup.Root>
840
+ </Dialog.Body>
841
+ <ButtonGroup>
842
+ <Dialog.Close render={<Button size="large" />}>Done</Dialog.Close>
843
+ </ButtonGroup>
844
+ </Dialog.Popup>
845
+ </Dialog.Root>
846
+ )
847
+ }
848
+
849
+ namespace styles {
850
+ export const qr = style({ alignSelf: 'center' })
851
+ }
852
+ ```
853
+
854
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/ReceiveAddress.tsx)
855
+
856
+ ### Verify Code
857
+
858
+ Confirms an email or phone number with a one-time code.
859
+
860
+ ```tsx
861
+ import { Button, ButtonGroup, Dialog, Field, TextInput } from '@tempoxyz/ds'
862
+
863
+ export function Demo() {
864
+ return (
865
+ <Dialog.Root>
866
+ <Dialog.Trigger render={<Button variant="secondary" />}>Verify email</Dialog.Trigger>
867
+ <Dialog.Popup closeButton>
868
+ <Dialog.Header layout="stacked">
869
+ <Dialog.Nav>
870
+ <Dialog.Back />
871
+ </Dialog.Nav>
872
+ <Dialog.Title>Check your email</Dialog.Title>
873
+ <Dialog.Description>
874
+ Enter the 6-digit code we sent to you@example.com.
875
+ </Dialog.Description>
876
+ </Dialog.Header>
877
+ <Dialog.Body>
878
+ <Field.Root>
879
+ <TextInput.Group>
880
+ <TextInput.Control
881
+ aria-label="Code"
882
+ autoComplete="one-time-code"
883
+ defaultValue="48291"
884
+ inputMode="numeric"
885
+ />
886
+ </TextInput.Group>
887
+ <Field.Description>Didn’t get it? Check spam. Resend in 0:30</Field.Description>
888
+ </Field.Root>
889
+ </Dialog.Body>
890
+ <ButtonGroup orientation="vertical">
891
+ <Button size="large">Continue</Button>
892
+ <Dialog.Close render={<Button size="large" variant="tertiary" />}>
893
+ Use a different email
894
+ </Dialog.Close>
895
+ </ButtonGroup>
896
+ </Dialog.Popup>
897
+ </Dialog.Root>
898
+ )
899
+ }
900
+ ```
901
+
902
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Dialog/VerifyCode.tsx)
903
+
904
+ ## Anatomy
905
+
906
+ Import the component and assemble its parts.
907
+
908
+ ```tsx
909
+ import { ButtonGroup, Dialog } from '@tempoxyz/ds'
910
+
911
+ <Dialog.Root>
912
+ <Dialog.Trigger />
913
+ <Dialog.Popup>
914
+ <Dialog.Header>
915
+ <Dialog.Nav>
916
+ <Dialog.Back />
917
+ </Dialog.Nav>
918
+ <Dialog.Title />
919
+ <Dialog.Description />
920
+ </Dialog.Header>
921
+ <Dialog.Body />
922
+ <ButtonGroup>
923
+ <Dialog.Close />
924
+ </ButtonGroup>
925
+ </Dialog.Popup>
926
+ </Dialog.Root>
927
+ ```
928
+
929
+ ## Guidelines
930
+
931
+ ### When to Use
932
+
933
+ * Complete a short task in place, such as Rename wallet.
934
+ * Walk through a short flow one step at a time, such as Send or Add a passkey.
935
+ * Review details before you commit, such as a payment before Send.
936
+ * Show something to read before you continue, such as a new API key that appears once.
937
+
938
+ ### When Not to Use
939
+
940
+ * Use an [AlertDialog](/docs/components/alert-dialog) to confirm a destructive or irreversible action.
941
+ * Use a [Drawer](/docs/components/drawer) for long forms, filters, or record details.
942
+ * Use a [Popover](/docs/components/popover) for a sentence or one setting anchored to its trigger.
943
+ * Use a page for content that still scrolls at the `large` size.
944
+ * Use a [Toast](/docs/components/toast) to confirm that an action finished.
945
+
946
+ See [Choosing a Component](/docs/components#popups) to compare overlays.
947
+
948
+ ### Pick the Size From the Content
949
+
950
+ Start at `medium`. Choose the size from what the dialog holds, not from how important it is.
951
+
952
+ * **One Decision (small):** One decision or outcome, with no fields.
953
+ * **One Task (medium):** Up to three fields, or one list.
954
+ * **Side-by-Side Review (large):** Several groups to read before you sign.
955
+
956
+ ### Keep One Size per Flow
957
+
958
+ Every step of a flow keeps the size of its first step. Swap the header and body for the next step instead of opening another dialog. Past 420px the body scrolls while the header and footer stay. If the body still scrolls at `large`, move the content to a page.
959
+
960
+ **Do:** Keep one task and one primary action in the dialog.
961
+
962
+ ```tsx
963
+ /**
964
+ * Do example for "Keep One Task per Dialog" on the Dialog page.
965
+ * Keep one task and one primary action in the dialog.
966
+ * @module
967
+ */
968
+ import { Button, Field, style, TextInput } from '@tempoxyz/ds'
969
+
970
+ /** Renders the static preview, which the guideline card shows inert. */
971
+ export function Demo() {
972
+ return (
973
+ <div {...styles.frame()}>
974
+ <div {...styles.popup()}>
975
+ <span {...styles.title()}>Rename wallet</span>
976
+ <Field.Root>
977
+ <Field.Label>Name</Field.Label>
978
+ <TextInput.Group>
979
+ <TextInput.Control defaultValue="Main wallet" />
980
+ </TextInput.Group>
981
+ </Field.Root>
982
+ <div {...styles.actions()}>
983
+ <Button size="small" variant="secondary">
984
+ Cancel
985
+ </Button>
986
+ <Button size="small">Save name</Button>
987
+ </div>
988
+ </div>
989
+ </div>
990
+ )
991
+ }
992
+
993
+ namespace styles {
994
+ export const actions = style({
995
+ columnGap: '8',
996
+ display: 'flex',
997
+ justifyContent: 'flex-end',
998
+ marginTop: '8',
999
+ })
1000
+
1001
+ export const frame = style({
1002
+ height: '15rem !custom',
1003
+ position: 'relative',
1004
+ width: '19rem !custom',
1005
+ })
1006
+
1007
+ export const popup = style({
1008
+ backgroundColor: 'background.elevated',
1009
+ borderRadius: 'sm',
1010
+ boxShadow: 'regular',
1011
+ boxSizing: 'border-box',
1012
+ display: 'flex',
1013
+ flexDirection: 'column',
1014
+ left: '0',
1015
+ padding: '24',
1016
+ position: 'absolute',
1017
+ rowGap: '12',
1018
+ top: '0',
1019
+ width: '17rem !custom',
1020
+ })
1021
+
1022
+ export const title = style({ color: 'content.primary', typography: 'heading.h3' })
1023
+ }
1024
+ ```
1025
+
1026
+ **Don't:** Open a dialog from a dialog.
1027
+
1028
+ ```tsx
1029
+ /** Don't example for "Keep One Task per Dialog" on the Dialog page. Don't open a dialog from a dialog. @module */
1030
+ import { Button, Field, style, TextInput } from '@tempoxyz/ds'
1031
+ import { Safe, Savings, Wallet } from '@tempoxyz/ds/icons'
1032
+
1033
+ /** Renders the static preview, which the guideline card shows inert. */
1034
+ export function Demo() {
1035
+ return (
1036
+ <div {...styles.frame()}>
1037
+ <div {...styles.popup()}>
1038
+ <span {...styles.title()}>Rename wallet</span>
1039
+ <Field.Root>
1040
+ <Field.Label>Name</Field.Label>
1041
+ <TextInput.Group>
1042
+ <TextInput.Control defaultValue="Main wallet" />
1043
+ </TextInput.Group>
1044
+ </Field.Root>
1045
+ <div {...styles.actions()}>
1046
+ <Button size="small" variant="secondary">
1047
+ Cancel
1048
+ </Button>
1049
+ <Button size="small">Save name</Button>
1050
+ </div>
1051
+ </div>
1052
+ <div {...styles.stacked()}>
1053
+ <span {...styles.title()}>Choose an icon</span>
1054
+ <div {...styles.icons()}>
1055
+ <Button aria-label="Wallet" shape="icon" size="small" variant="secondary">
1056
+ <Wallet />
1057
+ </Button>
1058
+ <Button aria-label="Safe" shape="icon" size="small" variant="secondary">
1059
+ <Safe />
1060
+ </Button>
1061
+ <Button aria-label="Savings" shape="icon" size="small" variant="secondary">
1062
+ <Savings />
1063
+ </Button>
1064
+ </div>
1065
+ <div {...styles.actions()}>
1066
+ <Button size="small">Use icon</Button>
1067
+ </div>
1068
+ </div>
1069
+ </div>
1070
+ )
1071
+ }
1072
+
1073
+ namespace styles {
1074
+ export const actions = style({
1075
+ columnGap: '8',
1076
+ display: 'flex',
1077
+ justifyContent: 'flex-end',
1078
+ marginTop: '8',
1079
+ })
1080
+
1081
+ export const frame = style({
1082
+ height: '15rem !custom',
1083
+ position: 'relative',
1084
+ width: '19rem !custom',
1085
+ })
1086
+
1087
+ export const icons = style({ columnGap: '8', display: 'flex' })
1088
+
1089
+ export const popup = style({
1090
+ backgroundColor: 'background.elevated',
1091
+ borderRadius: 'sm',
1092
+ boxShadow: 'regular',
1093
+ boxSizing: 'border-box',
1094
+ display: 'flex',
1095
+ flexDirection: 'column',
1096
+ left: '0',
1097
+ padding: '24',
1098
+ position: 'absolute',
1099
+ rowGap: '12',
1100
+ top: '0',
1101
+ width: '17rem !custom',
1102
+ })
1103
+
1104
+ export const stacked = style({
1105
+ backgroundColor: 'background.elevated',
1106
+ borderRadius: 'sm',
1107
+ bottom: '0',
1108
+ boxShadow: 'regular',
1109
+ boxSizing: 'border-box',
1110
+ display: 'flex',
1111
+ flexDirection: 'column',
1112
+ padding: '20',
1113
+ position: 'absolute',
1114
+ right: '0',
1115
+ rowGap: '12',
1116
+ width: '13rem !custom',
1117
+ })
1118
+
1119
+ export const title = style({ color: 'content.primary', typography: 'heading.h3' })
1120
+ }
1121
+ ```
1122
+
1123
+ ### Provide a Visible Way Out
1124
+
1125
+ Touch screens have no Escape key, and the backdrop doesn't look pressable. Set `closeButton` on `Dialog.Popup`, or add a `Dialog.Close` action such as Cancel. From step 2 of a flow, add a `Dialog.Back`.
1126
+
1127
+ **Do:** Offer a close button and a Cancel action beside the primary action.
1128
+
1129
+ ```tsx
1130
+ /**
1131
+ * Do example for "Provide a Visible Way Out" on the Dialog page.
1132
+ * Offer a close button and a Cancel action beside the primary action.
1133
+ * @module
1134
+ */
1135
+ import { Button, Field, style, TextInput } from '@tempoxyz/ds'
1136
+ import { Close } from '@tempoxyz/ds/icons'
1137
+
1138
+ /** Renders the static preview, which the guideline card shows inert. */
1139
+ export function Demo() {
1140
+ return (
1141
+ <div {...styles.popup()}>
1142
+ <div {...styles.header()}>
1143
+ <span {...styles.title()}>Rename wallet</span>
1144
+ <Button aria-label="Close" shape="icon" size="small" variant="secondary">
1145
+ <Close />
1146
+ </Button>
1147
+ </div>
1148
+ <Field.Root>
1149
+ <Field.Label>Name</Field.Label>
1150
+ <TextInput.Group>
1151
+ <TextInput.Control defaultValue="Main wallet" />
1152
+ </TextInput.Group>
1153
+ </Field.Root>
1154
+ <div {...styles.actions()}>
1155
+ <Button size="small" variant="secondary">
1156
+ Cancel
1157
+ </Button>
1158
+ <Button size="small">Save name</Button>
1159
+ </div>
1160
+ </div>
1161
+ )
1162
+ }
1163
+
1164
+ namespace styles {
1165
+ export const actions = style({
1166
+ columnGap: '8',
1167
+ display: 'flex',
1168
+ justifyContent: 'flex-end',
1169
+ marginTop: '8',
1170
+ })
1171
+
1172
+ export const header = style({
1173
+ alignItems: 'center',
1174
+ columnGap: '12',
1175
+ display: 'flex',
1176
+ justifyContent: 'space-between',
1177
+ })
1178
+
1179
+ export const popup = style({
1180
+ backgroundColor: 'background.elevated',
1181
+ borderRadius: 'sm',
1182
+ boxShadow: 'regular',
1183
+ boxSizing: 'border-box',
1184
+ display: 'flex',
1185
+ flexDirection: 'column',
1186
+ padding: '24',
1187
+ rowGap: '12',
1188
+ width: '17rem !custom',
1189
+ })
1190
+
1191
+ export const title = style({ color: 'content.primary', typography: 'heading.h3' })
1192
+ }
1193
+ ```
1194
+
1195
+ **Don't:** Make the primary action the only visible way out.
1196
+
1197
+ ```tsx
1198
+ /**
1199
+ * Don't example for "Provide a Visible Way Out" on the Dialog page.
1200
+ * Don't make the primary action the only visible way out.
1201
+ * @module
1202
+ */
1203
+ import { Button, Field, style, TextInput } from '@tempoxyz/ds'
1204
+
1205
+ /** Renders the static preview, which the guideline card shows inert. */
1206
+ export function Demo() {
1207
+ return (
1208
+ <div {...styles.popup()}>
1209
+ <span {...styles.title()}>Rename wallet</span>
1210
+ <Field.Root>
1211
+ <Field.Label>Name</Field.Label>
1212
+ <TextInput.Group>
1213
+ <TextInput.Control defaultValue="Main wallet" />
1214
+ </TextInput.Group>
1215
+ </Field.Root>
1216
+ <div {...styles.actions()}>
1217
+ <Button size="small">Save name</Button>
1218
+ </div>
1219
+ </div>
1220
+ )
1221
+ }
1222
+
1223
+ namespace styles {
1224
+ export const actions = style({ display: 'flex', justifyContent: 'flex-end', marginTop: '8' })
1225
+
1226
+ export const popup = style({
1227
+ backgroundColor: 'background.elevated',
1228
+ borderRadius: 'sm',
1229
+ boxShadow: 'regular',
1230
+ boxSizing: 'border-box',
1231
+ display: 'flex',
1232
+ flexDirection: 'column',
1233
+ padding: '24',
1234
+ rowGap: '12',
1235
+ width: '17rem !custom',
1236
+ })
1237
+
1238
+ export const title = style({ color: 'content.primary', typography: 'heading.h3' })
1239
+ }
1240
+ ```
1241
+
1242
+ ### Protect Unsaved Input
1243
+
1244
+ A backdrop press closes the dialog. Escape and the close controls work in both cases.
1245
+
1246
+ * **Forms (disablePointerDismissal):** Set it on `Dialog.Root` so a stray backdrop press can't discard input.
1247
+ * **Content to Read (default):** Keep the default for content with no input, so a backdrop press closes the dialog.
1248
+
1249
+ ## Content
1250
+
1251
+ ### Title With the Task
1252
+
1253
+ Write a verb and an object, without end punctuation. Reuse the trigger's label, such as Rename wallet. Save questions for an [AlertDialog](/docs/components/alert-dialog).
1254
+
1255
+ ### Describe the Effect
1256
+
1257
+ In `Dialog.Description`, state what the task changes in one or two sentences, such as The name is only visible to you.
1258
+
1259
+ ### Repeat the Verb on the Primary Action
1260
+
1261
+ Label the primary action with the title's verb, such as Save name. Use Cancel to close without changes.
1262
+
1263
+ ### Write One Headline
1264
+
1265
+ Give each dialog one `Dialog.Title`. Name the groups in the body with a [ListGroup](/docs/components/list-group) heading instead of a second headline.
1266
+
1267
+ ## Accessibility
1268
+
1269
+ A dialog follows the [WAI-ARIA Authoring Practices modal dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/).
1270
+
1271
+ ### Semantics
1272
+
1273
+ * **Dialog.Trigger (button):** Announces the dialog and whether it is open.
1274
+ * **Dialog.Popup (dialog):** Modal by default.
1275
+ * **Dialog.Title (h2):** Names the dialog.
1276
+ * **Dialog.Description (p):** Describes the dialog.
1277
+ * **Dialog.Back (button):** A "Back" icon button, first in the focus order.
1278
+ * **Stepper (progressbar):** Name the flow with `aria-label` on the [Stepper](/docs/components/stepper).
1279
+ * **Close Button (closeButton):** A "Close" icon button, last in the focus order.
1280
+ * **Page Behind (inert):** Ignores pointer input and scroll while open.
1281
+
1282
+ ### Keyboard
1283
+
1284
+ * **Open or Activate (Enter or Space):** Opens the dialog, or activates the focused control.
1285
+ * **Move to the Next Element (Tab):** Moves to the next element, and wraps.
1286
+ * **Move to the Previous Element (Shift+Tab):** Moves to the previous element, and wraps.
1287
+ * **Close the Dialog (Escape):** Closes the dialog and returns focus to the trigger.
1288
+
1289
+ ### Focus
1290
+
1291
+ * **Opening (initialFocus):** Moves to the first tabbable element, or the popup on touch. Pass `initialFocus` to start elsewhere, such as the amount field.
1292
+ * **Trapping:** Tab and Shift+Tab wrap inside the dialog, so focus stays inside while it is open.
1293
+ * **Backdrop Press:** Closes the dialog. In a form, set `disablePointerDismissal` on `Dialog.Root` to keep it open.
1294
+ * **Closing (finalFocus):** Returns to the trigger. Pass `finalFocus` if the task removes the trigger.
1295
+
1296
+ ### Motion
1297
+
1298
+ Under reduced motion, the dialog fades in and out without scaling or sliding.
1299
+
1300
+ ### Your Responsibilities
1301
+
1302
+ * **Render a Title (Dialog.Title):** Without it, the dialog has no accessible name.
1303
+
1304
+ ## Related
1305
+
1306
+ [AlertDialog](/docs/components/alert-dialog)
1307
+
1308
+ Use for confirming destructive actions.
1309
+
1310
+ [ButtonGroup](/docs/components/button-group)
1311
+
1312
+ Use for the actions in the footer.
1313
+
1314
+ [Drawer](/docs/components/drawer)
1315
+
1316
+ Use for long forms and details panels.
1317
+
1318
+ [Popover](/docs/components/popover)
1319
+
1320
+ Use for anchored, non-blocking content.
1321
+
1322
+ [Stepper](/docs/components/stepper)
1323
+
1324
+ Use for progress through the steps of a flow.
1325
+
1326
+ [Forms](/docs/patterns/forms)
1327
+
1328
+ Covers laying out fields and actions.
1329
+
1330
+ [Payments](/docs/patterns/payments)
1331
+
1332
+ Covers reviewing a payment before you send it.