@tempoxyz/ds 0.3.10 → 0.3.13

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 (210) hide show
  1. package/README.md +7 -6
  2. package/dist/docs/AGENTS.md +12 -0
  3. package/dist/docs/index.md +145 -0
  4. package/dist/docs/marketing/introduction/getting-started.md +241 -0
  5. package/dist/docs/product/components/accordion.md +489 -0
  6. package/dist/docs/product/components/address-card.md +147 -0
  7. package/dist/docs/product/components/agent-logo.md +194 -0
  8. package/dist/docs/product/components/alert-dialog.md +631 -0
  9. package/dist/docs/product/components/alert.md +448 -0
  10. package/dist/docs/product/components/amount-input.md +305 -0
  11. package/dist/docs/product/components/amount.md +794 -0
  12. package/dist/docs/product/components/app-shell.md +559 -0
  13. package/dist/docs/product/components/asset-chip.md +294 -0
  14. package/dist/docs/product/components/autocomplete.md +620 -0
  15. package/dist/docs/product/components/avatar.md +321 -0
  16. package/dist/docs/product/components/badge.md +339 -0
  17. package/dist/docs/product/components/badged-logo.md +116 -0
  18. package/dist/docs/product/components/banner.md +379 -0
  19. package/dist/docs/product/components/bar-chart.md +208 -0
  20. package/dist/docs/product/components/breadcrumbs.md +380 -0
  21. package/dist/docs/product/components/button-group.md +286 -0
  22. package/dist/docs/product/components/button.md +650 -0
  23. package/dist/docs/product/components/calendar.md +375 -0
  24. package/dist/docs/product/components/callout.md +96 -0
  25. package/dist/docs/product/components/checkbox-group.md +521 -0
  26. package/dist/docs/product/components/checkbox.md +467 -0
  27. package/dist/docs/product/components/choice-group.md +516 -0
  28. package/dist/docs/product/components/choicebox.md +641 -0
  29. package/dist/docs/product/components/code.md +100 -0
  30. package/dist/docs/product/components/combobox.md +939 -0
  31. package/dist/docs/product/components/container.md +257 -0
  32. package/dist/docs/product/components/copy-button.md +642 -0
  33. package/dist/docs/product/components/date-picker.md +514 -0
  34. package/dist/docs/product/components/dialog.md +1332 -0
  35. package/dist/docs/product/components/drawer.md +966 -0
  36. package/dist/docs/product/components/empty.md +340 -0
  37. package/dist/docs/product/components/field.md +570 -0
  38. package/dist/docs/product/components/fieldset.md +398 -0
  39. package/dist/docs/product/components/frame.md +434 -0
  40. package/dist/docs/product/components/gauge.md +299 -0
  41. package/dist/docs/product/components/get-started-visual.md +117 -0
  42. package/dist/docs/product/components/grid.md +288 -0
  43. package/dist/docs/product/components/hero-visual.md +155 -0
  44. package/dist/docs/product/components/hex.md +487 -0
  45. package/dist/docs/product/components/icon-tile.md +292 -0
  46. package/dist/docs/product/components/identicon.md +371 -0
  47. package/dist/docs/product/components/inline.md +233 -0
  48. package/dist/docs/product/components/inset.md +225 -0
  49. package/dist/docs/product/components/kbd.md +423 -0
  50. package/dist/docs/product/components/list-group.md +981 -0
  51. package/dist/docs/product/components/loading-dots.md +237 -0
  52. package/dist/docs/product/components/menu.md +934 -0
  53. package/dist/docs/product/components/native-select.md +204 -0
  54. package/dist/docs/product/components/network-logo.md +248 -0
  55. package/dist/docs/product/components/number-field.md +543 -0
  56. package/dist/docs/product/components/otp-field.md +616 -0
  57. package/dist/docs/product/components/pagination.md +516 -0
  58. package/dist/docs/product/components/popover.md +544 -0
  59. package/dist/docs/product/components/progress.md +388 -0
  60. package/dist/docs/product/components/qr.md +314 -0
  61. package/dist/docs/product/components/radio-group.md +528 -0
  62. package/dist/docs/product/components/secret-value.md +406 -0
  63. package/dist/docs/product/components/select.md +751 -0
  64. package/dist/docs/product/components/separator.md +379 -0
  65. package/dist/docs/product/components/sidebar-menu.md +922 -0
  66. package/dist/docs/product/components/skeleton.md +517 -0
  67. package/dist/docs/product/components/slider.md +539 -0
  68. package/dist/docs/product/components/snippet.md +424 -0
  69. package/dist/docs/product/components/spinner.md +216 -0
  70. package/dist/docs/product/components/split-bar.md +139 -0
  71. package/dist/docs/product/components/split-button.md +464 -0
  72. package/dist/docs/product/components/stack.md +299 -0
  73. package/dist/docs/product/components/status-chip.md +226 -0
  74. package/dist/docs/product/components/status-indicator.md +119 -0
  75. package/dist/docs/product/components/status-row.md +174 -0
  76. package/dist/docs/product/components/stepper.md +451 -0
  77. package/dist/docs/product/components/success-hero.md +157 -0
  78. package/dist/docs/product/components/surface.md +639 -0
  79. package/dist/docs/product/components/switch.md +472 -0
  80. package/dist/docs/product/components/table.md +967 -0
  81. package/dist/docs/product/components/tabs.md +627 -0
  82. package/dist/docs/product/components/tempo-logo.md +185 -0
  83. package/dist/docs/product/components/text-input.md +624 -0
  84. package/dist/docs/product/components/textarea.md +352 -0
  85. package/dist/docs/product/components/toast.md +535 -0
  86. package/dist/docs/product/components/token-logo.md +371 -0
  87. package/dist/docs/product/components/token.md +247 -0
  88. package/dist/docs/product/components/tooltip.md +490 -0
  89. package/dist/docs/product/components/verification-code.md +116 -0
  90. package/dist/docs/product/components/visual.md +249 -0
  91. package/dist/docs/product/components.md +203 -0
  92. package/dist/docs/product/foundations/accessibility.md +377 -0
  93. package/dist/docs/product/foundations/agents.md +150 -0
  94. package/dist/docs/product/foundations/color.md +522 -0
  95. package/dist/docs/product/foundations/content/tone.md +194 -0
  96. package/dist/docs/product/foundations/content/voice.md +194 -0
  97. package/dist/docs/product/foundations/content/writing-style.md +406 -0
  98. package/dist/docs/product/foundations/iconography.md +282 -0
  99. package/dist/docs/product/foundations/intent.md +360 -0
  100. package/dist/docs/product/foundations/layout.md +508 -0
  101. package/dist/docs/product/foundations/linter.md +496 -0
  102. package/dist/docs/product/foundations/motion.md +151 -0
  103. package/dist/docs/product/foundations/radius-and-padding.md +86 -0
  104. package/dist/docs/product/foundations/typography.md +266 -0
  105. package/dist/docs/product/introduction/faq.md +133 -0
  106. package/dist/docs/product/introduction/getting-started.md +377 -0
  107. package/dist/docs/product/patterns/blockchain-values.md +709 -0
  108. package/dist/docs/product/patterns/dashboards.md +638 -0
  109. package/dist/docs/product/patterns/destructive-actions.md +626 -0
  110. package/dist/docs/product/patterns/empty-states.md +612 -0
  111. package/dist/docs/product/patterns/errors-and-validation.md +808 -0
  112. package/dist/docs/product/patterns/feedback-and-status.md +518 -0
  113. package/dist/docs/product/patterns/forms.md +679 -0
  114. package/dist/docs/product/patterns/loading.md +630 -0
  115. package/dist/docs/product/patterns/navigation.md +397 -0
  116. package/dist/docs/product/patterns/payments.md +765 -0
  117. package/dist/docs/product/patterns/responsive-layout.md +997 -0
  118. package/dist/docs/product/patterns/tables-and-lists.md +964 -0
  119. package/dist/docs/product/products/tempo-console.md +671 -0
  120. package/dist/docs/product/products/tempo-performance.md +437 -0
  121. package/dist/docs/product/products/tempo-wallet.md +438 -0
  122. package/dist/marketing/index.d.ts +4 -0
  123. package/dist/marketing/index.d.ts.map +1 -0
  124. package/dist/marketing/index.js +4 -0
  125. package/dist/marketing/index.js.map +1 -0
  126. package/dist/marketing/index.js.zyzz.json +1 -0
  127. package/dist/marketing/vars.d.ts +907 -0
  128. package/dist/marketing/vars.d.ts.map +1 -0
  129. package/dist/marketing/vars.js +15 -0
  130. package/dist/marketing/vars.js.map +1 -0
  131. package/dist/marketing/vars.js.zyzz.json +1 -0
  132. package/dist/marketing/zyzz.config.d.ts +4512 -0
  133. package/dist/marketing/zyzz.config.d.ts.map +1 -0
  134. package/dist/marketing/zyzz.config.js +63 -0
  135. package/dist/marketing/zyzz.config.js.map +1 -0
  136. package/dist/marketing/zyzz.config.js.zyzz.json +1 -0
  137. package/dist/oxlint/index.d.ts +12 -1
  138. package/dist/oxlint/index.d.ts.map +1 -1
  139. package/dist/oxlint/index.js +39 -1
  140. package/dist/oxlint/index.js.map +1 -1
  141. package/dist/oxlint/internal/Docs.d.ts +9 -0
  142. package/dist/oxlint/internal/Docs.d.ts.map +1 -0
  143. package/dist/oxlint/internal/Docs.js +15 -0
  144. package/dist/oxlint/internal/Docs.js.map +1 -0
  145. package/dist/oxlint/internal/Imports.d.ts +1 -1
  146. package/dist/oxlint/internal/Imports.d.ts.map +1 -1
  147. package/dist/oxlint/internal/Imports.js +2 -0
  148. package/dist/oxlint/internal/Imports.js.map +1 -1
  149. package/dist/oxlint/internal/Styles.d.ts +40 -0
  150. package/dist/oxlint/internal/Styles.d.ts.map +1 -0
  151. package/dist/oxlint/internal/Styles.js +194 -0
  152. package/dist/oxlint/internal/Styles.js.map +1 -0
  153. package/dist/oxlint/internal/Tokens.d.ts +16 -0
  154. package/dist/oxlint/internal/Tokens.d.ts.map +1 -0
  155. package/dist/oxlint/internal/Tokens.js +39 -0
  156. package/dist/oxlint/internal/Tokens.js.map +1 -0
  157. package/dist/oxlint/rules/NoCrossSurface.d.ts +2 -2
  158. package/dist/oxlint/rules/NoCrossSurface.js +3 -3
  159. package/dist/oxlint/rules/NoCrossSurface.js.map +1 -1
  160. package/dist/oxlint/rules/NoCustomValues.d.ts +11 -0
  161. package/dist/oxlint/rules/NoCustomValues.d.ts.map +1 -0
  162. package/dist/oxlint/rules/NoCustomValues.js +150 -0
  163. package/dist/oxlint/rules/NoCustomValues.js.map +1 -0
  164. package/dist/oxlint/rules/NoSingleKeyShortcuts.d.ts +11 -0
  165. package/dist/oxlint/rules/NoSingleKeyShortcuts.d.ts.map +1 -0
  166. package/dist/oxlint/rules/NoSingleKeyShortcuts.js +158 -0
  167. package/dist/oxlint/rules/NoSingleKeyShortcuts.js.map +1 -0
  168. package/dist/oxlint/rules/PreferComponent.d.ts +3 -2
  169. package/dist/oxlint/rules/PreferComponent.d.ts.map +1 -1
  170. package/dist/oxlint/rules/PreferComponent.js +11 -4
  171. package/dist/oxlint/rules/PreferComponent.js.map +1 -1
  172. package/dist/oxlint/rules/PreferLayout.d.ts +9 -0
  173. package/dist/oxlint/rules/PreferLayout.d.ts.map +1 -0
  174. package/dist/oxlint/rules/PreferLayout.js +90 -0
  175. package/dist/oxlint/rules/PreferLayout.js.map +1 -0
  176. package/dist/oxlint/rules/PreferProgress.d.ts +9 -0
  177. package/dist/oxlint/rules/PreferProgress.d.ts.map +1 -0
  178. package/dist/oxlint/rules/PreferProgress.js +43 -0
  179. package/dist/oxlint/rules/PreferProgress.js.map +1 -0
  180. package/dist/oxlint/rules/PreferSurface.d.ts +9 -0
  181. package/dist/oxlint/rules/PreferSurface.d.ts.map +1 -0
  182. package/dist/oxlint/rules/PreferSurface.js +55 -0
  183. package/dist/oxlint/rules/PreferSurface.js.map +1 -0
  184. package/dist/oxlint/rules/RequireAccessibleName.d.ts.map +1 -1
  185. package/dist/oxlint/rules/RequireAccessibleName.js +3 -1
  186. package/dist/oxlint/rules/RequireAccessibleName.js.map +1 -1
  187. package/dist/oxlint/rules/RequireScrollRegion.d.ts +11 -0
  188. package/dist/oxlint/rules/RequireScrollRegion.d.ts.map +1 -0
  189. package/dist/oxlint/rules/RequireScrollRegion.js +71 -0
  190. package/dist/oxlint/rules/RequireScrollRegion.js.map +1 -0
  191. package/dist/web/icons/index.js.zyzz.json +1 -0
  192. package/package.json +6 -1
  193. package/src/marketing/README.md +21 -0
  194. package/src/marketing/index.ts +3 -0
  195. package/src/marketing/vars.ts +751 -0
  196. package/src/marketing/zyzz.config.ts +376 -0
  197. package/src/oxlint/index.ts +40 -1
  198. package/src/oxlint/internal/Docs.ts +18 -0
  199. package/src/oxlint/internal/Imports.ts +2 -1
  200. package/src/oxlint/internal/Styles.ts +240 -0
  201. package/src/oxlint/internal/Tokens.ts +43 -0
  202. package/src/oxlint/rules/NoCrossSurface.ts +3 -3
  203. package/src/oxlint/rules/NoCustomValues.ts +170 -0
  204. package/src/oxlint/rules/NoSingleKeyShortcuts.ts +190 -0
  205. package/src/oxlint/rules/PreferComponent.ts +11 -4
  206. package/src/oxlint/rules/PreferLayout.ts +96 -0
  207. package/src/oxlint/rules/PreferProgress.ts +49 -0
  208. package/src/oxlint/rules/PreferSurface.ts +70 -0
  209. package/src/oxlint/rules/RequireAccessibleName.ts +3 -1
  210. package/src/oxlint/rules/RequireScrollRegion.ts +92 -0
