@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.
- package/dist/docs/AGENTS.md +9 -0
- package/dist/docs/components/accordion.md +489 -0
- package/dist/docs/components/address-card.md +147 -0
- package/dist/docs/components/agent-logo.md +194 -0
- package/dist/docs/components/alert-dialog.md +631 -0
- package/dist/docs/components/alert.md +448 -0
- package/dist/docs/components/amount-input.md +305 -0
- package/dist/docs/components/amount.md +794 -0
- package/dist/docs/components/app-shell.md +559 -0
- package/dist/docs/components/asset-chip.md +294 -0
- package/dist/docs/components/autocomplete.md +619 -0
- package/dist/docs/components/avatar.md +321 -0
- package/dist/docs/components/badge.md +339 -0
- package/dist/docs/components/badged-logo.md +116 -0
- package/dist/docs/components/banner.md +379 -0
- package/dist/docs/components/bar-chart.md +208 -0
- package/dist/docs/components/breadcrumbs.md +380 -0
- package/dist/docs/components/button-group.md +286 -0
- package/dist/docs/components/button.md +650 -0
- package/dist/docs/components/calendar.md +375 -0
- package/dist/docs/components/callout.md +96 -0
- package/dist/docs/components/checkbox-group.md +521 -0
- package/dist/docs/components/checkbox.md +467 -0
- package/dist/docs/components/choice-group.md +516 -0
- package/dist/docs/components/choicebox.md +641 -0
- package/dist/docs/components/code.md +100 -0
- package/dist/docs/components/combobox.md +939 -0
- package/dist/docs/components/container.md +257 -0
- package/dist/docs/components/copy-button.md +642 -0
- package/dist/docs/components/date-picker.md +514 -0
- package/dist/docs/components/dialog.md +1332 -0
- package/dist/docs/components/drawer.md +966 -0
- package/dist/docs/components/empty.md +340 -0
- package/dist/docs/components/field.md +570 -0
- package/dist/docs/components/fieldset.md +398 -0
- package/dist/docs/components/frame.md +434 -0
- package/dist/docs/components/gauge.md +299 -0
- package/dist/docs/components/get-started-visual.md +117 -0
- package/dist/docs/components/grid.md +288 -0
- package/dist/docs/components/hero-visual.md +155 -0
- package/dist/docs/components/hex.md +487 -0
- package/dist/docs/components/icon-tile.md +292 -0
- package/dist/docs/components/identicon.md +371 -0
- package/dist/docs/components/inline.md +233 -0
- package/dist/docs/components/inset.md +225 -0
- package/dist/docs/components/kbd.md +423 -0
- package/dist/docs/components/list-group.md +981 -0
- package/dist/docs/components/loading-dots.md +237 -0
- package/dist/docs/components/menu.md +934 -0
- package/dist/docs/components/native-select.md +204 -0
- package/dist/docs/components/network-logo.md +248 -0
- package/dist/docs/components/number-field.md +543 -0
- package/dist/docs/components/otp-field.md +616 -0
- package/dist/docs/components/pagination.md +516 -0
- package/dist/docs/components/popover.md +544 -0
- package/dist/docs/components/progress.md +388 -0
- package/dist/docs/components/qr.md +314 -0
- package/dist/docs/components/radio-group.md +528 -0
- package/dist/docs/components/secret-value.md +406 -0
- package/dist/docs/components/select.md +751 -0
- package/dist/docs/components/separator.md +379 -0
- package/dist/docs/components/sidebar-menu.md +922 -0
- package/dist/docs/components/skeleton.md +517 -0
- package/dist/docs/components/slider.md +539 -0
- package/dist/docs/components/snippet.md +424 -0
- package/dist/docs/components/spinner.md +216 -0
- package/dist/docs/components/split-bar.md +139 -0
- package/dist/docs/components/split-button.md +464 -0
- package/dist/docs/components/stack.md +299 -0
- package/dist/docs/components/status-chip.md +226 -0
- package/dist/docs/components/status-indicator.md +119 -0
- package/dist/docs/components/status-row.md +174 -0
- package/dist/docs/components/stepper.md +451 -0
- package/dist/docs/components/success-hero.md +157 -0
- package/dist/docs/components/surface.md +639 -0
- package/dist/docs/components/switch.md +472 -0
- package/dist/docs/components/table.md +967 -0
- package/dist/docs/components/tabs.md +627 -0
- package/dist/docs/components/tempo-logo.md +185 -0
- package/dist/docs/components/text-input.md +624 -0
- package/dist/docs/components/textarea.md +352 -0
- package/dist/docs/components/toast.md +535 -0
- package/dist/docs/components/token-logo.md +371 -0
- package/dist/docs/components/token.md +247 -0
- package/dist/docs/components/tooltip.md +490 -0
- package/dist/docs/components/verification-code.md +116 -0
- package/dist/docs/components/visual.md +249 -0
- package/dist/docs/components.md +203 -0
- package/dist/docs/foundations/accessibility.md +377 -0
- package/dist/docs/foundations/agents.md +150 -0
- package/dist/docs/foundations/color.md +522 -0
- package/dist/docs/foundations/content/tone.md +194 -0
- package/dist/docs/foundations/content/voice.md +194 -0
- package/dist/docs/foundations/content/writing-style.md +406 -0
- package/dist/docs/foundations/iconography.md +282 -0
- package/dist/docs/foundations/intent.md +360 -0
- package/dist/docs/foundations/layout.md +508 -0
- package/dist/docs/foundations/linter.md +296 -0
- package/dist/docs/foundations/motion.md +151 -0
- package/dist/docs/foundations/radius-and-padding.md +86 -0
- package/dist/docs/foundations/typography.md +266 -0
- package/dist/docs/index.md +137 -0
- package/dist/docs/introduction/faq.md +132 -0
- package/dist/docs/introduction/getting-started.md +377 -0
- package/dist/docs/patterns/blockchain-values.md +709 -0
- package/dist/docs/patterns/dashboards.md +638 -0
- package/dist/docs/patterns/destructive-actions.md +626 -0
- package/dist/docs/patterns/empty-states.md +612 -0
- package/dist/docs/patterns/errors-and-validation.md +808 -0
- package/dist/docs/patterns/feedback-and-status.md +518 -0
- package/dist/docs/patterns/forms.md +679 -0
- package/dist/docs/patterns/loading.md +630 -0
- package/dist/docs/patterns/navigation.md +397 -0
- package/dist/docs/patterns/payments.md +765 -0
- package/dist/docs/patterns/responsive-layout.md +997 -0
- package/dist/docs/patterns/tables-and-lists.md +964 -0
- package/dist/docs/products/tempo-console.md +671 -0
- package/dist/docs/products/tempo-performance.md +437 -0
- package/dist/docs/products/tempo-wallet.md +438 -0
- package/dist/web/components/OtpField.css +1 -1
- package/dist/web/components/OtpField.d.ts +1 -1
- package/dist/web/components/OtpField.d.ts.map +1 -1
- package/dist/web/components/OtpField.js +3 -4
- package/dist/web/components/OtpField.js.map +1 -1
- package/dist/web/components/OtpField.js.zyzz.json +1 -1
- package/dist/web/index.js.zyzz.json +1 -1
- package/package.json +1 -1
- package/src/web/components/OtpField.tsx +2 -24
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
# AppShell
|
|
2
|
+
|
|
3
|
+
Places a sticky sidebar beside the main content column.
|
|
4
|
+
|
|
5
|
+
[View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/AppShell.tsx)
|
|
6
|
+
|
|
7
|
+
Figma: [Layout/App Shell](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-5593) (`AppShell.Root`), [Layout/Page Header](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-5633) (`AppShell.Header`), [Navigation/Tab Bar](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5265-2292) (`AppShell.Root`)
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
The outer frame of an application. Below 1024px, the sidebar moves into a drawer, which a menu button in a bottom bar opens. With `collapse`, the sidebar collapses to its icons until 768px instead. The bottom bar also repeats the items you choose.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### Basic
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
/** Composes the Tempo Wallet shell: the account row, the wallet pages, the theme item and a page header. @module */
|
|
19
|
+
import { AppShell, Avatar, Hex, Kbd, Menu, SidebarMenu, StatusChip, style, Surface } from '@tempoxyz/ds'
|
|
20
|
+
import { Clock, DarkMode, Home, Link, LogOut, Plus, Wallet } from '@tempoxyz/ds/icons'
|
|
21
|
+
|
|
22
|
+
export const fill = true
|
|
23
|
+
const address = '0x7a3f2c19b0e4d85a6f1c3e9b2d7a40c58e6f91c2'
|
|
24
|
+
|
|
25
|
+
const balances = ['pathUSD', 'USDC.e', 'AlphaUSD']
|
|
26
|
+
|
|
27
|
+
export function Demo() {
|
|
28
|
+
return (
|
|
29
|
+
<div {...styles.frame()}>
|
|
30
|
+
<AppShell.Root
|
|
31
|
+
align="start"
|
|
32
|
+
collapse
|
|
33
|
+
style={{ height: '100%', minHeight: 0, overflowY: 'auto' }}
|
|
34
|
+
>
|
|
35
|
+
<AppShell.Sidebar aria-label="Wallet" style={{ height: '100%' }}>
|
|
36
|
+
<AppShell.SidebarHeader>
|
|
37
|
+
<Menu.Root>
|
|
38
|
+
<Menu.Trigger
|
|
39
|
+
render={<SidebarMenu.AccountRow description="Tempo Wallet" label="Piril Akay" />}
|
|
40
|
+
>
|
|
41
|
+
<Avatar size="32">
|
|
42
|
+
<Avatar.Fallback>PA</Avatar.Fallback>
|
|
43
|
+
</Avatar>
|
|
44
|
+
</Menu.Trigger>
|
|
45
|
+
<Menu.Popup>
|
|
46
|
+
<Menu.Item>
|
|
47
|
+
<Plus />
|
|
48
|
+
Add account
|
|
49
|
+
</Menu.Item>
|
|
50
|
+
<Menu.Separator />
|
|
51
|
+
<Menu.Item intent="negative">
|
|
52
|
+
<LogOut />
|
|
53
|
+
Sign out
|
|
54
|
+
</Menu.Item>
|
|
55
|
+
</Menu.Popup>
|
|
56
|
+
</Menu.Root>
|
|
57
|
+
</AppShell.SidebarHeader>
|
|
58
|
+
<AppShell.SidebarBody>
|
|
59
|
+
<SidebarMenu.Root>
|
|
60
|
+
<SidebarMenu.Group aria-label="Wallet">
|
|
61
|
+
<SidebarMenu.Item active href="#" tabBar="tab">
|
|
62
|
+
<Home />
|
|
63
|
+
Home
|
|
64
|
+
</SidebarMenu.Item>
|
|
65
|
+
<SidebarMenu.Item href="#" tabBar="tab">
|
|
66
|
+
<Clock />
|
|
67
|
+
Activity
|
|
68
|
+
</SidebarMenu.Item>
|
|
69
|
+
<SidebarMenu.Item href="#" tabBar="tab">
|
|
70
|
+
<Wallet />
|
|
71
|
+
Assets
|
|
72
|
+
</SidebarMenu.Item>
|
|
73
|
+
<SidebarMenu.Item href="#">
|
|
74
|
+
<Link />
|
|
75
|
+
Connections
|
|
76
|
+
</SidebarMenu.Item>
|
|
77
|
+
</SidebarMenu.Group>
|
|
78
|
+
</SidebarMenu.Root>
|
|
79
|
+
</AppShell.SidebarBody>
|
|
80
|
+
<AppShell.SidebarFooter>
|
|
81
|
+
<SidebarMenu.Root aria-label="Preferences">
|
|
82
|
+
<SidebarMenu.Item render={<button type="button" />}>
|
|
83
|
+
<DarkMode />
|
|
84
|
+
<span {...styles.grow()}>Dark mode</span>
|
|
85
|
+
<Kbd alt size="small">
|
|
86
|
+
M
|
|
87
|
+
</Kbd>
|
|
88
|
+
</SidebarMenu.Item>
|
|
89
|
+
</SidebarMenu.Root>
|
|
90
|
+
</AppShell.SidebarFooter>
|
|
91
|
+
</AppShell.Sidebar>
|
|
92
|
+
<AppShell.Main>
|
|
93
|
+
<AppShell.Header>
|
|
94
|
+
<AppShell.Title>Home</AppShell.Title>
|
|
95
|
+
<div {...styles.actions()}>
|
|
96
|
+
<StatusChip intent="positive" network>
|
|
97
|
+
Mainnet
|
|
98
|
+
</StatusChip>
|
|
99
|
+
<Hex.Root size="small" value={address} variant="filled" {...styles.address()}>
|
|
100
|
+
<Hex.Text />
|
|
101
|
+
<Hex.Copy aria-label="Copy wallet address" />
|
|
102
|
+
</Hex.Root>
|
|
103
|
+
</div>
|
|
104
|
+
<AppShell.Trigger />
|
|
105
|
+
</AppShell.Header>
|
|
106
|
+
<div {...styles.list()}>
|
|
107
|
+
{balances.map((balance) => (
|
|
108
|
+
<Surface key={balance}>
|
|
109
|
+
<p {...styles.name()}>{balance}</p>
|
|
110
|
+
<p {...styles.caption()}>Available to send</p>
|
|
111
|
+
</Surface>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
</AppShell.Main>
|
|
115
|
+
</AppShell.Root>
|
|
116
|
+
</div>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
namespace styles {
|
|
121
|
+
// The bottom bar takes over below 768px, so the header keeps only its title.
|
|
122
|
+
export const actions = style({
|
|
123
|
+
alignItems: 'center',
|
|
124
|
+
columnGap: '8',
|
|
125
|
+
display: 'flex',
|
|
126
|
+
'@media (max-width: 767px)': { display: 'none' },
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
export const address = style({ maxWidth: '10rem !custom' })
|
|
130
|
+
|
|
131
|
+
export const caption = style({ color: 'content.secondary', margin: '0', typography: 'body.b3' })
|
|
132
|
+
|
|
133
|
+
// The transform makes the frame the containing block for the mobile drawer.
|
|
134
|
+
export const frame = style({
|
|
135
|
+
borderRadius: 'sm',
|
|
136
|
+
height: '32rem !custom',
|
|
137
|
+
overflow: 'hidden',
|
|
138
|
+
transform: 'translateZ(0) !custom',
|
|
139
|
+
selectors: {
|
|
140
|
+
// The maximized gallery preview shows the shell at full window height.
|
|
141
|
+
':root[data-maximized] &': { borderRadius: '0px !custom', height: '100vh !custom' },
|
|
142
|
+
},
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
export const grow = style({ flex: '1 !custom', minWidth: '0 !custom' })
|
|
146
|
+
|
|
147
|
+
export const list = style({ display: 'flex', flexDirection: 'column', rowGap: '12' })
|
|
148
|
+
|
|
149
|
+
export const name = style({ margin: '0', typography: 'body.b2' })
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/AppShell/Basic.tsx)
|
|
154
|
+
|
|
155
|
+
## Anatomy
|
|
156
|
+
|
|
157
|
+
Import the component and assemble its parts.
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { AppShell } from '@tempoxyz/ds'
|
|
161
|
+
|
|
162
|
+
<AppShell.Root>
|
|
163
|
+
<AppShell.Sidebar>
|
|
164
|
+
<AppShell.SidebarHeader />
|
|
165
|
+
<AppShell.SidebarBody />
|
|
166
|
+
<AppShell.SidebarFooter />
|
|
167
|
+
</AppShell.Sidebar>
|
|
168
|
+
<AppShell.Main>
|
|
169
|
+
<AppShell.Header>
|
|
170
|
+
<AppShell.Title />
|
|
171
|
+
</AppShell.Header>
|
|
172
|
+
</AppShell.Main>
|
|
173
|
+
<AppShell.Trigger />
|
|
174
|
+
</AppShell.Root>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Guidelines
|
|
178
|
+
|
|
179
|
+
### When to Use
|
|
180
|
+
|
|
181
|
+
* Frame a product with persistent navigation, such as Tempo Console or the web wallet. Render it once, in the root layout.
|
|
182
|
+
* Pair a [SidebarMenu](/docs/components/sidebar-menu) with a content column of tables, cards, or settings.
|
|
183
|
+
|
|
184
|
+
### When Not to Use
|
|
185
|
+
|
|
186
|
+
* Use [Container](/docs/components/container) for pages without persistent navigation, such as sign-in, onboarding, or a public receipt.
|
|
187
|
+
* Use [Dialog](/docs/components/dialog), or a full-screen flow in a [Container](/docs/components/container), for focused tasks such as Send payment.
|
|
188
|
+
* Use [Drawer](/docs/components/drawer) for a details or filter panel that opens on demand. The shell's drawer holds only primary navigation.
|
|
189
|
+
* Use [Frame](/docs/components/frame) for a header, body, and footer inside a card or popup.
|
|
190
|
+
|
|
191
|
+
### Plan for Each Layout
|
|
192
|
+
|
|
193
|
+
The shell switches layouts at 768px and 1024px (the `'768'` and `'1024'` breakpoints). Design and test each.
|
|
194
|
+
|
|
195
|
+
* **Below 768px (Bottom bar):** The sidebar becomes a drawer from the right edge, and a bottom bar holds the chosen items and the menu button.
|
|
196
|
+
* **768px to 1023px (collapse):** The drawer and bottom bar by default. With `collapse`, a 64px column of icons that keep their labels as names.
|
|
197
|
+
* **1024px and Up (28px padding):** The sidebar sticks beside the content with its own scroll, and the bottom bar hides.
|
|
198
|
+
|
|
199
|
+
The drawer is `min(18rem, 85vw)` wide. Server rendering emits the inline sidebar, and CSS hides it below 1024px, or collapses it until 768px with `collapse`.
|
|
200
|
+
|
|
201
|
+
### Always Render the Trigger
|
|
202
|
+
|
|
203
|
+
Render `AppShell.Trigger` once inside `AppShell.Root`. It adds the menu button to the end of the bottom bar, so the header keeps its full width for the title and actions.
|
|
204
|
+
|
|
205
|
+
**Do:** Render the trigger once in the shell.
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
/**
|
|
209
|
+
* Do example for "Always Render the Trigger" on the AppShell page.
|
|
210
|
+
* Render the trigger once in the shell.
|
|
211
|
+
* @module
|
|
212
|
+
*/
|
|
213
|
+
import { Button, style, Surface } from '@tempoxyz/ds'
|
|
214
|
+
import { Menu } from '@tempoxyz/ds/icons'
|
|
215
|
+
|
|
216
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
217
|
+
export function Demo() {
|
|
218
|
+
return (
|
|
219
|
+
<Surface size="small" {...styles.frame()}>
|
|
220
|
+
<span {...styles.title()}>Balances</span>
|
|
221
|
+
<div {...styles.bar()}>
|
|
222
|
+
<Button aria-label="Open menu" shape="icon" size="small">
|
|
223
|
+
<Menu />
|
|
224
|
+
</Button>
|
|
225
|
+
</div>
|
|
226
|
+
</Surface>
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
namespace styles {
|
|
231
|
+
export const bar = style({ display: 'flex', justifyContent: 'flex-end' })
|
|
232
|
+
|
|
233
|
+
export const frame = style({
|
|
234
|
+
display: 'flex',
|
|
235
|
+
flexDirection: 'column',
|
|
236
|
+
gap: '24',
|
|
237
|
+
width: '18rem !custom',
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
export const title = style({ color: 'content.primary', typography: 'heading.h3' })
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Don't:** Leave it out. Below 1024px, or 768px with `collapse`, it is the only way to reach every
|
|
245
|
+
destination.
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
/**
|
|
249
|
+
* Don't example for "Always Render the Trigger" on the AppShell page.
|
|
250
|
+
* Don't leave it out. Below 1024px, it is the only way to reach navigation.
|
|
251
|
+
* @module
|
|
252
|
+
*/
|
|
253
|
+
import { style, Surface } from '@tempoxyz/ds'
|
|
254
|
+
|
|
255
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
256
|
+
export function Demo() {
|
|
257
|
+
return (
|
|
258
|
+
<Surface size="small" {...styles.frame()}>
|
|
259
|
+
<span {...styles.title()}>Balances</span>
|
|
260
|
+
<div {...styles.bar()} />
|
|
261
|
+
</Surface>
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
namespace styles {
|
|
266
|
+
// Matches the height of the bottom bar in the Do example, so only the missing button differs.
|
|
267
|
+
export const bar = style({ minHeight: '32' })
|
|
268
|
+
|
|
269
|
+
export const frame = style({
|
|
270
|
+
display: 'flex',
|
|
271
|
+
flexDirection: 'column',
|
|
272
|
+
gap: '24',
|
|
273
|
+
width: '18rem !custom',
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
export const title = style({ color: 'content.primary', typography: 'heading.h3' })
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Choose an Alignment
|
|
281
|
+
|
|
282
|
+
* **Centered (center):** The default. Centers the sidebar beside a content column up to 1024px wide.
|
|
283
|
+
* **Start (start):** Pins the sidebar left, and the content fills the row. Use it for wide data views.
|
|
284
|
+
|
|
285
|
+
### Structure the Sidebar
|
|
286
|
+
|
|
287
|
+
* **AppShell.SidebarHeader:** Holds the product or project switcher at the top of the sidebar.
|
|
288
|
+
* **AppShell.SidebarBody:** Holds the sidebar menu. It scrolls on its own and fades the edge with more content.
|
|
289
|
+
* **AppShell.SidebarFooter:** Holds the account row and other controls, pinned to the bottom edge.
|
|
290
|
+
|
|
291
|
+
Keep the header and footer short so the menu keeps most of the height. Set `--sidebar-width` to change the width of the wide sidebar.
|
|
292
|
+
|
|
293
|
+
### Choose the Bottom Bar Items
|
|
294
|
+
|
|
295
|
+
Wherever the drawer replaces the sidebar, a bottom bar repeats the [SidebarMenu](/docs/components/sidebar-menu) items that set `tabBar`, in sidebar order, and ends with the menu button. Keep the rest in the drawer.
|
|
296
|
+
|
|
297
|
+
* **Tab (tabBar="tab"):** Shows the icon in the tab bar pill. Choose up to three top destinations.
|
|
298
|
+
* **Action (tabBar="action"):** Shows the icon in a floating button beside the pill. Choose one main action.
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
<SidebarMenu.Item active href="/" tabBar="tab">
|
|
302
|
+
<Home />
|
|
303
|
+
Home
|
|
304
|
+
</SidebarMenu.Item>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Use One Title per Page
|
|
308
|
+
|
|
309
|
+
`AppShell.Title` renders the page's `h1` in the header bar across the top of the content column. Keep one primary action in the header.
|
|
310
|
+
|
|
311
|
+
**Do:** Put the title first, then page actions, such as a `small` [Button](/docs/components/button).
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
/**
|
|
315
|
+
* Do example for "Use One Title per Page" on the AppShell page.
|
|
316
|
+
* Put the title first, then page actions, such as a `small` Button.
|
|
317
|
+
* @module
|
|
318
|
+
*/
|
|
319
|
+
import { Button, style, Surface } from '@tempoxyz/ds'
|
|
320
|
+
|
|
321
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
322
|
+
export function Demo() {
|
|
323
|
+
return (
|
|
324
|
+
<Surface size="small" {...styles.frame()}>
|
|
325
|
+
<div {...styles.header()}>
|
|
326
|
+
<span {...styles.title()}>API keys</span>
|
|
327
|
+
<Button size="small">New key</Button>
|
|
328
|
+
</div>
|
|
329
|
+
</Surface>
|
|
330
|
+
)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
namespace styles {
|
|
334
|
+
export const frame = style({ width: '18rem !custom' })
|
|
335
|
+
|
|
336
|
+
export const header = style({ alignItems: 'center', display: 'flex', gap: '12' })
|
|
337
|
+
|
|
338
|
+
export const title = style({
|
|
339
|
+
color: 'content.primary',
|
|
340
|
+
flex: '1 !custom',
|
|
341
|
+
typography: 'heading.h3',
|
|
342
|
+
})
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Don't:** Place actions before the title.
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
/** Don't example for "Use One Title per Page" on the AppShell page. Don't place actions before the title. @module */
|
|
350
|
+
import { Button, style, Surface } from '@tempoxyz/ds'
|
|
351
|
+
|
|
352
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
353
|
+
export function Demo() {
|
|
354
|
+
return (
|
|
355
|
+
<Surface size="small" {...styles.frame()}>
|
|
356
|
+
<div {...styles.header()}>
|
|
357
|
+
<Button size="small">New key</Button>
|
|
358
|
+
<span {...styles.title()}>API keys</span>
|
|
359
|
+
</div>
|
|
360
|
+
</Surface>
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
namespace styles {
|
|
365
|
+
export const frame = style({ width: '18rem !custom' })
|
|
366
|
+
|
|
367
|
+
export const header = style({ alignItems: 'center', display: 'flex', gap: '12' })
|
|
368
|
+
|
|
369
|
+
export const title = style({
|
|
370
|
+
color: 'content.primary',
|
|
371
|
+
flex: '1 !custom',
|
|
372
|
+
typography: 'heading.h3',
|
|
373
|
+
})
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Close the Drawer After Navigating
|
|
378
|
+
|
|
379
|
+
With a client-side router, the drawer stays open when the route changes. Control `AppShell.Root` with `open` and `onOpenChange`, and set `open` to `false` when the location changes.
|
|
380
|
+
|
|
381
|
+
**Do:** Close the drawer, so the new page shows.
|
|
382
|
+
|
|
383
|
+
```tsx
|
|
384
|
+
/**
|
|
385
|
+
* Do example for "Close the Drawer After Navigating" on the AppShell page.
|
|
386
|
+
* Close the drawer, so the new page shows.
|
|
387
|
+
* @module
|
|
388
|
+
*/
|
|
389
|
+
import { Button, style } from '@tempoxyz/ds'
|
|
390
|
+
import { Menu } from '@tempoxyz/ds/icons'
|
|
391
|
+
|
|
392
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
393
|
+
export function Demo() {
|
|
394
|
+
return (
|
|
395
|
+
<div {...styles.screen()}>
|
|
396
|
+
<div {...styles.header()}>
|
|
397
|
+
<span {...styles.title()}>API keys</span>
|
|
398
|
+
<Button aria-label="Open menu" shape="icon" size="small" variant="secondary">
|
|
399
|
+
<Menu />
|
|
400
|
+
</Button>
|
|
401
|
+
</div>
|
|
402
|
+
<span {...styles.meta()}>2 active keys</span>
|
|
403
|
+
</div>
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
namespace styles {
|
|
408
|
+
export const header = style({
|
|
409
|
+
alignItems: 'center',
|
|
410
|
+
display: 'flex',
|
|
411
|
+
gap: '12',
|
|
412
|
+
justifyContent: 'space-between',
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
export const meta = style({ color: 'content.secondary', typography: 'body.b2' })
|
|
416
|
+
|
|
417
|
+
export const screen = style({
|
|
418
|
+
display: 'flex',
|
|
419
|
+
flexDirection: 'column',
|
|
420
|
+
height: '9rem !custom',
|
|
421
|
+
position: 'relative',
|
|
422
|
+
rowGap: '8',
|
|
423
|
+
width: '18rem !custom',
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
export const title = style({ color: 'content.primary', typography: 'heading.h3' })
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**Don't:** Leave the drawer over the new page.
|
|
431
|
+
|
|
432
|
+
```tsx
|
|
433
|
+
/**
|
|
434
|
+
* Don't example for "Close the Drawer After Navigating" on the AppShell page.
|
|
435
|
+
* Don't leave the drawer over the new page.
|
|
436
|
+
* @module
|
|
437
|
+
*/
|
|
438
|
+
import { Button, SidebarMenu, style, Surface } from '@tempoxyz/ds'
|
|
439
|
+
import { Home, Key, Menu } from '@tempoxyz/ds/icons'
|
|
440
|
+
|
|
441
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
442
|
+
export function Demo() {
|
|
443
|
+
return (
|
|
444
|
+
<div {...styles.screen()}>
|
|
445
|
+
<div {...styles.header()}>
|
|
446
|
+
<span {...styles.title()}>API keys</span>
|
|
447
|
+
<Button aria-label="Open menu" shape="icon" size="small" variant="secondary">
|
|
448
|
+
<Menu />
|
|
449
|
+
</Button>
|
|
450
|
+
</div>
|
|
451
|
+
<span {...styles.meta()}>2 active keys</span>
|
|
452
|
+
<Surface size="small" {...styles.drawer()}>
|
|
453
|
+
<SidebarMenu.Root aria-label="Primary" size="small">
|
|
454
|
+
<SidebarMenu.Item href="#">
|
|
455
|
+
<Home />
|
|
456
|
+
Home
|
|
457
|
+
</SidebarMenu.Item>
|
|
458
|
+
<SidebarMenu.Item active href="#">
|
|
459
|
+
<Key />
|
|
460
|
+
API keys
|
|
461
|
+
</SidebarMenu.Item>
|
|
462
|
+
</SidebarMenu.Root>
|
|
463
|
+
</Surface>
|
|
464
|
+
</div>
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
namespace styles {
|
|
469
|
+
export const drawer = style({
|
|
470
|
+
insetBlock: '0 !custom',
|
|
471
|
+
insetInlineEnd: '0 !custom',
|
|
472
|
+
position: 'absolute',
|
|
473
|
+
width: '11rem !custom',
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
export const header = style({
|
|
477
|
+
alignItems: 'center',
|
|
478
|
+
display: 'flex',
|
|
479
|
+
gap: '12',
|
|
480
|
+
justifyContent: 'space-between',
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
export const meta = style({ color: 'content.secondary', typography: 'body.b2' })
|
|
484
|
+
|
|
485
|
+
export const screen = style({
|
|
486
|
+
display: 'flex',
|
|
487
|
+
flexDirection: 'column',
|
|
488
|
+
height: '9rem !custom',
|
|
489
|
+
position: 'relative',
|
|
490
|
+
rowGap: '8',
|
|
491
|
+
width: '18rem !custom',
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
export const title = style({ color: 'content.primary', typography: 'heading.h3' })
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
## Content
|
|
499
|
+
|
|
500
|
+
* Match `AppShell.Title` to the label of the active [SidebarMenu](/docs/components/sidebar-menu) item, such as API keys.
|
|
501
|
+
* Keep titles short enough to share the header row with its actions at 360px.
|
|
502
|
+
|
|
503
|
+
## Accessibility
|
|
504
|
+
|
|
505
|
+
Provides the page landmarks and the `h1`. Below 1024px, the sidebar drawer follows the [WAI-ARIA Dialog (Modal) pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/).
|
|
506
|
+
|
|
507
|
+
### Semantics
|
|
508
|
+
|
|
509
|
+
* **AppShell.Main (main):** The page's main landmark.
|
|
510
|
+
* **AppShell.Header (header):** Holds the menu trigger and the page title.
|
|
511
|
+
* **AppShell.Title (h1):** The page's top-level heading.
|
|
512
|
+
* **Wide AppShell.Sidebar (complementary):** From 1024px, a landmark named by `aria-label`.
|
|
513
|
+
* **Narrow AppShell.Sidebar (dialog):** Below 1024px, a dialog with the same name.
|
|
514
|
+
* **AppShell.Trigger (button):** Named "Open menu" or "Close menu" by state.
|
|
515
|
+
|
|
516
|
+
### Keyboard
|
|
517
|
+
|
|
518
|
+
* **Move Through the Shell (Tab):** Moves to the trigger, or cycles the open drawer.
|
|
519
|
+
* **Open the Drawer (Enter or Space):** Opens the drawer from the trigger.
|
|
520
|
+
* **Close the Drawer (Escape):** Closes the drawer.
|
|
521
|
+
|
|
522
|
+
### Focus
|
|
523
|
+
|
|
524
|
+
* **Opening (Tab or Shift+Tab):** The drawer is modal. Opening it moves focus inside and traps it.
|
|
525
|
+
* **While Open (aria-hidden):** Hides the rest of the page, including the trigger, from assistive technology.
|
|
526
|
+
* **Closing:** Escape, the backdrop, or a swipe right closes the drawer and returns focus to the trigger.
|
|
527
|
+
|
|
528
|
+
### Motion and Size
|
|
529
|
+
|
|
530
|
+
* **Reduced Motion (prefers-reduced-motion):** The drawer fades, and the trigger switches instantly.
|
|
531
|
+
* **Minimum Width (360px):** The shell never shrinks below this width.
|
|
532
|
+
|
|
533
|
+
### Your Responsibilities
|
|
534
|
+
|
|
535
|
+
* **Name the Sidebar (aria-label):** Name the sidebar, such as "Primary". The name labels the landmark and the drawer.
|
|
536
|
+
* **Use One Title (h1):** Use one page title. Start the headings of the content at `h2`.
|
|
537
|
+
* **Add a Skip Link (SC 2.4.1):** If the sidebar holds many links, add a [skip link](https://www.w3.org/WAI/WCAG22/Understanding/bypass-blocks.html). The shell adds none.
|
|
538
|
+
|
|
539
|
+
## Related
|
|
540
|
+
|
|
541
|
+
[SidebarMenu](/docs/components/sidebar-menu)
|
|
542
|
+
|
|
543
|
+
Use for the navigation inside the sidebar.
|
|
544
|
+
|
|
545
|
+
[Container](/docs/components/container)
|
|
546
|
+
|
|
547
|
+
Use for pages without a sidebar.
|
|
548
|
+
|
|
549
|
+
[Drawer](/docs/components/drawer)
|
|
550
|
+
|
|
551
|
+
Use for panels that open on demand.
|
|
552
|
+
|
|
553
|
+
[Responsive Layout](/docs/patterns/responsive-layout)
|
|
554
|
+
|
|
555
|
+
Covers adapting pages across breakpoints.
|
|
556
|
+
|
|
557
|
+
[Navigation](/docs/patterns/navigation)
|
|
558
|
+
|
|
559
|
+
Covers choosing between navigation components.
|