@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,612 @@
1
+ # Empty States
2
+
3
+ Explain why a region has nothing to show, and offer the next step that fills it.
4
+
5
+ ## Overview
6
+
7
+ An empty region can mean missing data, a filter with no matches, finished work, or a failed request. Each cause takes different words and actions. [Empty](/docs/components/empty) composes the parts, and every empty state covers three points:
8
+
9
+ * **What Is This Place? (Empty.Title):** Names the content that would appear.
10
+ * **Why Is It Empty? (Empty.Description):** Explains the cause in one sentence.
11
+ * **What Can I Do? (Empty.Actions):** One action that fills the region, if one exists.
12
+
13
+ ## Variants
14
+
15
+ The cause decides the text and the action. These are reference layouts built from [Surface](/docs/components/surface), [Empty](/docs/components/empty), and [Button](/docs/components/button), not shipped components.
16
+
17
+ ### First Use
18
+
19
+ ```tsx
20
+ import { Button, Empty, style, Surface } from '@tempoxyz/ds'
21
+ import { History } from '@tempoxyz/ds/icons'
22
+
23
+ export const fill = true
24
+ export function Demo() {
25
+ return (
26
+ <div {...styles.page()}>
27
+ <Surface {...styles.card()}>
28
+ <Surface.Header>
29
+ <Surface.Title>Activity</Surface.Title>
30
+ </Surface.Header>
31
+ <Empty.Root>
32
+ <Empty.Icon>
33
+ <History />
34
+ </Empty.Icon>
35
+ <Empty.Title>No activity yet</Empty.Title>
36
+ <Empty.Description>
37
+ Payments you send and receive appear here. Add funds to make your first payment.
38
+ </Empty.Description>
39
+ <Empty.Actions>
40
+ <Button size="small">Add funds</Button>
41
+ </Empty.Actions>
42
+ </Empty.Root>
43
+ </Surface>
44
+ </div>
45
+ )
46
+ }
47
+
48
+ namespace styles {
49
+ export const card = style({
50
+ boxSizing: 'border-box',
51
+ maxWidth: '26rem !custom',
52
+ width: '100% !custom',
53
+ })
54
+
55
+ export const page = style({
56
+ backgroundColor: 'background.primary',
57
+ display: 'grid',
58
+ justifyItems: 'center',
59
+ padding: '16',
60
+ })
61
+ }
62
+ ```
63
+
64
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Patterns/EmptyFirstUse.tsx)
65
+
66
+ Nothing exists yet. State what will appear, and offer the action that creates the first item, such as Add funds or Create key.
67
+
68
+ ### No Results
69
+
70
+ ```tsx
71
+ import { Button, Empty, Field, Identicon, ListGroup, style, Surface, TextInput } from '@tempoxyz/ds'
72
+ import { Search } from '@tempoxyz/ds/icons'
73
+ import { useRef, useState } from 'react'
74
+
75
+ export const fill = true
76
+ const contacts = [
77
+ ['Ada', '0x71C7656EC7ab88b098defB751B7401B5f6d8976F'],
78
+ ['Payroll', '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'],
79
+ ['Treasury', '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'],
80
+ ] as const
81
+
82
+ export function Demo() {
83
+ const input = useRef<HTMLInputElement>(null)
84
+ const [query, setQuery] = useState('zara')
85
+ const matches = contacts.filter(([name, address]) =>
86
+ `${name} ${address}`.toLowerCase().includes(query.trim().toLowerCase()),
87
+ )
88
+
89
+ return (
90
+ <div {...styles.page()}>
91
+ <Surface {...styles.card()}>
92
+ <Surface.Header>
93
+ <Surface.Title>Contacts</Surface.Title>
94
+ </Surface.Header>
95
+ <Field.Root>
96
+ <TextInput.Group>
97
+ <TextInput.Prefix>
98
+ <Search />
99
+ </TextInput.Prefix>
100
+ <TextInput.Control
101
+ aria-label="Search contacts"
102
+ onValueChange={setQuery}
103
+ placeholder="Name or address"
104
+ ref={input}
105
+ type="search"
106
+ value={query}
107
+ />
108
+ <TextInput.Clear />
109
+ </TextInput.Group>
110
+ </Field.Root>
111
+ {/* Stays mounted, so screen readers hear the count change as you type. */}
112
+ <p role="status" {...styles.status()}>
113
+ {matches.length === 1 ? '1 contact' : `${matches.length} contacts`}
114
+ </p>
115
+ {matches.length > 0 ? (
116
+ <div {...styles.rows()}>
117
+ <ListGroup.Root>
118
+ {matches.map(([name, address]) => (
119
+ <ListGroup.Row key={address}>
120
+ <Identicon address={address} aria-hidden size="24" />
121
+ <ListGroup.Info>
122
+ <ListGroup.Title>{name}</ListGroup.Title>
123
+ <ListGroup.Description>
124
+ {address.slice(0, 6)}…{address.slice(-4)}
125
+ </ListGroup.Description>
126
+ </ListGroup.Info>
127
+ </ListGroup.Row>
128
+ ))}
129
+ </ListGroup.Root>
130
+ </div>
131
+ ) : (
132
+ <Empty.Root>
133
+ <Empty.Icon>
134
+ <Search />
135
+ </Empty.Icon>
136
+ <Empty.Title>No contacts match “{query.trim()}”</Empty.Title>
137
+ <Empty.Description>
138
+ Check the spelling, or search by the full 0x address.
139
+ </Empty.Description>
140
+ <Empty.Actions>
141
+ <Button
142
+ onClick={() => {
143
+ setQuery('')
144
+ input.current?.focus()
145
+ }}
146
+ size="small"
147
+ variant="secondary"
148
+ >
149
+ Clear search
150
+ </Button>
151
+ </Empty.Actions>
152
+ </Empty.Root>
153
+ )}
154
+ </Surface>
155
+ </div>
156
+ )
157
+ }
158
+
159
+ namespace styles {
160
+ export const card = style({
161
+ boxSizing: 'border-box',
162
+ maxWidth: '26rem !custom',
163
+ width: '100% !custom',
164
+ })
165
+
166
+ export const page = style({
167
+ backgroundColor: 'background.primary',
168
+ display: 'grid',
169
+ justifyItems: 'center',
170
+ padding: '16',
171
+ })
172
+
173
+ export const rows = style({ marginBlockStart: '8' })
174
+
175
+ export const status = style({
176
+ clipPath: 'inset(50%) !custom',
177
+ height: '1px !custom',
178
+ margin: '0',
179
+ overflow: 'hidden',
180
+ position: 'absolute',
181
+ whiteSpace: 'nowrap',
182
+ width: '1px !custom',
183
+ })
184
+ }
185
+ ```
186
+
187
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Patterns/EmptyNoResults.tsx)
188
+
189
+ Repeat the query in the title, so the reader can spot a typo. Offer Clear search or Clear filters as a `secondary` button, and keep the search field and filters visible above the empty state.
190
+
191
+ **Do:** Repeat the query and offer a way to widen the search.
192
+
193
+ ```tsx
194
+ <Empty.Root>
195
+ <Empty.Icon>
196
+ <Search />
197
+ </Empty.Icon>
198
+ <Empty.Title>No results for "rent"</Empty.Title>
199
+ <Empty.Description>Check the spelling or search for another payment.</Empty.Description>
200
+ <Empty.Actions>
201
+ <Button size="small" variant="secondary">Clear search</Button>
202
+ </Empty.Actions>
203
+ </Empty.Root>
204
+ ```
205
+
206
+ **Don't:** If a filter hides existing items, don't show the first use state.
207
+
208
+ ```tsx
209
+ <Empty.Root>
210
+ <Empty.Icon>
211
+ <Wallet />
212
+ </Empty.Icon>
213
+ <Empty.Title>No payments yet</Empty.Title>
214
+ <Empty.Description>Payments you send or receive appear here.</Empty.Description>
215
+ <Empty.Actions>
216
+ <Button size="small">Add funds</Button>
217
+ </Empty.Actions>
218
+ </Empty.Root>
219
+ ```
220
+
221
+ ### Cleared
222
+
223
+ ```tsx
224
+ import { Button, Empty, style, Surface } from '@tempoxyz/ds'
225
+ import { CheckCircle } from '@tempoxyz/ds/icons'
226
+
227
+ export const fill = true
228
+ export function Demo() {
229
+ return (
230
+ <div {...styles.page()}>
231
+ <Surface {...styles.card()}>
232
+ <Surface.Header>
233
+ <Surface.Title>Payment requests</Surface.Title>
234
+ </Surface.Header>
235
+ <Empty.Root>
236
+ <Empty.Icon>
237
+ <CheckCircle />
238
+ </Empty.Icon>
239
+ <Empty.Title>No pending requests</Empty.Title>
240
+ <Empty.Description>
241
+ You've paid or declined every request. New requests appear here.
242
+ </Empty.Description>
243
+ <Empty.Actions>
244
+ <Button size="small" variant="secondary">
245
+ View history
246
+ </Button>
247
+ </Empty.Actions>
248
+ </Empty.Root>
249
+ </Surface>
250
+ </div>
251
+ )
252
+ }
253
+
254
+ namespace styles {
255
+ export const card = style({
256
+ boxSizing: 'border-box',
257
+ maxWidth: '26rem !custom',
258
+ width: '100% !custom',
259
+ })
260
+
261
+ export const page = style({
262
+ backgroundColor: 'background.primary',
263
+ display: 'grid',
264
+ justifyItems: 'center',
265
+ padding: '16',
266
+ })
267
+ }
268
+ ```
269
+
270
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Patterns/EmptyCleared.tsx)
271
+
272
+ If the reader processed everything, such as every payment request, confirm that nothing is left. Leave out the primary action. If a history exists, link to it.
273
+
274
+ **Do:** Confirm the reader finished, and state what will appear next.
275
+
276
+ ```tsx
277
+ <Empty.Root>
278
+ <Empty.Icon>
279
+ <Check />
280
+ </Empty.Icon>
281
+ <Empty.Title>All caught up</Empty.Title>
282
+ <Empty.Description>New requests appear here when someone asks you to pay.</Empty.Description>
283
+ </Empty.Root>
284
+ ```
285
+
286
+ **Don't:** Use playful filler in place of a clear outcome.
287
+
288
+ ```tsx
289
+ <Empty.Root>
290
+ <Empty.Icon>
291
+ <Clipboard />
292
+ </Empty.Icon>
293
+ <Empty.Title>Nothing here!</Empty.Title>
294
+ <Empty.Description>Looks like it is pretty quiet. Check back later!</Empty.Description>
295
+ </Empty.Root>
296
+ ```
297
+
298
+ ### Error-Empty
299
+
300
+ ```tsx
301
+ import { Button, Empty, ListGroup, style, Surface } from '@tempoxyz/ds'
302
+ import { AlertCircle } from '@tempoxyz/ds/icons'
303
+ import { useState } from 'react'
304
+
305
+ export const fill = true
306
+ function Failed(props: { onRetry: () => void; retrying: boolean }) {
307
+ return (
308
+ <Empty.Root>
309
+ <Empty.Icon>
310
+ <AlertCircle />
311
+ </Empty.Icon>
312
+ <Empty.Title>Couldn't load activity</Empty.Title>
313
+ <Empty.Description>
314
+ Check your connection, then try again. Your balance and payments aren't affected.
315
+ </Empty.Description>
316
+ <Empty.Actions>
317
+ <Button loading={props.retrying} onClick={props.onRetry} size="small">
318
+ Try again
319
+ </Button>
320
+ </Empty.Actions>
321
+ </Empty.Root>
322
+ )
323
+ }
324
+
325
+ export function Demo() {
326
+ const [loaded, setLoaded] = useState(false)
327
+ const [retrying, setRetrying] = useState(false)
328
+
329
+ return (
330
+ <div {...styles.page()}>
331
+ <Surface {...styles.card()}>
332
+ <Surface.Header>
333
+ <Surface.Title>Activity</Surface.Title>
334
+ </Surface.Header>
335
+ {loaded ? (
336
+ <div {...styles.rows()}>
337
+ <ListGroup.Root>
338
+ <ListGroup.Row>
339
+ <ListGroup.Label>Received from 0x3C44…93BC</ListGroup.Label>
340
+ 120.00 USDC
341
+ </ListGroup.Row>
342
+ <ListGroup.Row>
343
+ <ListGroup.Label>Sent to 0x71C7…976F</ListGroup.Label>
344
+ 25.00 USDC
345
+ </ListGroup.Row>
346
+ </ListGroup.Root>
347
+ </div>
348
+ ) : (
349
+ <Failed
350
+ onRetry={() => {
351
+ setRetrying(true)
352
+ setTimeout(() => {
353
+ setRetrying(false)
354
+ setLoaded(true)
355
+ }, 1000)
356
+ }}
357
+ retrying={retrying}
358
+ />
359
+ )}
360
+ </Surface>
361
+ </div>
362
+ )
363
+ }
364
+
365
+ namespace styles {
366
+ export const card = style({
367
+ boxSizing: 'border-box',
368
+ maxWidth: '26rem !custom',
369
+ width: '100% !custom',
370
+ })
371
+
372
+ export const page = style({
373
+ backgroundColor: 'background.primary',
374
+ display: 'grid',
375
+ justifyItems: 'center',
376
+ padding: '16',
377
+ })
378
+
379
+ export const rows = style({ marginBlockStart: '8' })
380
+ }
381
+ ```
382
+
383
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Patterns/EmptyError.tsx)
384
+
385
+ If the request fails, write Couldn't load followed by the content, and offer Try again. Set `loading` on the [Button](/docs/components/button) while the retry runs.
386
+
387
+ ## Guidelines
388
+
389
+ ### Explain the Cause and the Next Step
390
+
391
+ Write the title as a short statement of what is missing, such as No API keys yet. Use the description for the cause and what the action does.
392
+
393
+ **Do:** State what is missing and offer the action that adds it.
394
+
395
+ ```tsx
396
+ /**
397
+ * Do example for "Explain the Cause and the Next Step" on the Empty States page.
398
+ * State what is missing and offer the action that adds it.
399
+ * @module
400
+ */
401
+ import { Button, Empty } from '@tempoxyz/ds'
402
+ import { Key } from '@tempoxyz/ds/icons'
403
+
404
+ /** Renders the static preview, which the guideline card shows inert. */
405
+ export function Demo() {
406
+ return (
407
+ <Empty.Root>
408
+ <Empty.Icon>
409
+ <Key />
410
+ </Empty.Icon>
411
+ <Empty.Title>No API keys yet</Empty.Title>
412
+ <Empty.Description>Create a key to call the Tempo API from your server.</Empty.Description>
413
+ <Empty.Actions>
414
+ <Button size="small">Create key</Button>
415
+ </Empty.Actions>
416
+ </Empty.Root>
417
+ )
418
+ }
419
+ ```
420
+
421
+ **Don't:** Show a bare No data message with nothing to do next.
422
+
423
+ ```tsx
424
+ /**
425
+ * Don't example for "Explain the Cause and the Next Step" on the Empty States page.
426
+ * Don't show a bare No data message with nothing to do next.
427
+ * @module
428
+ */
429
+ import { Empty } from '@tempoxyz/ds'
430
+
431
+ /** Renders the static preview, which the guideline card shows inert. */
432
+ export function Demo() {
433
+ return (
434
+ <Empty.Root>
435
+ <Empty.Title>No data</Empty.Title>
436
+ </Empty.Root>
437
+ )
438
+ }
439
+ ```
440
+
441
+ ### Never Show a Failed Request as Empty
442
+
443
+ An empty list after a failed request falsely reports no activity. Keep empty states for successful requests with no content.
444
+
445
+ **Do:** State that the content couldn't load, and offer Try again.
446
+
447
+ ```tsx
448
+ /**
449
+ * Do example for "Never Show a Failed Request as Empty" on the Empty States page.
450
+ * State that the content couldn't load, and offer Try again.
451
+ * @module
452
+ */
453
+ import { Button, Empty } from '@tempoxyz/ds'
454
+ import { AlertCircle } from '@tempoxyz/ds/icons'
455
+
456
+ /** Renders the static preview, which the guideline card shows inert. */
457
+ export function Demo() {
458
+ return (
459
+ <Empty.Root>
460
+ <Empty.Icon>
461
+ <AlertCircle />
462
+ </Empty.Icon>
463
+ <Empty.Title>Couldn't load activity</Empty.Title>
464
+ <Empty.Description>Check your connection, then try again.</Empty.Description>
465
+ <Empty.Actions>
466
+ <Button size="small">Try again</Button>
467
+ </Empty.Actions>
468
+ </Empty.Root>
469
+ )
470
+ }
471
+ ```
472
+
473
+ **Don't:** Show No activity yet after the request failed.
474
+
475
+ ```tsx
476
+ /**
477
+ * Don't example for "Never Show a Failed Request as Empty" on the Empty States page.
478
+ * Don't show No activity yet after the request failed.
479
+ * @module
480
+ */
481
+ import { Empty } from '@tempoxyz/ds'
482
+ import { History } from '@tempoxyz/ds/icons'
483
+
484
+ /** Renders the static preview, which the guideline card shows inert. */
485
+ export function Demo() {
486
+ return (
487
+ <Empty.Root>
488
+ <Empty.Icon>
489
+ <History />
490
+ </Empty.Icon>
491
+ <Empty.Title>No activity yet</Empty.Title>
492
+ <Empty.Description>Payments you send and receive appear here.</Empty.Description>
493
+ </Empty.Root>
494
+ )
495
+ }
496
+ ```
497
+
498
+ ### Offer One Primary Action
499
+
500
+ Make it the task that fills the region. Add a secondary button only for a related step, such as a guide, and use `Button.Link` if the action opens another page.
501
+
502
+ **Do:** Pair one primary action with an optional secondary one.
503
+
504
+ ```tsx
505
+ /**
506
+ * Do example for "Offer One Primary Action" on the Empty States page.
507
+ * Pair one primary action with an optional secondary one.
508
+ * @module
509
+ */
510
+ import { Button, Empty } from '@tempoxyz/ds'
511
+ import { Link } from '@tempoxyz/ds/icons'
512
+
513
+ /** Renders the static preview, which the guideline card shows inert. */
514
+ export function Demo() {
515
+ return (
516
+ <Empty.Root>
517
+ <Empty.Icon>
518
+ <Link />
519
+ </Empty.Icon>
520
+ <Empty.Title>No webhooks</Empty.Title>
521
+ <Empty.Description>Add an endpoint to receive payment events.</Empty.Description>
522
+ <Empty.Actions>
523
+ <Button size="small">Add webhook</Button>
524
+ <Button size="small" variant="secondary">
525
+ Read the guide
526
+ </Button>
527
+ </Empty.Actions>
528
+ </Empty.Root>
529
+ )
530
+ }
531
+ ```
532
+
533
+ **Don't:** Offer several primary actions that compete for attention.
534
+
535
+ ```tsx
536
+ /**
537
+ * Don't example for "Offer One Primary Action" on the Empty States page.
538
+ * Don't offer several primary actions that compete for attention.
539
+ * @module
540
+ */
541
+ import { Button, Empty } from '@tempoxyz/ds'
542
+ import { Link } from '@tempoxyz/ds/icons'
543
+
544
+ /** Renders the static preview, which the guideline card shows inert. */
545
+ export function Demo() {
546
+ return (
547
+ <Empty.Root>
548
+ <Empty.Icon>
549
+ <Link />
550
+ </Empty.Icon>
551
+ <Empty.Title>No webhooks</Empty.Title>
552
+ <Empty.Description>Add an endpoint to receive payment events.</Empty.Description>
553
+ <Empty.Actions>
554
+ <Button size="small">Add webhook</Button>
555
+ <Button size="small">Import</Button>
556
+ <Button size="small">Test event</Button>
557
+ </Empty.Actions>
558
+ </Empty.Root>
559
+ )
560
+ }
561
+ ```
562
+
563
+ ### Fit the State to the Region
564
+
565
+ * **Pages and Cards:** Use the full empty state with an icon that hints at the content.
566
+ * **Small Regions:** In a small region, such as a [Popover](/docs/components/popover), use a short line of text and no icon.
567
+ * **Comboboxes (Combobox.Empty):** Render it in a [Combobox](/docs/components/combobox), so an empty result is announced instead of an empty popup.
568
+ * **Keep the Frame:** Keep the heading and filters. In a [Table](/docs/components/table), put the empty state below the header row.
569
+
570
+ ### Don't Show an Empty State While Loading
571
+
572
+ * **Load First (Skeleton):** Show a [Skeleton](/docs/components/skeleton) until the request settles. An empty state that flashes first reads as a missing balance.
573
+ * **Then Decide:** After the request settles, choose content, an empty state, or an error (see [Loading](/docs/patterns/loading)).
574
+
575
+ ## States
576
+
577
+ * **Disabled:** If the reader lacks permission for the action, leave it out and state who can act.
578
+ * **Success (Toast):** The new item replaces the empty state, and a [Toast](/docs/components/toast) confirms it.
579
+
580
+ ## Content
581
+
582
+ * **Title (Sentence case):** Name what is missing, such as "No payments yet".
583
+ * **Description (1 sentence):** Address the reader as "you".
584
+ * **Action (Verb):** Lead with a verb, such as "Add funds".
585
+
586
+ ## Accessibility
587
+
588
+ * **Heading Level (h3):** Place the state under an `h2`, such as a `Surface.Title`, so heading levels stay in order.
589
+ * **Announce Results (role=status):** The parts render no live region. If results change on input, announce the count in a polite region that stays mounted.
590
+ * **Focus After Clearing:** After Clear search, move focus to the search field.
591
+
592
+ ## Related
593
+
594
+ [Empty](/docs/components/empty)
595
+
596
+ Use for the component parts.
597
+
598
+ [Combobox](/docs/components/combobox)
599
+
600
+ Use for empty popups.
601
+
602
+ [Loading](/docs/patterns/loading)
603
+
604
+ Covers what shows before content.
605
+
606
+ [Feedback and Status](/docs/patterns/feedback-and-status)
607
+
608
+ Covers the Couldn't load state.
609
+
610
+ [Tables and Lists](/docs/patterns/tables-and-lists)
611
+
612
+ Covers empty tables.