@@ -0,0 +1,934 @@
1
+ # Menu
2
+
3
+ Opens a list of actions from a button.
4
+
5
+ [View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/Menu.tsx)
6
+
7
+ Figma: [Navigation/Account Menu](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-3061) (`Menu.Popup`)
8
+
9
+ ## Overview
10
+
11
+ Menu renders a button that opens a list of actions and commands. Arrow keys move through the items, and Escape closes the list.
12
+
13
+ ## Examples
14
+
15
+ ### Basic
16
+
17
+ ```tsx
18
+ import { Button, Menu, style } from '@tempoxyz/ds'
19
+
20
+ export function Demo() {
21
+ // The gallery canvas stretches its children, so the wrapper keeps the trigger at its own width.
22
+ return (
23
+ <div {...styles.row()}>
24
+ <Menu.Root>
25
+ <Menu.Trigger render={<Button variant="secondary" />}>Options</Menu.Trigger>
26
+ <Menu.Popup align="start">
27
+ <Menu.Item>
28
+ Edit
29
+ <Menu.Shortcut>⌘E</Menu.Shortcut>
30
+ </Menu.Item>
31
+ <Menu.Item>Duplicate</Menu.Item>
32
+ <Menu.Separator />
33
+ <Menu.Item>Archive</Menu.Item>
34
+ </Menu.Popup>
35
+ </Menu.Root>
36
+ </div>
37
+ )
38
+ }
39
+
40
+ namespace styles {
41
+ export const row = style({ alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: '12' })
42
+ }
43
+ ```
44
+
45
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Menu/Basic.tsx)
46
+
47
+ ### Size
48
+
49
+ Set `size` on `Menu.Popup` to `small`, `medium` (default), or `large`.
50
+
51
+ ```tsx
52
+ import { Button, Menu, style } from '@tempoxyz/ds'
53
+ import { useState } from 'react'
54
+
55
+ export function Demo() {
56
+ const [density, setDensity] = useState('comfortable')
57
+
58
+ // The gallery canvas stretches its children, so the wrapper keeps each trigger at its own width.
59
+ return (
60
+ <div {...styles.row()}>
61
+ {(['small', 'medium', 'large'] as const).map((size) => (
62
+ <Menu.Root key={size}>
63
+ <Menu.Trigger render={<Button size={size} variant="secondary" />}>
64
+ Options ({size})
65
+ </Menu.Trigger>
66
+ <Menu.Popup align="start" size={size}>
67
+ <Menu.Item>
68
+ Edit
69
+ <Menu.Shortcut>⌘E</Menu.Shortcut>
70
+ </Menu.Item>
71
+ <Menu.Item disabled>Duplicate</Menu.Item>
72
+ <Menu.LinkItem href="https://tempo.xyz">Documentation</Menu.LinkItem>
73
+ <Menu.Separator />
74
+ <Menu.CheckboxItem defaultChecked>Show archived</Menu.CheckboxItem>
75
+ <Menu.Group>
76
+ <Menu.GroupLabel>Density</Menu.GroupLabel>
77
+ <Menu.RadioGroup onValueChange={(value) => setDensity(String(value))} value={density}>
78
+ <Menu.RadioItem value="comfortable">Comfortable</Menu.RadioItem>
79
+ <Menu.RadioItem value="compact">Compact</Menu.RadioItem>
80
+ </Menu.RadioGroup>
81
+ </Menu.Group>
82
+ <Menu.Separator />
83
+ <Menu.SubmenuRoot>
84
+ <Menu.SubmenuTrigger>Share</Menu.SubmenuTrigger>
85
+ <Menu.Popup size={size}>
86
+ <Menu.Item>Copy link</Menu.Item>
87
+ <Menu.Item>Email</Menu.Item>
88
+ </Menu.Popup>
89
+ </Menu.SubmenuRoot>
90
+ <Menu.Item intent="negative">Delete</Menu.Item>
91
+ </Menu.Popup>
92
+ </Menu.Root>
93
+ ))}
94
+ </div>
95
+ )
96
+ }
97
+
98
+ namespace styles {
99
+ export const row = style({ alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: '12' })
100
+ }
101
+ ```
102
+
103
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Menu/Size.tsx)
104
+
105
+ ### Intent
106
+
107
+ Set `intent="negative"` on a `Menu.Item` for destructive actions. The default is `default`.
108
+
109
+ ```tsx
110
+ import { Button, Menu, style } from '@tempoxyz/ds'
111
+
112
+ export function Demo() {
113
+ // The gallery canvas stretches its children, so the wrapper keeps the trigger at its own width.
114
+ return (
115
+ <div {...styles.row()}>
116
+ <Menu.Root>
117
+ <Menu.Trigger render={<Button variant="secondary" />}>Options</Menu.Trigger>
118
+ <Menu.Popup align="start">
119
+ <Menu.Item>Edit</Menu.Item>
120
+ <Menu.Separator />
121
+ <Menu.Item intent="negative">Delete</Menu.Item>
122
+ </Menu.Popup>
123
+ </Menu.Root>
124
+ </div>
125
+ )
126
+ }
127
+
128
+ namespace styles {
129
+ export const row = style({ alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: '12' })
130
+ }
131
+ ```
132
+
133
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Menu/Intent.tsx)
134
+
135
+ ### Disabled
136
+
137
+ Set `disabled` on a `Menu.Item` to make it unavailable.
138
+
139
+ ```tsx
140
+ import { Button, Menu, style } from '@tempoxyz/ds'
141
+
142
+ export function Demo() {
143
+ // The gallery canvas stretches its children, so the wrapper keeps the trigger at its own width.
144
+ return (
145
+ <div {...styles.row()}>
146
+ <Menu.Root>
147
+ <Menu.Trigger render={<Button variant="secondary" />}>Options</Menu.Trigger>
148
+ <Menu.Popup align="start">
149
+ <Menu.Item>Edit</Menu.Item>
150
+ <Menu.Item disabled>Duplicate</Menu.Item>
151
+ </Menu.Popup>
152
+ </Menu.Root>
153
+ </div>
154
+ )
155
+ }
156
+
157
+ namespace styles {
158
+ export const row = style({ alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: '12' })
159
+ }
160
+ ```
161
+
162
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Menu/Disabled.tsx)
163
+
164
+ ## Anatomy
165
+
166
+ Import the component and assemble its parts.
167
+
168
+ ```tsx
169
+ import { Menu } from '@tempoxyz/ds'
170
+
171
+ <Menu.Root>
172
+ <Menu.Trigger />
173
+ <Menu.Popup>
174
+ <Menu.Item>
175
+ <Menu.Shortcut />
176
+ </Menu.Item>
177
+ <Menu.LinkItem />
178
+ <Menu.CheckboxItem />
179
+ <Menu.Separator />
180
+ <Menu.Group>
181
+ <Menu.GroupLabel />
182
+ <Menu.RadioGroup>
183
+ <Menu.RadioItem />
184
+ </Menu.RadioGroup>
185
+ </Menu.Group>
186
+ <Menu.SubmenuRoot>
187
+ <Menu.SubmenuTrigger />
188
+ <Menu.Popup />
189
+ </Menu.SubmenuRoot>
190
+ </Menu.Popup>
191
+ </Menu.Root>
192
+ ```
193
+
194
+ ## Guidelines
195
+
196
+ ### When to Use
197
+
198
+ * Offer actions on one object, such as Send to address, Copy address, Rename, and Remove address.
199
+ * If a toolbar, card header, or table row has more than three secondary actions, put them behind an icon button.
200
+ * Change view options in place with `Menu.CheckboxItem` (Show archived) or `Menu.RadioItem` (density). These items keep the menu open on click.
201
+
202
+ ### When Not to Use
203
+
204
+ * Use [Select](select.md) or [Combobox](combobox.md) to choose a form value, such as a token or network.
205
+ * Use [Tabs](tabs.md) or [ChoiceGroup](choice-group.md) to switch views with every option visible.
206
+ * Use a [Button](button.md) for one or two actions.
207
+ * Use [SplitButton](split-button.md) when one action is the clear default.
208
+ * Use [SidebarMenu](sidebar-menu.md) or links for navigation. `Menu.LinkItem` suits an occasional link among actions, such as View on explorer.
209
+ * Use [Popover](popover.md) for text or form controls. A menu holds only items.
210
+
211
+ ### Order Items by Frequency
212
+
213
+ Put the most frequent action at the top. Keep the same items in the same order wherever the menu appears, such as on every table row.
214
+
215
+ **Do:** Put the most frequent action at the top.
216
+
217
+ ```tsx
218
+ /** Do example for "Order Items by Frequency" on the Menu page: put the most frequent action at the top. @module */
219
+ import { style, variants } from '@tempoxyz/ds'
220
+
221
+ /** Renders the static preview, which the guideline card shows inert. */
222
+ export function Demo() {
223
+ return (
224
+ <div {...styles.popup()}>
225
+ <div {...styles.item()}>Send to address</div>
226
+ <div {...styles.item()}>Copy address</div>
227
+ <div {...styles.item()}>Rename</div>
228
+ </div>
229
+ )
230
+ }
231
+
232
+ namespace styles {
233
+ export const item = variants({
234
+ base: {
235
+ alignItems: 'center',
236
+ borderRadius: 'xs',
237
+ color: 'content.primary',
238
+ columnGap: '8',
239
+ display: 'flex',
240
+ minHeight: '40',
241
+ paddingInline: '12',
242
+ typography: 'body.b2Strong',
243
+ },
244
+ defaultVariants: { state: 'default' },
245
+ variants: {
246
+ state: {
247
+ default: {},
248
+ disabled: { opacity: 'disabled' },
249
+ negative: { color: 'content.negative' },
250
+ },
251
+ },
252
+ })
253
+
254
+ export const popup = style({
255
+ backgroundColor: 'background.elevated',
256
+ borderRadius: 'sm',
257
+ boxShadow: 'regular',
258
+ boxSizing: 'border-box',
259
+ minWidth: '14rem !custom',
260
+ padding: '12',
261
+ })
262
+ }
263
+ ```
264
+
265
+ **Don't:** Lead with a rare action, such as Rename.
266
+
267
+ ```tsx
268
+ /**
269
+ * Don't example for "Order Items by Frequency" on the Menu page.
270
+ * Don't lead with a rare action, such as Rename.
271
+ * @module
272
+ */
273
+ import { style, variants } from '@tempoxyz/ds'
274
+
275
+ /** Renders the static preview, which the guideline card shows inert. */
276
+ export function Demo() {
277
+ return (
278
+ <div {...styles.popup()}>
279
+ <div {...styles.item()}>Rename</div>
280
+ <div {...styles.item()}>Copy address</div>
281
+ <div {...styles.item()}>Send to address</div>
282
+ </div>
283
+ )
284
+ }
285
+
286
+ namespace styles {
287
+ export const item = variants({
288
+ base: {
289
+ alignItems: 'center',
290
+ borderRadius: 'xs',
291
+ color: 'content.primary',
292
+ columnGap: '8',
293
+ display: 'flex',
294
+ minHeight: '40',
295
+ paddingInline: '12',
296
+ typography: 'body.b2Strong',
297
+ },
298
+ defaultVariants: { state: 'default' },
299
+ variants: {
300
+ state: {
301
+ default: {},
302
+ disabled: { opacity: 'disabled' },
303
+ negative: { color: 'content.negative' },
304
+ },
305
+ },
306
+ })
307
+
308
+ export const popup = style({
309
+ backgroundColor: 'background.elevated',
310
+ borderRadius: 'sm',
311
+ boxShadow: 'regular',
312
+ boxSizing: 'border-box',
313
+ minWidth: '14rem !custom',
314
+ padding: '12',
315
+ })
316
+ }
317
+ ```
318
+
319
+ ### Group Related Items
320
+
321
+ Wrap related items in `Menu.Group` and divide groups with `Menu.Separator`. If the items alone do not explain the group, add a `Menu.GroupLabel`, such as Density.
322
+
323
+ Keep a menu to about seven items. A `Menu.SubmenuRoot` hides its items, so keep frequent actions at the top level and nest only one level deep.
324
+
325
+ **Do:** Divide groups with a separator, and label a group whose items need it.
326
+
327
+ ```tsx
328
+ /**
329
+ * Do example for "Group Related Items" on the Menu page.
330
+ * Divide groups with a separator, and label a group whose items need it.
331
+ * @module
332
+ */
333
+ import { Check } from '@tempoxyz/ds/icons'
334
+ import { style, variants } from '@tempoxyz/ds'
335
+
336
+ /** Renders the static preview, which the guideline card shows inert. */
337
+ export function Demo() {
338
+ return (
339
+ <div {...styles.popup()}>
340
+ <div {...styles.item()}>Send to address</div>
341
+ <div {...styles.item()}>Copy address</div>
342
+ <div {...styles.separator()} />
343
+ <div {...styles.groupLabel()}>Density</div>
344
+ <div {...styles.item()}>
345
+ Comfortable
346
+ <Check {...styles.check()} />
347
+ </div>
348
+ <div {...styles.item()}>Compact</div>
349
+ </div>
350
+ )
351
+ }
352
+
353
+ namespace styles {
354
+ export const check = style({ fontSize: '16', marginInlineStart: 'auto !custom' })
355
+
356
+ export const groupLabel = style({
357
+ color: 'content.secondary',
358
+ paddingBlock: '8',
359
+ paddingInline: '12',
360
+ typography: 'body.b3',
361
+ })
362
+
363
+ export const item = variants({
364
+ base: {
365
+ alignItems: 'center',
366
+ borderRadius: 'xs',
367
+ color: 'content.primary',
368
+ columnGap: '8',
369
+ display: 'flex',
370
+ minHeight: '40',
371
+ paddingInline: '12',
372
+ typography: 'body.b2Strong',
373
+ },
374
+ defaultVariants: { state: 'default' },
375
+ variants: {
376
+ state: {
377
+ default: {},
378
+ disabled: { opacity: 'disabled' },
379
+ negative: { color: 'content.negative' },
380
+ },
381
+ },
382
+ })
383
+
384
+ export const popup = style({
385
+ backgroundColor: 'background.elevated',
386
+ borderRadius: 'sm',
387
+ boxShadow: 'regular',
388
+ boxSizing: 'border-box',
389
+ minWidth: '14rem !custom',
390
+ padding: '12',
391
+ })
392
+
393
+ export const separator = style({
394
+ backgroundColor: 'container.strong',
395
+ height: '1px !custom',
396
+ marginBlock: '8',
397
+ marginInline: '12',
398
+ })
399
+ }
400
+ ```
401
+
402
+ **Don't:** Mix actions and view options in one flat list.
403
+
404
+ ```tsx
405
+ /**
406
+ * Don't example for "Group Related Items" on the Menu page.
407
+ * Don't mix actions and view options in one flat list.
408
+ * @module
409
+ */
410
+ import { Check } from '@tempoxyz/ds/icons'
411
+ import { style, variants } from '@tempoxyz/ds'
412
+
413
+ /** Renders the static preview, which the guideline card shows inert. */
414
+ export function Demo() {
415
+ return (
416
+ <div {...styles.popup()}>
417
+ <div {...styles.item()}>Send to address</div>
418
+ <div {...styles.item()}>
419
+ Comfortable
420
+ <Check {...styles.check()} />
421
+ </div>
422
+ <div {...styles.item()}>Copy address</div>
423
+ <div {...styles.item()}>Compact</div>
424
+ </div>
425
+ )
426
+ }
427
+
428
+ namespace styles {
429
+ export const check = style({ fontSize: '16', marginInlineStart: 'auto !custom' })
430
+
431
+ export const item = variants({
432
+ base: {
433
+ alignItems: 'center',
434
+ borderRadius: 'xs',
435
+ color: 'content.primary',
436
+ columnGap: '8',
437
+ display: 'flex',
438
+ minHeight: '40',
439
+ paddingInline: '12',
440
+ typography: 'body.b2Strong',
441
+ },
442
+ defaultVariants: { state: 'default' },
443
+ variants: {
444
+ state: {
445
+ default: {},
446
+ disabled: { opacity: 'disabled' },
447
+ negative: { color: 'content.negative' },
448
+ },
449
+ },
450
+ })
451
+
452
+ export const popup = style({
453
+ backgroundColor: 'background.elevated',
454
+ borderRadius: 'sm',
455
+ boxShadow: 'regular',
456
+ boxSizing: 'border-box',
457
+ minWidth: '14rem !custom',
458
+ padding: '12',
459
+ })
460
+ }
461
+ ```
462
+
463
+ ### Put Destructive Items Last
464
+
465
+ A keyboard open focuses the first item, so a destructive item at the top is one Enter press away. Place it last, after a `Menu.Separator`, with `intent="negative"`.
466
+
467
+ **Do:** Put destructive items at the end, after a separator.
468
+
469
+ ```tsx
470
+ /**
471
+ * Do example for "Put Destructive Items Last" on the Menu page.
472
+ * Put destructive items at the end, after a separator.
473
+ * @module
474
+ */
475
+ import { style, variants } from '@tempoxyz/ds'
476
+
477
+ /** Renders the static preview, which the guideline card shows inert. */
478
+ export function Demo() {
479
+ return (
480
+ <div {...styles.popup()}>
481
+ <div {...styles.item()}>Send to address</div>
482
+ <div {...styles.item()}>Copy address</div>
483
+ <div {...styles.item()}>Rename</div>
484
+ <div {...styles.separator()} />
485
+ <div {...styles.item({ intent: 'negative' })}>Remove address</div>
486
+ </div>
487
+ )
488
+ }
489
+
490
+ namespace styles {
491
+ export const item = variants({
492
+ base: {
493
+ alignItems: 'center',
494
+ borderRadius: 'xs',
495
+ color: 'content.primary',
496
+ display: 'flex',
497
+ minHeight: '40',
498
+ paddingInline: '12',
499
+ typography: 'body.b2Strong',
500
+ },
501
+ defaultVariants: { intent: 'default' },
502
+ variants: {
503
+ intent: {
504
+ default: {},
505
+ negative: { color: 'content.negative' },
506
+ },
507
+ },
508
+ })
509
+
510
+ export const popup = style({
511
+ backgroundColor: 'background.elevated',
512
+ borderRadius: 'sm',
513
+ boxShadow: 'regular',
514
+ boxSizing: 'border-box',
515
+ minWidth: '14rem !custom',
516
+ padding: '12',
517
+ })
518
+
519
+ export const separator = style({
520
+ backgroundColor: 'container.strong',
521
+ height: '1px !custom',
522
+ marginBlock: '8',
523
+ marginInline: '12',
524
+ })
525
+ }
526
+ ```
527
+
528
+ **Don't:** Lead with a destructive item, or mix it in with routine actions.
529
+
530
+ ```tsx
531
+ /**
532
+ * Don't example for "Put Destructive Items Last" on the Menu page.
533
+ * Don't lead with a destructive item, or mix it in with routine actions.
534
+ * @module
535
+ */
536
+ import { style, variants } from '@tempoxyz/ds'
537
+
538
+ /** Renders the static preview, which the guideline card shows inert. */
539
+ export function Demo() {
540
+ return (
541
+ <div {...styles.popup()}>
542
+ <div {...styles.item({ intent: 'negative' })}>Remove address</div>
543
+ <div {...styles.item()}>Send to address</div>
544
+ <div {...styles.item()}>Copy address</div>
545
+ <div {...styles.item()}>Rename</div>
546
+ </div>
547
+ )
548
+ }
549
+
550
+ namespace styles {
551
+ export const item = variants({
552
+ base: {
553
+ alignItems: 'center',
554
+ borderRadius: 'xs',
555
+ color: 'content.primary',
556
+ display: 'flex',
557
+ minHeight: '40',
558
+ paddingInline: '12',
559
+ typography: 'body.b2Strong',
560
+ },
561
+ defaultVariants: { intent: 'default' },
562
+ variants: {
563
+ intent: {
564
+ default: {},
565
+ negative: { color: 'content.negative' },
566
+ },
567
+ },
568
+ })
569
+
570
+ export const popup = style({
571
+ backgroundColor: 'background.elevated',
572
+ borderRadius: 'sm',
573
+ boxShadow: 'regular',
574
+ boxSizing: 'border-box',
575
+ minWidth: '14rem !custom',
576
+ padding: '12',
577
+ })
578
+ }
579
+ ```
580
+
581
+ If the action cannot be undone, open an [AlertDialog](alert-dialog.md) from the item and repeat the item's verb on its confirm button:
582
+
583
+ * **Placement:** Render the dialog outside `Menu.Popup`, which unmounts when the menu closes.
584
+ * **State:** Open it from the item's `onClick` with `open` and `onOpenChange`.
585
+
586
+ See [Destructive Actions](../patterns/destructive-actions.md).
587
+
588
+ ### Disable Unavailable Items
589
+
590
+ Set `disabled` on an item that is unavailable now, such as Send to address with no funds. The menu keeps its shape. Leave out items that are never available, such as admin actions without the permission.
591
+
592
+ **Do:** Disable an item that is unavailable for now.
593
+
594
+ ```tsx
595
+ /**
596
+ * Do example for "Disable Unavailable Items" on the Menu page.
597
+ * Disable an item that is unavailable for now.
598
+ * @module
599
+ */
600
+ import { style, variants } from '@tempoxyz/ds'
601
+
602
+ /** Renders the static preview, which the guideline card shows inert. */
603
+ export function Demo() {
604
+ return (
605
+ <div {...styles.popup()}>
606
+ <div {...styles.item({ state: 'disabled' })}>Send to address</div>
607
+ <div {...styles.item()}>Copy address</div>
608
+ <div {...styles.item()}>Rename</div>
609
+ <div {...styles.separator()} />
610
+ <div {...styles.item({ state: 'negative' })}>Remove address</div>
611
+ </div>
612
+ )
613
+ }
614
+
615
+ namespace styles {
616
+ export const item = variants({
617
+ base: {
618
+ alignItems: 'center',
619
+ borderRadius: 'xs',
620
+ color: 'content.primary',
621
+ columnGap: '8',
622
+ display: 'flex',
623
+ minHeight: '40',
624
+ paddingInline: '12',
625
+ typography: 'body.b2Strong',
626
+ },
627
+ defaultVariants: { state: 'default' },
628
+ variants: {
629
+ state: {
630
+ default: {},
631
+ disabled: { opacity: 'disabled' },
632
+ negative: { color: 'content.negative' },
633
+ },
634
+ },
635
+ })
636
+
637
+ export const popup = style({
638
+ backgroundColor: 'background.elevated',
639
+ borderRadius: 'sm',
640
+ boxShadow: 'regular',
641
+ boxSizing: 'border-box',
642
+ minWidth: '14rem !custom',
643
+ padding: '12',
644
+ })
645
+
646
+ export const separator = style({
647
+ backgroundColor: 'container.strong',
648
+ height: '1px !custom',
649
+ marginBlock: '8',
650
+ marginInline: '12',
651
+ })
652
+ }
653
+ ```
654
+
655
+ **Don't:** Remove an item that returns later. The menu changes shape.
656
+
657
+ ```tsx
658
+ /**
659
+ * Don't example for "Disable Unavailable Items" on the Menu page.
660
+ * Don't remove an item that returns later. The menu changes shape.
661
+ * @module
662
+ */
663
+ import { style, variants } from '@tempoxyz/ds'
664
+
665
+ /** Renders the static preview, which the guideline card shows inert. */
666
+ export function Demo() {
667
+ return (
668
+ <div {...styles.popup()}>
669
+ <div {...styles.item()}>Copy address</div>
670
+ <div {...styles.item()}>Rename</div>
671
+ <div {...styles.separator()} />
672
+ <div {...styles.item({ state: 'negative' })}>Remove address</div>
673
+ </div>
674
+ )
675
+ }
676
+
677
+ namespace styles {
678
+ export const item = variants({
679
+ base: {
680
+ alignItems: 'center',
681
+ borderRadius: 'xs',
682
+ color: 'content.primary',
683
+ columnGap: '8',
684
+ display: 'flex',
685
+ minHeight: '40',
686
+ paddingInline: '12',
687
+ typography: 'body.b2Strong',
688
+ },
689
+ defaultVariants: { state: 'default' },
690
+ variants: {
691
+ state: {
692
+ default: {},
693
+ disabled: { opacity: 'disabled' },
694
+ negative: { color: 'content.negative' },
695
+ },
696
+ },
697
+ })
698
+
699
+ export const popup = style({
700
+ backgroundColor: 'background.elevated',
701
+ borderRadius: 'sm',
702
+ boxShadow: 'regular',
703
+ boxSizing: 'border-box',
704
+ minWidth: '14rem !custom',
705
+ padding: '12',
706
+ })
707
+
708
+ export const separator = style({
709
+ backgroundColor: 'container.strong',
710
+ height: '1px !custom',
711
+ marginBlock: '8',
712
+ marginInline: '12',
713
+ })
714
+ }
715
+ ```
716
+
717
+ ### Show Only Shortcuts You Bind
718
+
719
+ `Menu.Shortcut` displays keys and binds nothing. Add one only if the shortcut also works with the menu closed, and register the key handler yourself.
720
+
721
+ **Do:** Show a shortcut only on an item whose key you bind.
722
+
723
+ ```tsx
724
+ /**
725
+ * Do example for "Show Only Shortcuts You Bind" on the Menu page.
726
+ * Show a shortcut only on an item whose key you bind.
727
+ * @module
728
+ */
729
+ import { style, variants } from '@tempoxyz/ds'
730
+
731
+ /** Renders the static preview, which the guideline card shows inert. */
732
+ export function Demo() {
733
+ return (
734
+ <div {...styles.popup()}>
735
+ <div {...styles.item()}>Send to address</div>
736
+ <div {...styles.item()}>
737
+ Copy address
738
+ <kbd {...styles.shortcut()}>⌘C</kbd>
739
+ </div>
740
+ <div {...styles.item()}>Rename</div>
741
+ </div>
742
+ )
743
+ }
744
+
745
+ namespace styles {
746
+ export const item = variants({
747
+ base: {
748
+ alignItems: 'center',
749
+ borderRadius: 'xs',
750
+ color: 'content.primary',
751
+ columnGap: '8',
752
+ display: 'flex',
753
+ minHeight: '40',
754
+ paddingInline: '12',
755
+ typography: 'body.b2Strong',
756
+ },
757
+ defaultVariants: { state: 'default' },
758
+ variants: {
759
+ state: {
760
+ default: {},
761
+ disabled: { opacity: 'disabled' },
762
+ negative: { color: 'content.negative' },
763
+ },
764
+ },
765
+ })
766
+
767
+ export const popup = style({
768
+ backgroundColor: 'background.elevated',
769
+ borderRadius: 'sm',
770
+ boxShadow: 'regular',
771
+ boxSizing: 'border-box',
772
+ minWidth: '14rem !custom',
773
+ padding: '12',
774
+ })
775
+
776
+ export const shortcut = style({
777
+ color: 'content.secondary',
778
+ marginInlineStart: 'auto !custom',
779
+ paddingInlineStart: '16',
780
+ typography: 'body.b3',
781
+ })
782
+ }
783
+ ```
784
+
785
+ **Don't:** Decorate every item with a shortcut that does nothing.
786
+
787
+ ```tsx
788
+ /**
789
+ * Don't example for "Show Only Shortcuts You Bind" on the Menu page.
790
+ * Don't decorate every item with a shortcut that does nothing.
791
+ * @module
792
+ */
793
+ import { style, variants } from '@tempoxyz/ds'
794
+
795
+ /** Renders the static preview, which the guideline card shows inert. */
796
+ export function Demo() {
797
+ return (
798
+ <div {...styles.popup()}>
799
+ <div {...styles.item()}>
800
+ Send to address
801
+ <kbd {...styles.shortcut()}>⌘S</kbd>
802
+ </div>
803
+ <div {...styles.item()}>
804
+ Copy address
805
+ <kbd {...styles.shortcut()}>⌘C</kbd>
806
+ </div>
807
+ <div {...styles.item()}>
808
+ Rename
809
+ <kbd {...styles.shortcut()}>⌘R</kbd>
810
+ </div>
811
+ </div>
812
+ )
813
+ }
814
+
815
+ namespace styles {
816
+ export const item = variants({
817
+ base: {
818
+ alignItems: 'center',
819
+ borderRadius: 'xs',
820
+ color: 'content.primary',
821
+ columnGap: '8',
822
+ display: 'flex',
823
+ minHeight: '40',
824
+ paddingInline: '12',
825
+ typography: 'body.b2Strong',
826
+ },
827
+ defaultVariants: { state: 'default' },
828
+ variants: {
829
+ state: {
830
+ default: {},
831
+ disabled: { opacity: 'disabled' },
832
+ negative: { color: 'content.negative' },
833
+ },
834
+ },
835
+ })
836
+
837
+ export const popup = style({
838
+ backgroundColor: 'background.elevated',
839
+ borderRadius: 'sm',
840
+ boxShadow: 'regular',
841
+ boxSizing: 'border-box',
842
+ minWidth: '14rem !custom',
843
+ padding: '12',
844
+ })
845
+
846
+ export const shortcut = style({
847
+ color: 'content.secondary',
848
+ marginInlineStart: 'auto !custom',
849
+ paddingInlineStart: '16',
850
+ typography: 'body.b3',
851
+ })
852
+ }
853
+ ```
854
+
855
+ ## Content
856
+
857
+ ### Start Items With a Verb
858
+
859
+ Write a verb, plus an object when the verb is ambiguous: Copy address, Rename, Remove address.
860
+
861
+ ### Lead With the Distinctive Word
862
+
863
+ Typing jumps to the item whose label starts with the typed characters. Start labels with different words. If an item's text starts with something else, such as a token symbol, pass `label`.
864
+
865
+ ### Name Destructive Items Plainly
866
+
867
+ `intent="negative"` only changes the color. Write a verb that states the loss, such as Remove address or Revoke key.
868
+
869
+ ## Accessibility
870
+
871
+ Menu follows the [WAI-ARIA Authoring Practices menu button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/).
872
+
873
+ ### Semantics
874
+
875
+ * **Menu.Trigger (button):** Announces a menu popup and its expanded state.
876
+ * **Menu.Popup (menu):** Named by its trigger.
877
+ * **Menu.Item and Menu.LinkItem (menuitem):** Menu items, and link items render real links.
878
+ * **Menu.CheckboxItem and Menu.RadioItem (aria-checked):** Checkable items that report their checked state.
879
+ * **Menu.Group (group):** Named by its group label.
880
+ * **Menu.Separator (separator):** Announced as a divider between groups of items.
881
+ * **Disabled Items (aria-disabled):** Reachable with arrow keys, but never activates.
882
+ * **Menu.Shortcut (kbd):** Displays the keys as text.
883
+
884
+ ### Keyboard
885
+
886
+ * **Open the Menu (Enter, Space, or Down Arrow):** Opens the menu from the trigger and focuses the first item.
887
+ * **Move to the Next Item (Down Arrow):** Moves focus to the next item, wrapping from the last item to the first.
888
+ * **Move to the Previous Item (Up Arrow):** Moves focus to the previous item, wrapping from the first item to the last.
889
+ * **Move to the First Item (Home):** Moves focus to the first item.
890
+ * **Move to the Last Item (End):** Moves focus to the last item.
891
+ * **Type to Jump (Any character):** Moves focus to the next item whose label starts with the typed characters.
892
+ * **Activate an Item (Enter):** Activates the focused item. Plain items close the menu. Checkbox and radio items keep it open.
893
+ * **Open a Submenu (Right Arrow):** On a submenu trigger, opens the submenu and focuses its first item.
894
+ * **Close a Submenu (Left Arrow):** In a submenu, closes it and returns focus to its trigger.
895
+ * **Close the Menu (Escape):** Closes the menu and returns focus to the trigger.
896
+ * **Leave the Menu (Tab):** Closes the menu and moves focus onward from it.
897
+
898
+ ### Focus
899
+
900
+ * **Opening:** A keyboard open focuses the first item. A click focuses the menu, and arrow keys then move to an item.
901
+ * **Highlight:** One item at a time holds focus and renders highlighted.
902
+ * **Modal (modal):** The default. A press outside closes the menu.
903
+ * **Closing (Escape):** Activating an item, pressing Escape, or pressing outside returns focus to the trigger.
904
+
905
+ ### Your Responsibilities
906
+
907
+ * **Name Icon-Only Triggers (aria-label):** Name the object, such as "Actions for Main wallet". The menu inherits the trigger's name.
908
+ * **Keep Links as Links (a):** Use `Menu.LinkItem` for navigation. It keeps link semantics.
909
+
910
+ ## Related
911
+
912
+ [SplitButton](split-button.md)
913
+
914
+ Use for a main action with alternatives.
915
+
916
+ [Select](select.md)
917
+
918
+ Use for choosing a form value.
919
+
920
+ [AlertDialog](alert-dialog.md)
921
+
922
+ Use for confirming a destructive item.
923
+
924
+ [Popover](popover.md)
925
+
926
+ Use for anchored content other than actions.
927
+
928
+ [Destructive Actions](../patterns/destructive-actions.md)
929
+
930
+ Covers deleting and revoking.
931
+
932
+ [Tables and Lists](../patterns/tables-and-lists.md)
933
+
934
+ Covers row actions.