@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,424 @@
1
+ # Snippet
2
+
3
+ Shows a command or value with a copy button.
4
+
5
+ [View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/Snippet.tsx)
6
+
7
+ Figma: [Content/Code Snippet](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-3317)
8
+
9
+ ## Overview
10
+
11
+ Snippet renders a command or value in a `<code>` element, with a copy button in the corner. A click anywhere on the command copies it.
12
+
13
+ ## Examples
14
+
15
+ ### Basic
16
+
17
+ ```tsx
18
+ import { Snippet, style } from '@tempoxyz/ds'
19
+
20
+ export const fill = true
21
+ export function Demo() {
22
+ return <Snippet {...styles.snippet()}>pnpm add @tempoxyz/ds</Snippet>
23
+ }
24
+
25
+ namespace styles {
26
+ export const snippet = style({ maxWidth: '28rem !custom' })
27
+ }
28
+ ```
29
+
30
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Snippet/Basic.tsx)
31
+
32
+ ### Wrap
33
+
34
+ The first word, the program, renders in the primary color. Long commands wrap, and wrapped lines align with the arguments.
35
+
36
+ ```tsx
37
+ import { Snippet, style } from '@tempoxyz/ds'
38
+
39
+ export const fill = true
40
+ export function Demo() {
41
+ return (
42
+ <Snippet {...styles.snippet()}>
43
+ claude "Read https://tempo.xyz/SKILL.md and connect to my Tempo wallet"
44
+ </Snippet>
45
+ )
46
+ }
47
+
48
+ namespace styles {
49
+ export const snippet = style({ maxWidth: '24.75rem !custom' })
50
+ }
51
+ ```
52
+
53
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Snippet/Wrap.tsx)
54
+
55
+ ### Prompt
56
+
57
+ Set `prompt` to a glyph, or to `false` to hide it. It defaults to `$`.
58
+
59
+ ```tsx
60
+ import { Snippet, style } from '@tempoxyz/ds'
61
+
62
+ export const fill = true
63
+ export function Demo() {
64
+ return (
65
+ <Snippet prompt={false} {...styles.snippet()}>
66
+ https://ds.tempo.xyz
67
+ </Snippet>
68
+ )
69
+ }
70
+
71
+ namespace styles {
72
+ export const snippet = style({ maxWidth: '28rem !custom' })
73
+ }
74
+ ```
75
+
76
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Snippet/Prompt.tsx)
77
+
78
+ ### Tabs
79
+
80
+ Pass a [Tabs](/docs/components/tabs) list as `tabs` to switch between commands. Wrap the snippet in `Tabs.Root`, and pass the command of the active tab as `children`.
81
+
82
+ ```tsx
83
+ import { Snippet, style, Tabs } from '@tempoxyz/ds'
84
+ import { useState } from 'react'
85
+
86
+ export const fill = true
87
+ const commands = {
88
+ bun: 'bun add @tempoxyz/ds zyzz',
89
+ npm: 'npm install @tempoxyz/ds zyzz',
90
+ pnpm: 'pnpm add @tempoxyz/ds zyzz',
91
+ } as const
92
+
93
+ const managers = ['npm', 'pnpm', 'bun'] as const
94
+
95
+ export function Demo() {
96
+ const [manager, setManager] = useState<(typeof managers)[number]>('npm')
97
+
98
+ return (
99
+ <Tabs.Root
100
+ onValueChange={(value) => {
101
+ const entry = managers.find((item) => item === value)
102
+ if (entry) setManager(entry)
103
+ }}
104
+ value={manager}
105
+ >
106
+ <Snippet
107
+ labels={{ copy: 'Copy install command' }}
108
+ tabs={
109
+ <Tabs.List aria-label="Package manager">
110
+ {managers.map((entry) => (
111
+ <Tabs.Tab key={entry} value={entry}>
112
+ {entry}
113
+ </Tabs.Tab>
114
+ ))}
115
+ <Tabs.Indicator />
116
+ </Tabs.List>
117
+ }
118
+ {...styles.snippet()}
119
+ >
120
+ {commands[manager]}
121
+ </Snippet>
122
+ </Tabs.Root>
123
+ )
124
+ }
125
+
126
+ namespace styles {
127
+ export const snippet = style({ maxWidth: '28rem !custom' })
128
+ }
129
+ ```
130
+
131
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Snippet/Tabs.tsx)
132
+
133
+ ## Anatomy
134
+
135
+ Import the component and assemble its parts.
136
+
137
+ ```tsx
138
+ import { Snippet, Tabs } from '@tempoxyz/ds'
139
+
140
+ <Tabs.Root>
141
+ <Snippet
142
+ tabs={
143
+ <Tabs.List>
144
+ <Tabs.Tab />
145
+ <Tabs.Indicator />
146
+ </Tabs.List>
147
+ }
148
+ />
149
+ </Tabs.Root>
150
+ ```
151
+
152
+ ## Guidelines
153
+
154
+ ### When to Use
155
+
156
+ * Show a command, such as an install command or a `curl` request.
157
+ * Show a value to paste elsewhere, such as a webhook URL or an RPC endpoint, with `prompt={false}`.
158
+ * Show one command per tool, such as an agent or a package manager, with `tabs`.
159
+
160
+ ### When Not to Use
161
+
162
+ * Use [SecretValue](/docs/components/secret-value) for API keys and other secrets, which stay masked.
163
+ * Use [Hex](/docs/components/hex) with `copy` for addresses and hashes, which truncates the middle to fit.
164
+ * Use [CopyButton](/docs/components/copy-button) to add copying beside your own text.
165
+ * Use [Tabs](/docs/components/tabs) with panels to switch content other than commands.
166
+
167
+ ### Copy Exactly What You Show
168
+
169
+ Snippet copies its `children`, never the prompt.
170
+
171
+ **Do:** Pass only the command, so the pasted text runs as is.
172
+
173
+ ```tsx
174
+ /**
175
+ * Do example for "Copy Exactly What You Show" on the Snippet page.
176
+ * Pass only the command, so the pasted text runs as is.
177
+ * @module
178
+ */
179
+ import { Snippet, style } from '@tempoxyz/ds'
180
+
181
+ /** Renders the static preview, which the guideline card shows inert. */
182
+ export function Demo() {
183
+ return <Snippet {...styles.snippet()}>pnpm add @tempoxyz/ds</Snippet>
184
+ }
185
+
186
+ namespace styles {
187
+ export const snippet = style({ width: '100% !custom' })
188
+ }
189
+ ```
190
+
191
+ **Don't:** Type a leading `$` into the command.
192
+
193
+ ```tsx
194
+ /**
195
+ * Don't example for "Copy Exactly What You Show" on the Snippet page.
196
+ * Don't type a leading `$` into the command.
197
+ * @module
198
+ */
199
+ import { Snippet, style } from '@tempoxyz/ds'
200
+
201
+ /** Renders the static preview, which the guideline card shows inert. */
202
+ export function Demo() {
203
+ return <Snippet {...styles.snippet()}>$ pnpm add @tempoxyz/ds</Snippet>
204
+ }
205
+
206
+ namespace styles {
207
+ export const snippet = style({ width: '100% !custom' })
208
+ }
209
+ ```
210
+
211
+ ### Show the Prompt Only for Shell Commands
212
+
213
+ The `$` marks text to run in a terminal.
214
+
215
+ **Do:** Set `prompt={false}` for a URL or other value.
216
+
217
+ ```tsx
218
+ /**
219
+ * Do example for "Show the Prompt Only for Shell Commands" on the Snippet page.
220
+ * Set `prompt={false}` for a URL or other value.
221
+ * @module
222
+ */
223
+ import { Snippet, style } from '@tempoxyz/ds'
224
+
225
+ /** Renders the static preview, which the guideline card shows inert. */
226
+ export function Demo() {
227
+ return (
228
+ <Snippet prompt={false} {...styles.snippet()}>
229
+ https://example.com/webhooks/tempo
230
+ </Snippet>
231
+ )
232
+ }
233
+
234
+ namespace styles {
235
+ export const snippet = style({ width: '100% !custom' })
236
+ }
237
+ ```
238
+
239
+ **Don't:** Show a shell prompt before a value that isn't a command.
240
+
241
+ ```tsx
242
+ /**
243
+ * Don't example for "Show the Prompt Only for Shell Commands" on the Snippet page.
244
+ * Don't show a shell prompt before a value that isn't a command.
245
+ * @module
246
+ */
247
+ import { Snippet, style } from '@tempoxyz/ds'
248
+
249
+ /** Renders the static preview, which the guideline card shows inert. */
250
+ export function Demo() {
251
+ return <Snippet {...styles.snippet()}>https://example.com/webhooks/tempo</Snippet>
252
+ }
253
+
254
+ namespace styles {
255
+ export const snippet = style({ width: '100% !custom' })
256
+ }
257
+ ```
258
+
259
+ ### Keep One Command per Snippet
260
+
261
+ Long commands wrap. Use a code block for multi-line scripts and config files.
262
+
263
+ **Do:** Split a multi-step setup into one snippet per command.
264
+
265
+ ```tsx
266
+ /**
267
+ * Do example for "Keep One Command per Snippet" on the Snippet page.
268
+ * Split a multi-step setup into one snippet per command.
269
+ * @module
270
+ */
271
+ import { Snippet, style } from '@tempoxyz/ds'
272
+
273
+ /** Renders the static preview, which the guideline card shows inert. */
274
+ export function Demo() {
275
+ return (
276
+ <div {...styles.stack()}>
277
+ <Snippet>pnpm add @tempoxyz/ds</Snippet>
278
+ <Snippet>pnpm build</Snippet>
279
+ <Snippet>pnpm dev</Snippet>
280
+ </div>
281
+ )
282
+ }
283
+
284
+ namespace styles {
285
+ export const stack = style({ display: 'grid', gap: '8', width: '100% !custom' })
286
+ }
287
+ ```
288
+
289
+ **Don't:** Chain several commands into one snippet.
290
+
291
+ ```tsx
292
+ /**
293
+ * Don't example for "Keep One Command per Snippet" on the Snippet page.
294
+ * Don't chain several commands into one snippet.
295
+ * @module
296
+ */
297
+ import { Snippet, style } from '@tempoxyz/ds'
298
+
299
+ /** Renders the static preview, which the guideline card shows inert. */
300
+ export function Demo() {
301
+ return <Snippet {...styles.snippet()}>pnpm add @tempoxyz/ds && pnpm build && pnpm dev</Snippet>
302
+ }
303
+
304
+ namespace styles {
305
+ export const snippet = style({ width: '100% !custom' })
306
+ }
307
+ ```
308
+
309
+ ### Switch Alternatives with Tabs
310
+
311
+ If the command differs per tool, such as a package manager, show one snippet with a tab per tool.
312
+
313
+ **Do:** Show one command per package manager in one snippet with tabs.
314
+
315
+ ```tsx
316
+ /**
317
+ * Do example for "Switch Alternatives with Tabs" on the Snippet page.
318
+ * Show one command per package manager in one snippet with tabs.
319
+ * @module
320
+ */
321
+ import { Snippet, style, Tabs } from '@tempoxyz/ds'
322
+
323
+ /** Renders the static preview, which the guideline card shows inert. */
324
+ export function Demo() {
325
+ return (
326
+ <Tabs.Root defaultValue="npm" {...styles.root()}>
327
+ <Snippet
328
+ tabs={
329
+ <Tabs.List aria-label="Package manager">
330
+ <Tabs.Tab value="npm">npm</Tabs.Tab>
331
+ <Tabs.Tab value="pnpm">pnpm</Tabs.Tab>
332
+ <Tabs.Tab value="bun">bun</Tabs.Tab>
333
+ <Tabs.Indicator />
334
+ </Tabs.List>
335
+ }
336
+ >
337
+ npm install @tempoxyz/ds
338
+ </Snippet>
339
+ </Tabs.Root>
340
+ )
341
+ }
342
+
343
+ namespace styles {
344
+ export const root = style({ width: '100% !custom' })
345
+ }
346
+ ```
347
+
348
+ **Don't:** Stack one snippet per package manager.
349
+
350
+ ```tsx
351
+ /**
352
+ * Don't example for "Switch Alternatives with Tabs" on the Snippet page.
353
+ * Don't stack one snippet per package manager.
354
+ * @module
355
+ */
356
+ import { Snippet, style } from '@tempoxyz/ds'
357
+
358
+ /** Renders the static preview, which the guideline card shows inert. */
359
+ export function Demo() {
360
+ return (
361
+ <div {...styles.stack()}>
362
+ <Snippet>npm install @tempoxyz/ds</Snippet>
363
+ <Snippet>pnpm add @tempoxyz/ds</Snippet>
364
+ <Snippet>bun add @tempoxyz/ds</Snippet>
365
+ </div>
366
+ )
367
+ }
368
+
369
+ namespace styles {
370
+ export const stack = style({ display: 'grid', gap: '8', width: '100% !custom' })
371
+ }
372
+ ```
373
+
374
+ ## Content
375
+
376
+ * Write commands that run unchanged. If a value differs per person, read it from an environment variable, such as `$TEMPO_API_KEY`, not a placeholder.
377
+ * Never put a real secret in a snippet.
378
+ * Introduce each snippet with a sentence that states what it does and where to run it.
379
+
380
+ ## Accessibility
381
+
382
+ The command renders in a `<code>` element. The copy button follows the [WAI-ARIA button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/button/), and the tabs follow the [WAI-ARIA tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/).
383
+
384
+ ### Semantics
385
+
386
+ * **Command (code):** Wraps to its width, so the whole command stays visible without scrolling.
387
+ * **Prompt (aria-hidden):** Hidden from screen readers, and never selected or copied with the command.
388
+ * **Copy Button (button):** Named "Copy" unless you pass `labels.copy`. Reads "Copied" for two seconds, and a hidden live region announces each copy.
389
+ * **Tabs (tablist):** Switches the command in place. A click on a tab never copies, and the list needs its own name.
390
+
391
+ ### Keyboard
392
+
393
+ * **Move Through the Snippet (Tab):** Moves focus to the active tab, if any, then to the copy button.
394
+ * **Switch the Command (Arrow keys):** Moves between tabs. Enter or Space shows the command of the focused tab.
395
+ * **Copy the Command (Enter or Space):** Copies the command from the copy button. Focus stays on the button.
396
+
397
+ A click anywhere on the command also copies, unless your `onClick` calls `preventDefault()`. The button is the keyboard path.
398
+
399
+ ### Your Responsibilities
400
+
401
+ * **Label Each Snippet (labels.copy):** If a page shows several snippets, put a visible heading before each, and name each copy button, such as "Copy install command".
402
+ * **Name the Tabs (SC 4.1.2):** Name the tab list, such as "Package manager", and pass the command of the active tab as the children.
403
+
404
+ ## Related
405
+
406
+ [CopyButton](/docs/components/copy-button)
407
+
408
+ Use for copying beside your own layout.
409
+
410
+ [SecretValue](/docs/components/secret-value)
411
+
412
+ Use for secrets.
413
+
414
+ [Hex](/docs/components/hex)
415
+
416
+ Use for addresses and hashes.
417
+
418
+ [Kbd](/docs/components/kbd)
419
+
420
+ Use for keyboard shortcuts.
421
+
422
+ [Tabs](/docs/components/tabs)
423
+
424
+ Use for switching between related views.
@@ -0,0 +1,216 @@
1
+ # Spinner
2
+
3
+ Shows that something is loading.
4
+
5
+ [View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/Spinner.tsx)
6
+
7
+ Figma: [Spinner](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=1186-3872)
8
+
9
+ ## Overview
10
+
11
+ An indeterminate loading indicator whose arc draws in the current text color, optionally on an elevated tile. Pass `aria-label` to name what loads.
12
+
13
+ ## Examples
14
+
15
+ ### Basic
16
+
17
+ ```tsx
18
+ import { Spinner } from '@tempoxyz/ds'
19
+
20
+ export function Demo() {
21
+ return <Spinner aria-label="Loading transactions" />
22
+ }
23
+ ```
24
+
25
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Spinner/Basic.tsx)
26
+
27
+ ### Elevated
28
+
29
+ Set `elevated` to place the spinner on a 44px raised tile with a shadow.
30
+
31
+ ```tsx
32
+ import { Spinner } from '@tempoxyz/ds'
33
+
34
+ export function Demo() {
35
+ return <Spinner aria-label="Loading quote" elevated />
36
+ }
37
+ ```
38
+
39
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Spinner/Elevated.tsx)
40
+
41
+ ### Size
42
+
43
+ Set `size="40"` for a 40px ring with a thicker stroke, such as a spinner that fills an empty panel.
44
+
45
+ ```tsx
46
+ /** Renders the 16px and 40px spinner rings side by side. @module */
47
+ import { Spinner } from '@tempoxyz/ds'
48
+
49
+ export function Demo() {
50
+ return (
51
+ <div style={{ alignItems: 'center', display: 'flex', gap: 24 }}>
52
+ <Spinner aria-label="Loading balance" />
53
+ <Spinner aria-label="Loading activity" size="40" />
54
+ </div>
55
+ )
56
+ }
57
+ ```
58
+
59
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Spinner/Size.tsx)
60
+
61
+ ## Anatomy
62
+
63
+ Import the component and assemble its parts.
64
+
65
+ ```tsx
66
+ import { Spinner } from '@tempoxyz/ds'
67
+
68
+ <Spinner />
69
+ ```
70
+
71
+ ## Guidelines
72
+
73
+ ### When to Use
74
+
75
+ * Mark a region busy for a short, unknown time, such as a table that refreshes after a filter change.
76
+ * Mark a small area that fetches data, such as a quote card or an address lookup.
77
+ * Hold the place of a result with no predictable shape, such as a quote that returns a price or an error.
78
+
79
+ ### When Not to Use
80
+
81
+ * Use `loading` on a [Button](/docs/components/button) while its action runs, not a spinner beside it.
82
+ * Use [Skeleton](/docs/components/skeleton) for content with a known shape, such as activity rows or metric tiles.
83
+ * Use [Progress](/docs/components/progress) for measurable work, or any wait over about 10 seconds.
84
+ * Use [LoadingDots](/docs/components/loading-dots) for a short wait inside a line of text.
85
+
86
+ ### Match the Indicator to the Wait
87
+
88
+ * **Under About 300 ms (nothing):** Show nothing. Render the result when it arrives.
89
+ * **About 300 ms to 10 Seconds (Spinner):** Mark the busy region or control.
90
+ * **Over About 10 Seconds (Progress):** Show a value for measurable work.
91
+
92
+ Once a spinner appears, keep it for at least about 500 ms so it doesn't flash. For whole pages, see [Loading](/docs/patterns/loading).
93
+
94
+ ### Use One Spinner per Region
95
+
96
+ **Do:** Center one spinner where the content will appear, and keep loaded content visible.
97
+
98
+ ```tsx
99
+ /**
100
+ * Do example for "Use One Spinner per Region" on the Spinner page.
101
+ * Center one spinner where the content will appear, and keep loaded content visible.
102
+ * @module
103
+ */
104
+ import { Spinner, style, Surface } from '@tempoxyz/ds'
105
+
106
+ /** Renders the static preview, which the guideline card shows inert. */
107
+ export function Demo() {
108
+ return (
109
+ <div {...styles.page()}>
110
+ <Surface size="small" {...styles.card()}>
111
+ <div {...styles.label()}>Balance</div>
112
+ <div {...styles.value()}>1,250.00 USDC</div>
113
+ </Surface>
114
+ <Surface size="small" {...styles.region()}>
115
+ <Spinner aria-label="Loading activity" />
116
+ </Surface>
117
+ </div>
118
+ )
119
+ }
120
+
121
+ namespace styles {
122
+ export const card = style({ display: 'flex', flexDirection: 'column', gap: '4' })
123
+
124
+ export const label = style({ color: 'content.secondary', typography: 'body.b3' })
125
+
126
+ export const page = style({
127
+ display: 'flex',
128
+ flexDirection: 'column',
129
+ gap: '12',
130
+ width: '100% !custom',
131
+ })
132
+
133
+ export const region = style({
134
+ alignItems: 'center',
135
+ display: 'flex',
136
+ height: '6rem !custom',
137
+ justifyContent: 'center',
138
+ })
139
+
140
+ export const value = style({ color: 'content.primary', typography: 'heading.h2' })
141
+ }
142
+ ```
143
+
144
+ **Don't:** Cover the whole page, which blocks content that already loaded.
145
+
146
+ ```tsx
147
+ /**
148
+ * Don't example for "Use One Spinner per Region" on the Spinner page.
149
+ * Don't cover the whole page, which blocks content that already loaded.
150
+ * @module
151
+ */
152
+ import { Spinner, style, Surface } from '@tempoxyz/ds'
153
+
154
+ /** Renders the static preview, which the guideline card shows inert. */
155
+ export function Demo() {
156
+ return (
157
+ <Surface size="small" {...styles.page()}>
158
+ <Spinner aria-label="Loading" />
159
+ </Surface>
160
+ )
161
+ }
162
+
163
+ namespace styles {
164
+ export const page = style({
165
+ alignItems: 'center',
166
+ boxSizing: 'border-box',
167
+ display: 'flex',
168
+ height: '10rem !custom',
169
+ justifyContent: 'center',
170
+ width: '100% !custom',
171
+ })
172
+ }
173
+ ```
174
+
175
+ ### Place It Beside a Status Line
176
+
177
+ Show the spinner inline beside a short status line, such as Approving. For a pending action, use `loading` on a [Button](/docs/components/button).
178
+
179
+ ## Accessibility
180
+
181
+ A spinner renders an indeterminate `progressbar` named "Loading". It is not a live region, so it is read only when the reader reaches it.
182
+
183
+ ### Semantics
184
+
185
+ * **Root (progressbar):** Named "Loading", with no value, so it reads as indeterminate.
186
+ * **Custom Name (aria-label):** Replaces the default name.
187
+ * **Ring (aria-hidden):** A hidden SVG that stops rotating under reduced motion.
188
+ * **Color (3:1):** Uses the text color. Keep 3:1 contrast against the background.
189
+
190
+ ### Your Responsibilities
191
+
192
+ * **Say What Is Loading (aria-label):** Name what loads, such as "Loading transactions". Several spinners named "Loading" are indistinguishable.
193
+ * **Announce the Result (SC 4.1.3):** A spinner isn't announced. Announce the result or an error in a status message or a [Toast](/docs/components/toast).
194
+ * **Mark the Region Busy (aria-busy):** Set `aria-busy` on the loading region, so assistive technology waits for its content.
195
+
196
+ ## Related
197
+
198
+ [Skeleton](/docs/components/skeleton)
199
+
200
+ Use for content with a known shape.
201
+
202
+ [Progress](/docs/components/progress)
203
+
204
+ Use for measurable work.
205
+
206
+ [LoadingDots](/docs/components/loading-dots)
207
+
208
+ Use for a wait inside a line of text.
209
+
210
+ [Button](/docs/components/button)
211
+
212
+ Use for an action's loading state.
213
+
214
+ [Loading](/docs/patterns/loading)
215
+
216
+ Covers choosing a loading indicator.