@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,922 @@
1
+ # SidebarMenu
2
+
3
+ Vertical navigation for sidebars and drawers.
4
+
5
+ [View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/SidebarMenu.tsx)
6
+
7
+ Figma: [Buttons/Floating Action Button](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5265-2305) (`SidebarMenu.Item`), [Navigation/Account Row](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-3036) (`SidebarMenu.AccountRow`), [Navigation/Nav Item](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-2694) (`SidebarMenu.Item`), [Navigation/Sidebar Promo](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5629-2524) (`SidebarMenu.Promo`), [Navigation/Tab Bar Item](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5265-2279) (`SidebarMenu.Item`), [Navigation/Wallet Sidebar](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-2727) (`SidebarMenu.Root`)
8
+
9
+ ## Overview
10
+
11
+ SidebarMenu renders vertical navigation of icon and label rows, for sidebars and drawers. Items are links, not menu items.
12
+
13
+ ## Examples
14
+
15
+ ### Basic
16
+
17
+ ```tsx
18
+ import { SidebarMenu, style } from '@tempoxyz/ds'
19
+ import { Key, Settings, Users } from '@tempoxyz/ds/icons'
20
+
21
+ export function Demo() {
22
+ return (
23
+ <SidebarMenu.Root aria-label="Console" {...styles.column()}>
24
+ <SidebarMenu.Group>
25
+ <SidebarMenu.GroupLabel>Project</SidebarMenu.GroupLabel>
26
+ <SidebarMenu.Item href="#">
27
+ <Key />
28
+ API Keys
29
+ </SidebarMenu.Item>
30
+ </SidebarMenu.Group>
31
+ <SidebarMenu.Group>
32
+ <SidebarMenu.GroupLabel>Organization</SidebarMenu.GroupLabel>
33
+ <SidebarMenu.Item active href="#">
34
+ <Users />
35
+ Team
36
+ </SidebarMenu.Item>
37
+ <SidebarMenu.Item render={<button type="button" />}>
38
+ <Settings />
39
+ Settings
40
+ </SidebarMenu.Item>
41
+ </SidebarMenu.Group>
42
+ </SidebarMenu.Root>
43
+ )
44
+ }
45
+
46
+ namespace styles {
47
+ export const column = style({ width: '15rem !custom' })
48
+ }
49
+ ```
50
+
51
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/SidebarMenu/Basic.tsx)
52
+
53
+ ### Size
54
+
55
+ Set `size` on `SidebarMenu.Root` to `small`, `medium` (default), or `large`.
56
+
57
+ ```tsx
58
+ import { SidebarMenu, style } from '@tempoxyz/ds'
59
+ import { Coins, CreditCard, Home, ShieldCheck, Trend } from '@tempoxyz/ds/icons'
60
+
61
+ export function Demo() {
62
+ return (
63
+ <div {...styles.row()}>
64
+ {(['small', 'medium', 'large'] as const).map((size) => (
65
+ <SidebarMenu.Root aria-label={`Wallet ${size}`} key={size} size={size} {...styles.column()}>
66
+ <SidebarMenu.Item active href="#">
67
+ <Home />
68
+ Home
69
+ </SidebarMenu.Item>
70
+ <SidebarMenu.Item href="#">
71
+ <Coins />
72
+ Balances
73
+ </SidebarMenu.Item>
74
+ <SidebarMenu.Item href="#">
75
+ <Trend />
76
+ Activity
77
+ </SidebarMenu.Item>
78
+ <SidebarMenu.Item href="#">
79
+ <CreditCard />
80
+ Credits
81
+ </SidebarMenu.Item>
82
+ <SidebarMenu.Item href="#">
83
+ <ShieldCheck />
84
+ Authorized Apps
85
+ </SidebarMenu.Item>
86
+ </SidebarMenu.Root>
87
+ ))}
88
+ </div>
89
+ )
90
+ }
91
+
92
+ namespace styles {
93
+ export const column = style({ width: '15rem !custom' })
94
+
95
+ export const row = style({ display: 'flex', flexWrap: 'wrap', gap: '32' })
96
+ }
97
+ ```
98
+
99
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/SidebarMenu/Size.tsx)
100
+
101
+ ### Submenu
102
+
103
+ Nest items in a `SidebarMenu.Submenu`. Set `defaultOpen` to expand it on first render.
104
+
105
+ ```tsx
106
+ import { SidebarMenu, style } from '@tempoxyz/ds'
107
+ import {
108
+ Bank,
109
+ Bell,
110
+ ChartBar,
111
+ Document,
112
+ GasFee,
113
+ Globe,
114
+ Grid,
115
+ Key,
116
+ Link,
117
+ Lock,
118
+ Mail,
119
+ Receipt,
120
+ Users,
121
+ Wallet,
122
+ } from '@tempoxyz/ds/icons'
123
+
124
+ export function Demo() {
125
+ return (
126
+ <SidebarMenu.Root aria-label="Dashboard" size="small" {...styles.column()}>
127
+ <SidebarMenu.Item active href="#">
128
+ <Grid />
129
+ Overview
130
+ </SidebarMenu.Item>
131
+ <SidebarMenu.Item href="#">
132
+ <ChartBar />
133
+ Analytics
134
+ </SidebarMenu.Item>
135
+ <SidebarMenu.Submenu defaultOpen>
136
+ <SidebarMenu.SubmenuTrigger>
137
+ <Receipt />
138
+ Transactions
139
+ </SidebarMenu.SubmenuTrigger>
140
+ <SidebarMenu.SubmenuPanel>
141
+ <SidebarMenu.Item href="#">Incoming</SidebarMenu.Item>
142
+ <SidebarMenu.Item href="#">Outgoing</SidebarMenu.Item>
143
+ <SidebarMenu.Item href="#">Refunds</SidebarMenu.Item>
144
+ </SidebarMenu.SubmenuPanel>
145
+ </SidebarMenu.Submenu>
146
+ <SidebarMenu.Group>
147
+ <SidebarMenu.GroupLabel>Payments</SidebarMenu.GroupLabel>
148
+ <SidebarMenu.Item href="#">
149
+ <Wallet />
150
+ Wallets
151
+ </SidebarMenu.Item>
152
+ <SidebarMenu.Item href="#">
153
+ <Bank />
154
+ Payouts
155
+ </SidebarMenu.Item>
156
+ <SidebarMenu.Item href="#">
157
+ <GasFee />
158
+ Fee payer
159
+ </SidebarMenu.Item>
160
+ <SidebarMenu.Item href="#">
161
+ <Link />
162
+ Payment links
163
+ </SidebarMenu.Item>
164
+ </SidebarMenu.Group>
165
+ <SidebarMenu.Group>
166
+ <SidebarMenu.GroupLabel>Developers</SidebarMenu.GroupLabel>
167
+ <SidebarMenu.Item href="#">
168
+ <Key />
169
+ API keys
170
+ </SidebarMenu.Item>
171
+ <SidebarMenu.Item href="#">
172
+ <Globe />
173
+ Webhooks
174
+ </SidebarMenu.Item>
175
+ <SidebarMenu.Item href="#">
176
+ <Document />
177
+ Docs
178
+ </SidebarMenu.Item>
179
+ </SidebarMenu.Group>
180
+ <SidebarMenu.Group>
181
+ <SidebarMenu.GroupLabel>Settings</SidebarMenu.GroupLabel>
182
+ <SidebarMenu.Item href="#">
183
+ <Users />
184
+ Team
185
+ </SidebarMenu.Item>
186
+ <SidebarMenu.Item href="#">
187
+ <Lock />
188
+ Security
189
+ </SidebarMenu.Item>
190
+ <SidebarMenu.Item href="#">
191
+ <Bell />
192
+ Notifications
193
+ </SidebarMenu.Item>
194
+ <SidebarMenu.Item href="#">
195
+ <Mail />
196
+ Emails
197
+ </SidebarMenu.Item>
198
+ </SidebarMenu.Group>
199
+ </SidebarMenu.Root>
200
+ )
201
+ }
202
+
203
+ namespace styles {
204
+ export const column = style({ width: '15rem !custom' })
205
+ }
206
+ ```
207
+
208
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/SidebarMenu/Submenu.tsx)
209
+
210
+ ### Account Row
211
+
212
+ Render a `SidebarMenu.AccountRow` in a `SidebarMenu.Account`, which draws the divider above it at the bottom of the sidebar. Pass the row to `Menu.Trigger` through `render` to open the account menu. The row ends in a chevron only while it opens a menu.
213
+
214
+ ```tsx
215
+ /** Shows the account row at the bottom of a sidebar, under its divider, opening the account menu. @module */
216
+ import { Avatar, Menu, SidebarMenu, style } from '@tempoxyz/ds'
217
+ import { Copy, Help, LogOut, User } from '@tempoxyz/ds/icons'
218
+
219
+ /** Renders the help link, then the account row as the trigger of the account menu. */
220
+ export function Demo() {
221
+ return (
222
+ <div {...styles.column()}>
223
+ <SidebarMenu.Root aria-label="Support">
224
+ <SidebarMenu.Item href="#">
225
+ <Help />
226
+ Help
227
+ </SidebarMenu.Item>
228
+ </SidebarMenu.Root>
229
+ <SidebarMenu.Account>
230
+ <Menu.Root>
231
+ <Menu.Trigger
232
+ render={<SidebarMenu.AccountRow description="piril@tempo.xyz" label="Piril Akay" />}
233
+ >
234
+ <Avatar size="32">
235
+ <Avatar.Fallback>PA</Avatar.Fallback>
236
+ </Avatar>
237
+ </Menu.Trigger>
238
+ <Menu.Popup side="top">
239
+ <Menu.Item>
240
+ <User />
241
+ Account details
242
+ </Menu.Item>
243
+ <Menu.Item>
244
+ <Copy />
245
+ Copy address
246
+ </Menu.Item>
247
+ <Menu.Separator />
248
+ <Menu.Item intent="negative">
249
+ <LogOut />
250
+ Sign out
251
+ </Menu.Item>
252
+ </Menu.Popup>
253
+ </Menu.Root>
254
+ </SidebarMenu.Account>
255
+ </div>
256
+ )
257
+ }
258
+
259
+ namespace styles {
260
+ export const column = style({
261
+ display: 'flex',
262
+ flexDirection: 'column',
263
+ rowGap: '8',
264
+ width: '15rem !custom',
265
+ })
266
+ }
267
+ ```
268
+
269
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/SidebarMenu/AccountRow.tsx)
270
+
271
+ ### Logo
272
+
273
+ Pass a `SidebarMenu.Logo` to an account row that names a product or wallet, such as the wallet switcher at the top of the sidebar.
274
+
275
+ ```tsx
276
+ /** Shows the wallet switcher at the top of a sidebar, with the Tempo mark in a logo tile. @module */
277
+ import { Menu, SidebarMenu, style, TempoLogo } from '@tempoxyz/ds'
278
+ import { Plus, Wallet } from '@tempoxyz/ds/icons'
279
+
280
+ /** Renders the wallet row, with the logo tile as its media, as the trigger of the wallet menu. */
281
+ export function Demo() {
282
+ return (
283
+ <div {...styles.column()}>
284
+ <Menu.Root>
285
+ <Menu.Trigger
286
+ render={<SidebarMenu.AccountRow description="0xc721…s9dk" label="Tempo Wallet" />}
287
+ >
288
+ <SidebarMenu.Logo>
289
+ <TempoLogo type="mark" />
290
+ </SidebarMenu.Logo>
291
+ </Menu.Trigger>
292
+ <Menu.Popup>
293
+ <Menu.Item>
294
+ <Wallet />
295
+ Savings
296
+ </Menu.Item>
297
+ <Menu.Separator />
298
+ <Menu.Item>
299
+ <Plus />
300
+ New wallet
301
+ </Menu.Item>
302
+ </Menu.Popup>
303
+ </Menu.Root>
304
+ </div>
305
+ )
306
+ }
307
+
308
+ namespace styles {
309
+ export const column = style({ width: '15rem !custom' })
310
+ }
311
+ ```
312
+
313
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/SidebarMenu/Logo.tsx)
314
+
315
+ ## Anatomy
316
+
317
+ Import the component and assemble its parts.
318
+
319
+ ```tsx
320
+ import { SidebarMenu } from '@tempoxyz/ds'
321
+
322
+ <SidebarMenu.Root>
323
+ <SidebarMenu.Group>
324
+ <SidebarMenu.GroupLabel />
325
+ <SidebarMenu.Item />
326
+ <SidebarMenu.Submenu>
327
+ <SidebarMenu.SubmenuTrigger />
328
+ <SidebarMenu.SubmenuPanel>
329
+ <SidebarMenu.Item />
330
+ </SidebarMenu.SubmenuPanel>
331
+ </SidebarMenu.Submenu>
332
+ </SidebarMenu.Group>
333
+ </SidebarMenu.Root>
334
+ <SidebarMenu.AccountRow>
335
+ <SidebarMenu.Logo />
336
+ </SidebarMenu.AccountRow>
337
+ <SidebarMenu.Account>
338
+ <SidebarMenu.AccountRow />
339
+ </SidebarMenu.Account>
340
+ <SidebarMenu.Mark />
341
+ <SidebarMenu.Promo />
342
+ ```
343
+
344
+ ## Guidelines
345
+
346
+ ### When to Use
347
+
348
+ * Navigate a product with five or more destinations, such as Home, API keys, Webhooks, Team, and Settings.
349
+ * Navigate inside `AppShell.Sidebar` ([AppShell](/docs/components/app-shell)), which moves the menu into a drawer below 1024px.
350
+
351
+ ### When Not to Use
352
+
353
+ * Use [Menu](/docs/components/menu) for a list of actions, such as Rename key or Revoke key.
354
+ * Use [Tabs](/docs/components/tabs) to switch views inside one page.
355
+ * Use [Breadcrumbs](/docs/components/breadcrumbs) to show where a deeply nested page sits.
356
+ * Use [ListGroup](/docs/components/list-group) for a settings page that lists its subpages in the content.
357
+
358
+ ### Group Related Destinations
359
+
360
+ Wrap related items in a `SidebarMenu.Group` with a `SidebarMenu.GroupLabel`, such as Payments or Developers. Adjacent groups render with space between them.
361
+
362
+ If the first group holds the main pages, such as Home, it can skip its label. Give it an `aria-label` instead.
363
+
364
+ **Do:** Group items under short labels.
365
+
366
+ ```tsx
367
+ /** Do example for "Group Related Destinations" on the SidebarMenu page: group items under short labels. @module */
368
+ import { SidebarMenu, style } from '@tempoxyz/ds'
369
+ import { Bank, Globe, Grid, Key, Wallet } from '@tempoxyz/ds/icons'
370
+
371
+ /** Renders the static preview, which the guideline card shows inert. */
372
+ export function Demo() {
373
+ return (
374
+ <SidebarMenu.Root aria-label="Console" size="small" {...styles.column()}>
375
+ <SidebarMenu.Group aria-label="Project">
376
+ <SidebarMenu.Item active href="#">
377
+ <Grid />
378
+ Home
379
+ </SidebarMenu.Item>
380
+ </SidebarMenu.Group>
381
+ <SidebarMenu.Group>
382
+ <SidebarMenu.GroupLabel>Payments</SidebarMenu.GroupLabel>
383
+ <SidebarMenu.Item href="#">
384
+ <Wallet />
385
+ Wallets
386
+ </SidebarMenu.Item>
387
+ <SidebarMenu.Item href="#">
388
+ <Bank />
389
+ Payouts
390
+ </SidebarMenu.Item>
391
+ </SidebarMenu.Group>
392
+ <SidebarMenu.Group>
393
+ <SidebarMenu.GroupLabel>Developers</SidebarMenu.GroupLabel>
394
+ <SidebarMenu.Item href="#">
395
+ <Key />
396
+ API keys
397
+ </SidebarMenu.Item>
398
+ <SidebarMenu.Item href="#">
399
+ <Globe />
400
+ Webhooks
401
+ </SidebarMenu.Item>
402
+ </SidebarMenu.Group>
403
+ </SidebarMenu.Root>
404
+ )
405
+ }
406
+
407
+ namespace styles {
408
+ export const column = style({ width: '15rem !custom' })
409
+ }
410
+ ```
411
+
412
+ **Don't:** List unrelated destinations in one flat column.
413
+
414
+ ```tsx
415
+ /**
416
+ * Don't example for "Group Related Destinations" on the SidebarMenu page.
417
+ * Don't list unrelated destinations in one flat column.
418
+ * @module
419
+ */
420
+ import { SidebarMenu, style } from '@tempoxyz/ds'
421
+ import { Bank, Bell, Globe, Grid, Key, Lock, Users, Wallet } from '@tempoxyz/ds/icons'
422
+
423
+ /** Renders the static preview, which the guideline card shows inert. */
424
+ export function Demo() {
425
+ return (
426
+ <SidebarMenu.Root aria-label="Console" size="small" {...styles.column()}>
427
+ <SidebarMenu.Item active href="#">
428
+ <Grid />
429
+ Home
430
+ </SidebarMenu.Item>
431
+ <SidebarMenu.Item href="#">
432
+ <Wallet />
433
+ Wallets
434
+ </SidebarMenu.Item>
435
+ <SidebarMenu.Item href="#">
436
+ <Key />
437
+ API keys
438
+ </SidebarMenu.Item>
439
+ <SidebarMenu.Item href="#">
440
+ <Users />
441
+ Team
442
+ </SidebarMenu.Item>
443
+ <SidebarMenu.Item href="#">
444
+ <Bank />
445
+ Payouts
446
+ </SidebarMenu.Item>
447
+ <SidebarMenu.Item href="#">
448
+ <Bell />
449
+ Notifications
450
+ </SidebarMenu.Item>
451
+ <SidebarMenu.Item href="#">
452
+ <Globe />
453
+ Webhooks
454
+ </SidebarMenu.Item>
455
+ <SidebarMenu.Item href="#">
456
+ <Lock />
457
+ Security
458
+ </SidebarMenu.Item>
459
+ </SidebarMenu.Root>
460
+ )
461
+ }
462
+
463
+ namespace styles {
464
+ export const column = style({ width: '15rem !custom' })
465
+ }
466
+ ```
467
+
468
+ ### Keep Item Counts Low
469
+
470
+ * **Per group:** Three to seven items, in at most four or five groups.
471
+ * **Single items:** Avoid groups of one, except an unlabelled first group.
472
+ * **Long menus:** If a menu has 15 or more items, set `size="small"` for 32px rows and 16px icons.
473
+ * **Overflow:** Move rare pages onto a hub page built from [ListGroup](/docs/components/list-group), such as Settings.
474
+
475
+ **Do:** Put several related items in each group.
476
+
477
+ ```tsx
478
+ /**
479
+ * Do example for "Keep Item Counts Low" on the SidebarMenu page.
480
+ * Put several related items in each group.
481
+ * @module
482
+ */
483
+ import { SidebarMenu, style } from '@tempoxyz/ds'
484
+ import { Bank, Globe, Key, Wallet } from '@tempoxyz/ds/icons'
485
+
486
+ /** Renders the static preview, which the guideline card shows inert. */
487
+ export function Demo() {
488
+ return (
489
+ <SidebarMenu.Root aria-label="Console" size="small" {...styles.column()}>
490
+ <SidebarMenu.Group>
491
+ <SidebarMenu.GroupLabel>Payments</SidebarMenu.GroupLabel>
492
+ <SidebarMenu.Item href="#">
493
+ <Wallet />
494
+ Wallets
495
+ </SidebarMenu.Item>
496
+ <SidebarMenu.Item href="#">
497
+ <Bank />
498
+ Payouts
499
+ </SidebarMenu.Item>
500
+ </SidebarMenu.Group>
501
+ <SidebarMenu.Group>
502
+ <SidebarMenu.GroupLabel>Developers</SidebarMenu.GroupLabel>
503
+ <SidebarMenu.Item href="#">
504
+ <Key />
505
+ API keys
506
+ </SidebarMenu.Item>
507
+ <SidebarMenu.Item href="#">
508
+ <Globe />
509
+ Webhooks
510
+ </SidebarMenu.Item>
511
+ </SidebarMenu.Group>
512
+ </SidebarMenu.Root>
513
+ )
514
+ }
515
+
516
+ namespace styles {
517
+ export const column = style({ width: '15rem !custom' })
518
+ }
519
+ ```
520
+
521
+ **Don't:** Give each item a group of its own.
522
+
523
+ ```tsx
524
+ /**
525
+ * Don't example for "Keep Item Counts Low" on the SidebarMenu page.
526
+ * Don't give each item a group of its own.
527
+ * @module
528
+ */
529
+ import { SidebarMenu, style } from '@tempoxyz/ds'
530
+ import { Bank, Globe, Key, Wallet } from '@tempoxyz/ds/icons'
531
+
532
+ /** Renders the static preview, which the guideline card shows inert. */
533
+ export function Demo() {
534
+ return (
535
+ <SidebarMenu.Root aria-label="Console" size="small" {...styles.column()}>
536
+ <SidebarMenu.Group>
537
+ <SidebarMenu.GroupLabel>Treasury</SidebarMenu.GroupLabel>
538
+ <SidebarMenu.Item href="#">
539
+ <Wallet />
540
+ Wallets
541
+ </SidebarMenu.Item>
542
+ </SidebarMenu.Group>
543
+ <SidebarMenu.Group>
544
+ <SidebarMenu.GroupLabel>Payments</SidebarMenu.GroupLabel>
545
+ <SidebarMenu.Item href="#">
546
+ <Bank />
547
+ Payouts
548
+ </SidebarMenu.Item>
549
+ </SidebarMenu.Group>
550
+ <SidebarMenu.Group>
551
+ <SidebarMenu.GroupLabel>Access</SidebarMenu.GroupLabel>
552
+ <SidebarMenu.Item href="#">
553
+ <Key />
554
+ API keys
555
+ </SidebarMenu.Item>
556
+ </SidebarMenu.Group>
557
+ <SidebarMenu.Group>
558
+ <SidebarMenu.GroupLabel>Integrations</SidebarMenu.GroupLabel>
559
+ <SidebarMenu.Item href="#">
560
+ <Globe />
561
+ Webhooks
562
+ </SidebarMenu.Item>
563
+ </SidebarMenu.Group>
564
+ </SidebarMenu.Root>
565
+ )
566
+ }
567
+
568
+ namespace styles {
569
+ export const column = style({ width: '15rem !custom' })
570
+ }
571
+ ```
572
+
573
+ ### Nest One Level at Most
574
+
575
+ A `SidebarMenu.Submenu` collapses related pages under a trigger. Open the submenu that holds the current page with `defaultOpen` or a controlled `open`. The trigger only expands and collapses, so give its pages their own items.
576
+
577
+ **Do:** Nest one level, such as Incoming and Outgoing under Transactions, without icons.
578
+
579
+ ```tsx
580
+ /**
581
+ * Do example for "Nest One Level at Most" on the SidebarMenu page.
582
+ * Nest one level, such as Incoming and Outgoing under Transactions, without icons.
583
+ * @module
584
+ */
585
+ import { SidebarMenu, style } from '@tempoxyz/ds'
586
+ import { Grid, Receipt } from '@tempoxyz/ds/icons'
587
+
588
+ /** Renders the static preview, which the guideline card shows inert. */
589
+ export function Demo() {
590
+ return (
591
+ <SidebarMenu.Root aria-label="Console" size="small" {...styles.column()}>
592
+ <SidebarMenu.Item href="#">
593
+ <Grid />
594
+ Home
595
+ </SidebarMenu.Item>
596
+ <SidebarMenu.Submenu defaultOpen>
597
+ <SidebarMenu.SubmenuTrigger>
598
+ <Receipt />
599
+ Transactions
600
+ </SidebarMenu.SubmenuTrigger>
601
+ <SidebarMenu.SubmenuPanel>
602
+ <SidebarMenu.Item active href="#">
603
+ Incoming
604
+ </SidebarMenu.Item>
605
+ <SidebarMenu.Item href="#">Outgoing</SidebarMenu.Item>
606
+ </SidebarMenu.SubmenuPanel>
607
+ </SidebarMenu.Submenu>
608
+ </SidebarMenu.Root>
609
+ )
610
+ }
611
+
612
+ namespace styles {
613
+ export const column = style({ width: '15rem !custom' })
614
+ }
615
+ ```
616
+
617
+ **Don't:** Nest a submenu inside another submenu.
618
+
619
+ ```tsx
620
+ /**
621
+ * Don't example for "Nest One Level at Most" on the SidebarMenu page.
622
+ * Don't nest a submenu inside another submenu.
623
+ * @module
624
+ */
625
+ import { SidebarMenu, style } from '@tempoxyz/ds'
626
+ import { Grid, Receipt } from '@tempoxyz/ds/icons'
627
+
628
+ /** Renders the static preview, which the guideline card shows inert. */
629
+ export function Demo() {
630
+ return (
631
+ <SidebarMenu.Root aria-label="Console" size="small" {...styles.column()}>
632
+ <SidebarMenu.Item href="#">
633
+ <Grid />
634
+ Home
635
+ </SidebarMenu.Item>
636
+ <SidebarMenu.Submenu defaultOpen>
637
+ <SidebarMenu.SubmenuTrigger>
638
+ <Receipt />
639
+ Transactions
640
+ </SidebarMenu.SubmenuTrigger>
641
+ <SidebarMenu.SubmenuPanel>
642
+ <SidebarMenu.Submenu defaultOpen>
643
+ <SidebarMenu.SubmenuTrigger>Incoming</SidebarMenu.SubmenuTrigger>
644
+ <SidebarMenu.SubmenuPanel>
645
+ <SidebarMenu.Item active href="#">
646
+ Pending
647
+ </SidebarMenu.Item>
648
+ <SidebarMenu.Item href="#">Settled</SidebarMenu.Item>
649
+ </SidebarMenu.SubmenuPanel>
650
+ </SidebarMenu.Submenu>
651
+ <SidebarMenu.Item href="#">Outgoing</SidebarMenu.Item>
652
+ </SidebarMenu.SubmenuPanel>
653
+ </SidebarMenu.Submenu>
654
+ </SidebarMenu.Root>
655
+ )
656
+ }
657
+
658
+ namespace styles {
659
+ export const column = style({ width: '15rem !custom' })
660
+ }
661
+ ```
662
+
663
+ ### Keep Labels on One Line
664
+
665
+ Items do not wrap. Give every top-level item an icon, or none of them. Mark a link that leaves the product, such as API docs, with a trailing `ExternalLink` icon.
666
+
667
+ **Do:** Shorten each label to the page's name.
668
+
669
+ ```tsx
670
+ /**
671
+ * Do example for "Keep Labels on One Line" on the SidebarMenu page.
672
+ * Shorten each label to the page's name.
673
+ * @module
674
+ */
675
+ import { SidebarMenu, style } from '@tempoxyz/ds'
676
+ import { Globe, Grid, Key } from '@tempoxyz/ds/icons'
677
+
678
+ /** Renders the static preview, which the guideline card shows inert. */
679
+ export function Demo() {
680
+ return (
681
+ <SidebarMenu.Root aria-label="Console" size="small" {...styles.column()}>
682
+ <SidebarMenu.Item href="#">
683
+ <Grid />
684
+ Home
685
+ </SidebarMenu.Item>
686
+ <SidebarMenu.Item active href="#">
687
+ <Key />
688
+ API keys
689
+ </SidebarMenu.Item>
690
+ <SidebarMenu.Item href="#">
691
+ <Globe />
692
+ Webhooks
693
+ </SidebarMenu.Item>
694
+ </SidebarMenu.Root>
695
+ )
696
+ }
697
+
698
+ namespace styles {
699
+ export const column = style({ width: '15rem !custom' })
700
+ }
701
+ ```
702
+
703
+ **Don't:** Write labels longer than the sidebar is wide.
704
+
705
+ ```tsx
706
+ /**
707
+ * Don't example for "Keep Labels on One Line" on the SidebarMenu page.
708
+ * Don't write labels longer than the sidebar is wide.
709
+ * @module
710
+ */
711
+ import { SidebarMenu, style } from '@tempoxyz/ds'
712
+ import { Globe, Grid, Key } from '@tempoxyz/ds/icons'
713
+
714
+ /** Renders the static preview, which the guideline card shows inert. */
715
+ export function Demo() {
716
+ return (
717
+ <SidebarMenu.Root aria-label="Console" size="small" {...styles.column()}>
718
+ <SidebarMenu.Item href="#">
719
+ <Grid />
720
+ Home
721
+ </SidebarMenu.Item>
722
+ <SidebarMenu.Item active href="#">
723
+ <Key />
724
+ Manage your API keys and secrets
725
+ </SidebarMenu.Item>
726
+ <SidebarMenu.Item href="#">
727
+ <Globe />
728
+ Webhooks
729
+ </SidebarMenu.Item>
730
+ </SidebarMenu.Root>
731
+ )
732
+ }
733
+
734
+ namespace styles {
735
+ export const column = style({ width: '15rem !custom' })
736
+ }
737
+ ```
738
+
739
+ ### Put Workspace and Account Controls at the Edges
740
+
741
+ In an [AppShell](/docs/components/app-shell), put the project switcher in `AppShell.SidebarHeader` and the account menu in a `SidebarMenu.Account` at the end of `AppShell.SidebarFooter`. Both open a [Menu](/docs/components/menu): trigger the account menu with a `SidebarMenu.AccountRow`, and render the switcher with `render={<button type="button" />}`.
742
+
743
+ **Do:** Pin the project switcher to the top and the account to the bottom.
744
+
745
+ ```tsx
746
+ /**
747
+ * Do example for "Put Workspace and Account Controls at the Edges" on the SidebarMenu page.
748
+ * Pin the project switcher to the top and the account to the bottom.
749
+ * @module
750
+ */
751
+ import { Avatar, SidebarMenu, style } from '@tempoxyz/ds'
752
+ import { ChevronDown, Globe, Grid, Key, Store } from '@tempoxyz/ds/icons'
753
+
754
+ /** Renders the static preview, which the guideline card shows inert. */
755
+ export function Demo() {
756
+ return (
757
+ <div {...styles.sidebar()}>
758
+ <SidebarMenu.Root aria-label="Project" size="small">
759
+ <SidebarMenu.Item render={<button type="button" />}>
760
+ <Store />
761
+ Checkout
762
+ <ChevronDown {...styles.chevron()} />
763
+ </SidebarMenu.Item>
764
+ </SidebarMenu.Root>
765
+ <SidebarMenu.Root aria-label="Console" size="small" {...styles.body()}>
766
+ <SidebarMenu.Item active href="#">
767
+ <Grid />
768
+ Home
769
+ </SidebarMenu.Item>
770
+ <SidebarMenu.Item href="#">
771
+ <Key />
772
+ API keys
773
+ </SidebarMenu.Item>
774
+ <SidebarMenu.Item href="#">
775
+ <Globe />
776
+ Webhooks
777
+ </SidebarMenu.Item>
778
+ </SidebarMenu.Root>
779
+ <SidebarMenu.Account>
780
+ <SidebarMenu.AccountRow description="alice@tempo.xyz" label="Alice Chen">
781
+ <Avatar size="32">
782
+ <Avatar.Fallback>AC</Avatar.Fallback>
783
+ </Avatar>
784
+ </SidebarMenu.AccountRow>
785
+ </SidebarMenu.Account>
786
+ </div>
787
+ )
788
+ }
789
+
790
+ namespace styles {
791
+ export const body = style({ flex: '1 !custom' })
792
+
793
+ export const chevron = style({ marginInlineStart: 'auto !custom' })
794
+
795
+ export const sidebar = style({
796
+ display: 'flex',
797
+ flexDirection: 'column',
798
+ height: '20rem !custom',
799
+ rowGap: '12',
800
+ width: '15rem !custom',
801
+ })
802
+ }
803
+ ```
804
+
805
+ **Don't:** List the switcher and the account among the pages.
806
+
807
+ ```tsx
808
+ /**
809
+ * Don't example for "Put Workspace and Account Controls at the Edges" on the SidebarMenu page.
810
+ * Don't list the switcher and the account among the pages.
811
+ * @module
812
+ */
813
+ import { SidebarMenu, style } from '@tempoxyz/ds'
814
+ import { ChevronDown, Globe, Grid, Key, Store, UserCircle } from '@tempoxyz/ds/icons'
815
+
816
+ /** Renders the static preview, which the guideline card shows inert. */
817
+ export function Demo() {
818
+ return (
819
+ <div {...styles.sidebar()}>
820
+ <SidebarMenu.Root aria-label="Console" size="small">
821
+ <SidebarMenu.Item active href="#">
822
+ <Grid />
823
+ Home
824
+ </SidebarMenu.Item>
825
+ <SidebarMenu.Item href="#">
826
+ <Key />
827
+ API keys
828
+ </SidebarMenu.Item>
829
+ <SidebarMenu.Item href="#">
830
+ <Globe />
831
+ Webhooks
832
+ </SidebarMenu.Item>
833
+ </SidebarMenu.Root>
834
+ <SidebarMenu.Root aria-label="Project" size="small">
835
+ <SidebarMenu.Item render={<button type="button" />}>
836
+ <Store />
837
+ Checkout
838
+ <ChevronDown {...styles.chevron()} />
839
+ </SidebarMenu.Item>
840
+ </SidebarMenu.Root>
841
+ <SidebarMenu.Root aria-label="Account" size="small">
842
+ <SidebarMenu.Item render={<button type="button" />}>
843
+ <UserCircle />
844
+ Alice Chen
845
+ </SidebarMenu.Item>
846
+ </SidebarMenu.Root>
847
+ </div>
848
+ )
849
+ }
850
+
851
+ namespace styles {
852
+ export const chevron = style({ marginInlineStart: 'auto !custom' })
853
+
854
+ export const sidebar = style({
855
+ display: 'flex',
856
+ flexDirection: 'column',
857
+ height: '20rem !custom',
858
+ rowGap: '12',
859
+ width: '15rem !custom',
860
+ })
861
+ }
862
+ ```
863
+
864
+ ## Content
865
+
866
+ * **Items:** Match the page's `AppShell.Title`, such as API keys. Use one or two nouns, without verbs such as Manage.
867
+ * **Group labels:** Short nouns, such as Payments, not repeated in the items below.
868
+ * **Account row:** The person's name over their email or wallet address, such as Piril Akay over <piril@tempo.xyz>.
869
+ * **Logo row:** The product or wallet name over its address, such as Tempo Wallet over 0xc721…s9dk.
870
+
871
+ ## Accessibility
872
+
873
+ SidebarMenu renders a navigation landmark of links, with disclosure submenus and no menu roles. It follows the [WAI-ARIA Authoring Practices disclosure navigation example](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/).
874
+
875
+ ### Semantics
876
+
877
+ * **SidebarMenu.Root (nav):** A landmark named "Main" unless you pass `aria-label`.
878
+ * **SidebarMenu.Item (aria-current):** Each item is a link. The active item is marked as the current page.
879
+ * **SidebarMenu.Group (group):** Named by its group label.
880
+ * **SidebarMenu.SubmenuTrigger (aria-expanded):** A button that reports whether its submenu is open. Enter and Space toggle it.
881
+ * **SidebarMenu.SubmenuPanel:** Collapsed links leave the tab order. Under reduced motion, it fades instead of growing.
882
+ * **SidebarMenu.AccountRow (button):** A button named by its two lines. As a menu trigger, it reports whether the menu is open.
883
+ * **SidebarMenu.Account (div):** A plain frame. Its divider is decorative, so it announces no separator before the row.
884
+ * **SidebarMenu.Logo (aria-hidden):** Hidden with the rest of the row's media. The row's label names the product or wallet.
885
+
886
+ ### Keyboard
887
+
888
+ * **Move Through Items (Tab):** Moves focus through items and submenu triggers.
889
+ * **Follow or Toggle (Enter):** Follows a focused link or toggles a submenu.
890
+ * **Toggle a Submenu (Space):** Toggles the focused submenu.
891
+
892
+ Arrow keys do not move focus. Links in a landmark use the Tab order.
893
+
894
+ ### Focus
895
+
896
+ Focus stays on a submenu trigger when it toggles. After it opens, the next Tab moves to its first link.
897
+
898
+ ### Your Responsibilities
899
+
900
+ * **Name Each Landmark (SC 2.4.1):** If a page has several navigation landmarks, give each a distinct `aria-label`.
901
+ * **Mark One Active Item (active):** Mark only the current page. When it is nested, keep its submenu open.
902
+ * **Name Unlabelled Groups (aria-label):** Name a group that has no visible label.
903
+ * **Keep a Consistent Order (SC 3.2.3):** Keep the item order the same on every page.
904
+ * **Name Pages in Text (aria-hidden):** Icons are hidden from assistive technology. The text label must name the page.
905
+
906
+ ## Related
907
+
908
+ [AppShell](/docs/components/app-shell)
909
+
910
+ Use for the frame that holds the sidebar.
911
+
912
+ [Menu](/docs/components/menu)
913
+
914
+ Use for lists of actions.
915
+
916
+ [Tabs](/docs/components/tabs)
917
+
918
+ Use for views within one page.
919
+
920
+ [Navigation](/docs/patterns/navigation)
921
+
922
+ Covers choosing between navigation components.