@ceed/ads 1.23.2 → 1.23.4
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/components/data-display/Badge.md +71 -39
- package/dist/components/data-display/InfoSign.md +74 -98
- package/dist/components/data-display/Typography.md +310 -61
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Dialog.md +8 -4
- package/dist/components/feedback/Modal.md +7 -3
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/inputs/ButtonGroup.md +115 -106
- package/dist/components/inputs/Calendar.md +98 -459
- package/dist/components/inputs/CurrencyInput.md +181 -8
- package/dist/components/inputs/DatePicker.md +108 -436
- package/dist/components/inputs/DateRangePicker.md +130 -496
- package/dist/components/inputs/FilterMenu.md +169 -19
- package/dist/components/inputs/FilterableCheckboxGroup.md +119 -24
- package/dist/components/inputs/FormControl.md +368 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/MonthPicker.md +95 -427
- package/dist/components/inputs/MonthRangePicker.md +89 -471
- package/dist/components/inputs/PercentageInput.md +183 -19
- package/dist/components/inputs/RadioButton.md +163 -35
- package/dist/components/inputs/RadioList.md +241 -0
- package/dist/components/inputs/RadioTileGroup.md +146 -62
- package/dist/components/inputs/Select.md +219 -328
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +136 -376
- package/dist/components/inputs/Textarea.md +209 -11
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +3 -0
- package/dist/components/navigation/Breadcrumbs.md +80 -322
- package/dist/components/navigation/Dropdown.md +92 -221
- package/dist/components/navigation/IconMenuButton.md +40 -502
- package/dist/components/navigation/InsetDrawer.md +68 -738
- package/dist/components/navigation/Link.md +39 -298
- package/dist/components/navigation/Menu.md +92 -285
- package/dist/components/navigation/MenuButton.md +55 -448
- package/dist/components/navigation/Pagination.md +47 -338
- package/dist/components/navigation/ProfileMenu.md +45 -268
- package/dist/components/navigation/Stepper.md +160 -28
- package/dist/components/navigation/Tabs.md +57 -316
- package/dist/components/surfaces/Sheet.md +150 -333
- package/dist/guides/ThemeProvider.md +116 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/llms.txt +8 -0
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Tabs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Tabs organize content into separate views where only one view is visible at a time. Users switch between views by selecting tab buttons, enabling efficient navigation within related content sections without leaving the current page. Built on Joy UI's Tabs, TabList, Tab, and TabPanel components, Tabs support horizontal and vertical orientations, multiple visual variants, semantic colors, and icon decorators.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Tabs follow a composition pattern: the `Tabs` container manages state, `TabList` holds the tab buttons, `Tab` is each individual button, and `TabPanel` displays the content for the active tab.
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
8
|
<Tabs {...args} defaultValue={0}>
|
|
@@ -50,11 +50,11 @@ function MyComponent() {
|
|
|
50
50
|
}
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
##
|
|
53
|
+
## Features
|
|
54
54
|
|
|
55
55
|
### Basic Tabs
|
|
56
56
|
|
|
57
|
-
Default tab navigation with
|
|
57
|
+
Default horizontal tab navigation with tab panels.
|
|
58
58
|
|
|
59
59
|
```tsx
|
|
60
60
|
<Tabs {...args}>
|
|
@@ -77,7 +77,7 @@ Default tab navigation with horizontal layout.
|
|
|
77
77
|
|
|
78
78
|
### Variants
|
|
79
79
|
|
|
80
|
-
Tabs support
|
|
80
|
+
Tabs support `plain`, `outlined`, `soft`, and `solid` visual styles. Apply the variant to both TabList and individual Tab components for a consistent appearance.
|
|
81
81
|
|
|
82
82
|
```tsx
|
|
83
83
|
<div style={{
|
|
@@ -118,7 +118,7 @@ Tabs support different visual styles through variants.
|
|
|
118
118
|
|
|
119
119
|
### Sizes
|
|
120
120
|
|
|
121
|
-
Tabs come in
|
|
121
|
+
Tabs come in `sm`, `md`, and `lg` sizes. Set the `size` prop on the Tabs container to affect all child components uniformly.
|
|
122
122
|
|
|
123
123
|
```tsx
|
|
124
124
|
<div style={{
|
|
@@ -152,7 +152,7 @@ Tabs come in different sizes to fit various use cases.
|
|
|
152
152
|
|
|
153
153
|
### Colors
|
|
154
154
|
|
|
155
|
-
Apply semantic colors to tabs.
|
|
155
|
+
Apply semantic colors (`primary`, `neutral`, `success`, `warning`, `danger`) to individual tabs for color-coded navigation.
|
|
156
156
|
|
|
157
157
|
```tsx
|
|
158
158
|
<div style={{
|
|
@@ -195,7 +195,7 @@ Apply semantic colors to tabs.
|
|
|
195
195
|
|
|
196
196
|
### With Decorators
|
|
197
197
|
|
|
198
|
-
Add icons or other elements
|
|
198
|
+
Add icons or other elements before or after the tab label using `startDecorator` and `endDecorator` props.
|
|
199
199
|
|
|
200
200
|
```tsx
|
|
201
201
|
<Tabs {...args}>
|
|
@@ -218,7 +218,7 @@ Add icons or other elements to tab labels.
|
|
|
218
218
|
|
|
219
219
|
### Vertical Orientation
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
Set `orientation="vertical"` on the Tabs container for sidebar-style navigation layouts.
|
|
222
222
|
|
|
223
223
|
```tsx
|
|
224
224
|
<Tabs defaultValue={0} orientation="vertical" sx={{
|
|
@@ -237,7 +237,7 @@ Tabs can be arranged vertically for sidebar-style navigation.
|
|
|
237
237
|
|
|
238
238
|
### Disabled Tabs
|
|
239
239
|
|
|
240
|
-
Individual tabs can be disabled while keeping
|
|
240
|
+
Individual tabs can be disabled with the `disabled` prop while keeping other tabs interactive.
|
|
241
241
|
|
|
242
242
|
```tsx
|
|
243
243
|
<Tabs defaultValue={0}>
|
|
@@ -254,7 +254,7 @@ Individual tabs can be disabled while keeping others active.
|
|
|
254
254
|
|
|
255
255
|
### Controlled Tabs
|
|
256
256
|
|
|
257
|
-
|
|
257
|
+
Use the `value` and `onChange` props for programmatic control of the active tab.
|
|
258
258
|
|
|
259
259
|
```tsx
|
|
260
260
|
<div>
|
|
@@ -274,26 +274,6 @@ Programmatically control the active tab.
|
|
|
274
274
|
</div>
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
-
## When to Use
|
|
278
|
-
|
|
279
|
-
### ✅ Good Use Cases
|
|
280
|
-
|
|
281
|
-
- **Related content sections**: When content can be logically grouped and users don't need to see all sections at once
|
|
282
|
-
- **Settings pages**: Organizing different categories of settings (Profile, Security, Notifications)
|
|
283
|
-
- **Dashboard views**: Switching between different data views or time periods
|
|
284
|
-
- **Product details**: Showing Description, Specifications, Reviews as separate tabs
|
|
285
|
-
- **Form sections**: Breaking long forms into logical steps (not for strict wizard flows)
|
|
286
|
-
- **Code examples**: Showing code in different languages or frameworks
|
|
287
|
-
|
|
288
|
-
### ❌ When Not to Use
|
|
289
|
-
|
|
290
|
-
- **Primary navigation**: Use a navigation menu or sidebar for main app navigation
|
|
291
|
-
- **Sequential workflows**: Use Stepper for multi-step processes that must be completed in order
|
|
292
|
-
- **Comparing content**: If users need to see multiple sections simultaneously, use accordions or show all content
|
|
293
|
-
- **Very few items**: If there are only 2 options, consider using a toggle or radio buttons
|
|
294
|
-
- **Many items**: More than 5-6 tabs become hard to navigate; consider a dropdown or nested navigation
|
|
295
|
-
- **Mobile layouts**: Consider alternative patterns like accordions or stacked sections on small screens
|
|
296
|
-
|
|
297
277
|
## Common Use Cases
|
|
298
278
|
|
|
299
279
|
### Settings Page
|
|
@@ -306,331 +286,92 @@ function SettingsPage() {
|
|
|
306
286
|
<Tab startDecorator={<PersonIcon />}>Profile</Tab>
|
|
307
287
|
<Tab startDecorator={<SecurityIcon />}>Security</Tab>
|
|
308
288
|
<Tab startDecorator={<NotificationsIcon />}>Notifications</Tab>
|
|
309
|
-
<Tab startDecorator={<PaletteIcon />}>Appearance</Tab>
|
|
310
|
-
</TabList>
|
|
311
|
-
<TabPanel value={0}>
|
|
312
|
-
<ProfileSettings />
|
|
313
|
-
</TabPanel>
|
|
314
|
-
<TabPanel value={1}>
|
|
315
|
-
<SecuritySettings />
|
|
316
|
-
</TabPanel>
|
|
317
|
-
<TabPanel value={2}>
|
|
318
|
-
<NotificationSettings />
|
|
319
|
-
</TabPanel>
|
|
320
|
-
<TabPanel value={3}>
|
|
321
|
-
<AppearanceSettings />
|
|
322
|
-
</TabPanel>
|
|
323
|
-
</Tabs>
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
### Product Detail Page
|
|
329
|
-
|
|
330
|
-
```tsx
|
|
331
|
-
function ProductDetails({ product }) {
|
|
332
|
-
return (
|
|
333
|
-
<Tabs defaultValue={0}>
|
|
334
|
-
<TabList>
|
|
335
|
-
<Tab>Description</Tab>
|
|
336
|
-
<Tab>Specifications</Tab>
|
|
337
|
-
<Tab>Reviews ({product.reviewCount})</Tab>
|
|
338
|
-
<Tab>Q&A</Tab>
|
|
339
289
|
</TabList>
|
|
340
|
-
<TabPanel value={0}>
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
<TabPanel value={1}>
|
|
344
|
-
<SpecificationTable specs={product.specifications} />
|
|
345
|
-
</TabPanel>
|
|
346
|
-
<TabPanel value={2}>
|
|
347
|
-
<ReviewList reviews={product.reviews} />
|
|
348
|
-
</TabPanel>
|
|
349
|
-
<TabPanel value={3}>
|
|
350
|
-
<QASection productId={product.id} />
|
|
351
|
-
</TabPanel>
|
|
290
|
+
<TabPanel value={0}><ProfileSettings /></TabPanel>
|
|
291
|
+
<TabPanel value={1}><SecuritySettings /></TabPanel>
|
|
292
|
+
<TabPanel value={2}><NotificationSettings /></TabPanel>
|
|
352
293
|
</Tabs>
|
|
353
294
|
);
|
|
354
295
|
}
|
|
355
296
|
```
|
|
356
297
|
|
|
357
|
-
### Dashboard
|
|
298
|
+
### Dashboard Time Range
|
|
358
299
|
|
|
359
300
|
```tsx
|
|
360
301
|
function DashboardTabs() {
|
|
361
|
-
const [
|
|
302
|
+
const [range, setRange] = useState('week');
|
|
362
303
|
|
|
363
304
|
return (
|
|
364
|
-
<Tabs value={
|
|
305
|
+
<Tabs value={range} onChange={(e, val) => setRange(val as string)}>
|
|
365
306
|
<TabList>
|
|
366
307
|
<Tab value="day">Today</Tab>
|
|
367
308
|
<Tab value="week">This Week</Tab>
|
|
368
309
|
<Tab value="month">This Month</Tab>
|
|
369
|
-
<Tab value="year">This Year</Tab>
|
|
370
310
|
</TabList>
|
|
371
|
-
<TabPanel value="day">
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
<TabPanel value="week">
|
|
375
|
-
<WeeklyStats />
|
|
376
|
-
</TabPanel>
|
|
377
|
-
<TabPanel value="month">
|
|
378
|
-
<MonthlyStats />
|
|
379
|
-
</TabPanel>
|
|
380
|
-
<TabPanel value="year">
|
|
381
|
-
<YearlyStats />
|
|
382
|
-
</TabPanel>
|
|
311
|
+
<TabPanel value="day"><DailyStats /></TabPanel>
|
|
312
|
+
<TabPanel value="week"><WeeklyStats /></TabPanel>
|
|
313
|
+
<TabPanel value="month"><MonthlyStats /></TabPanel>
|
|
383
314
|
</Tabs>
|
|
384
315
|
);
|
|
385
316
|
}
|
|
386
317
|
```
|
|
387
318
|
|
|
388
|
-
###
|
|
319
|
+
### Vertical Sidebar
|
|
389
320
|
|
|
390
321
|
```tsx
|
|
391
|
-
function
|
|
322
|
+
function SidebarTabs() {
|
|
392
323
|
return (
|
|
393
|
-
<Tabs
|
|
324
|
+
<Tabs orientation="vertical" defaultValue={0} sx={{ minWidth: 300 }}>
|
|
394
325
|
<TabList>
|
|
395
|
-
<Tab
|
|
396
|
-
<Tab
|
|
397
|
-
<Tab
|
|
326
|
+
<Tab startDecorator={<DashboardIcon />}>Dashboard</Tab>
|
|
327
|
+
<Tab startDecorator={<AnalyticsIcon />}>Analytics</Tab>
|
|
328
|
+
<Tab startDecorator={<SettingsIcon />}>Settings</Tab>
|
|
398
329
|
</TabList>
|
|
399
|
-
<TabPanel value=
|
|
400
|
-
|
|
401
|
-
</TabPanel>
|
|
402
|
-
<TabPanel value="vue">
|
|
403
|
-
<CodeBlock language="vue">{vueCode}</CodeBlock>
|
|
404
|
-
</TabPanel>
|
|
405
|
-
<TabPanel value="angular">
|
|
406
|
-
<CodeBlock language="typescript">{angularCode}</CodeBlock>
|
|
407
|
-
</TabPanel>
|
|
330
|
+
<TabPanel value={0}>Dashboard content</TabPanel>
|
|
331
|
+
<TabPanel value={1}>Analytics content</TabPanel>
|
|
332
|
+
<TabPanel value={2}>Settings content</TabPanel>
|
|
408
333
|
</Tabs>
|
|
409
334
|
);
|
|
410
335
|
}
|
|
411
336
|
```
|
|
412
337
|
|
|
413
|
-
### Vertical Sidebar Navigation
|
|
414
|
-
|
|
415
|
-
```tsx
|
|
416
|
-
function SidebarTabs() {
|
|
417
|
-
return (
|
|
418
|
-
<Box sx={{ display: 'flex' }}>
|
|
419
|
-
<Tabs orientation="vertical" defaultValue={0} sx={{ minWidth: 200 }}>
|
|
420
|
-
<TabList>
|
|
421
|
-
<Tab startDecorator={<DashboardIcon />}>Dashboard</Tab>
|
|
422
|
-
<Tab startDecorator={<AnalyticsIcon />}>Analytics</Tab>
|
|
423
|
-
<Tab startDecorator={<ReportsIcon />}>Reports</Tab>
|
|
424
|
-
<Tab startDecorator={<SettingsIcon />}>Settings</Tab>
|
|
425
|
-
</TabList>
|
|
426
|
-
</Tabs>
|
|
427
|
-
<Box sx={{ flex: 1, p: 2 }}>
|
|
428
|
-
<TabPanel value={0}>Dashboard content</TabPanel>
|
|
429
|
-
<TabPanel value={1}>Analytics content</TabPanel>
|
|
430
|
-
<TabPanel value={2}>Reports content</TabPanel>
|
|
431
|
-
<TabPanel value={3}>Settings content</TabPanel>
|
|
432
|
-
</Box>
|
|
433
|
-
</Box>
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
## Component Structure
|
|
439
|
-
|
|
440
|
-
Tabs uses a composition pattern with multiple sub-components:
|
|
441
|
-
|
|
442
|
-
```tsx
|
|
443
|
-
<Tabs> {/* Container - manages state */}
|
|
444
|
-
<TabList> {/* Tab button container */}
|
|
445
|
-
<Tab>Tab 1</Tab> {/* Individual tab buttons */}
|
|
446
|
-
<Tab>Tab 2</Tab>
|
|
447
|
-
</TabList>
|
|
448
|
-
<TabPanel value={0}> {/* Content panels */}
|
|
449
|
-
Content 1
|
|
450
|
-
</TabPanel>
|
|
451
|
-
<TabPanel value={1}>
|
|
452
|
-
Content 2
|
|
453
|
-
</TabPanel>
|
|
454
|
-
</Tabs>
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
## Props and Customization
|
|
458
|
-
|
|
459
|
-
### Tabs Props
|
|
460
|
-
|
|
461
|
-
| Prop | Type | Default | Description |
|
|
462
|
-
| -------------- | ---------------------------- | -------------- | ----------------------------------- |
|
|
463
|
-
| `defaultValue` | `string \| number` | - | Default selected tab (uncontrolled) |
|
|
464
|
-
| `value` | `string \| number` | - | Selected tab (controlled) |
|
|
465
|
-
| `onChange` | `function` | - | Callback when tab changes |
|
|
466
|
-
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Tab layout direction |
|
|
467
|
-
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of all child components |
|
|
468
|
-
|
|
469
|
-
### Tab Props
|
|
470
|
-
|
|
471
|
-
| Prop | Type | Default | Description |
|
|
472
|
-
| ---------------- | -------------------------------------------------------------- | --------- | ---------------------------------- |
|
|
473
|
-
| `value` | `string \| number` | - | Tab identifier (defaults to index) |
|
|
474
|
-
| `variant` | `'plain' \| 'outlined' \| 'soft' \| 'solid'` | `'plain'` | Visual style |
|
|
475
|
-
| `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | - | Color scheme |
|
|
476
|
-
| `disabled` | `boolean` | `false` | Disable the tab |
|
|
477
|
-
| `startDecorator` | `ReactNode` | - | Element before tab label |
|
|
478
|
-
| `endDecorator` | `ReactNode` | - | Element after tab label |
|
|
479
|
-
|
|
480
|
-
### TabPanel Props
|
|
481
|
-
|
|
482
|
-
| Prop | Type | Default | Description |
|
|
483
|
-
| ------------- | ------------------ | ------- | ----------------------------- |
|
|
484
|
-
| `value` | `string \| number` | - | Matching tab value |
|
|
485
|
-
| `keepMounted` | `boolean` | `false` | Keep panel in DOM when hidden |
|
|
486
|
-
|
|
487
|
-
### Custom Styling
|
|
488
|
-
|
|
489
|
-
```tsx
|
|
490
|
-
<Tabs
|
|
491
|
-
sx={{
|
|
492
|
-
'--Tabs-gap': '8px',
|
|
493
|
-
}}
|
|
494
|
-
>
|
|
495
|
-
<TabList
|
|
496
|
-
sx={{
|
|
497
|
-
borderBottom: '1px solid',
|
|
498
|
-
borderColor: 'divider',
|
|
499
|
-
}}
|
|
500
|
-
>
|
|
501
|
-
<Tab
|
|
502
|
-
sx={{
|
|
503
|
-
'&.Mui-selected': {
|
|
504
|
-
borderBottom: '2px solid',
|
|
505
|
-
borderColor: 'primary.500',
|
|
506
|
-
},
|
|
507
|
-
}}
|
|
508
|
-
>
|
|
509
|
-
Custom Tab
|
|
510
|
-
</Tab>
|
|
511
|
-
</TabList>
|
|
512
|
-
</Tabs>
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
## Accessibility
|
|
516
|
-
|
|
517
|
-
Tabs components follow WAI-ARIA Tabs pattern for comprehensive accessibility:
|
|
518
|
-
|
|
519
|
-
### ARIA Attributes
|
|
520
|
-
|
|
521
|
-
- `role="tablist"` on TabList
|
|
522
|
-
- `role="tab"` on each Tab
|
|
523
|
-
- `role="tabpanel"` on each TabPanel
|
|
524
|
-
- `aria-selected` indicates the active tab
|
|
525
|
-
- `aria-controls` links tabs to their panels
|
|
526
|
-
- `aria-labelledby` links panels to their tabs
|
|
527
|
-
|
|
528
|
-
### Keyboard Navigation
|
|
529
|
-
|
|
530
|
-
- **Tab**: Move focus to the tab list, then to the active panel
|
|
531
|
-
- **Arrow Left/Right**: Move between tabs (horizontal orientation)
|
|
532
|
-
- **Arrow Up/Down**: Move between tabs (vertical orientation)
|
|
533
|
-
- **Home**: Move to first tab
|
|
534
|
-
- **End**: Move to last tab
|
|
535
|
-
- **Enter/Space**: Activate the focused tab
|
|
536
|
-
|
|
537
|
-
### Screen Reader Support
|
|
538
|
-
|
|
539
|
-
```tsx
|
|
540
|
-
<Tabs aria-label="Account settings tabs">
|
|
541
|
-
<TabList>
|
|
542
|
-
<Tab>Profile</Tab>
|
|
543
|
-
<Tab>Security</Tab>
|
|
544
|
-
</TabList>
|
|
545
|
-
<TabPanel value={0}>
|
|
546
|
-
<h2 id="profile-heading">Profile Settings</h2>
|
|
547
|
-
{/* Content */}
|
|
548
|
-
</TabPanel>
|
|
549
|
-
</Tabs>
|
|
550
|
-
```
|
|
551
|
-
|
|
552
338
|
## Best Practices
|
|
553
339
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
1. **Use clear, concise labels**: Tab labels should clearly describe the content
|
|
557
|
-
|
|
558
|
-
```tsx
|
|
559
|
-
// ✅ Good: Clear labels
|
|
560
|
-
<Tab>Account Settings</Tab>
|
|
561
|
-
<Tab>Notifications</Tab>
|
|
562
|
-
|
|
563
|
-
// ❌ Bad: Vague labels
|
|
564
|
-
<Tab>Tab 1</Tab>
|
|
565
|
-
<Tab>Other</Tab>
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
2. **Keep tab count manageable**: Limit to 5-6 tabs maximum
|
|
340
|
+
- **Use clear, concise tab labels.** Each label should describe the content it reveals. Avoid generic names like "Tab 1" or "Other".
|
|
569
341
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
5. **Show active state clearly**: Users should always know which tab is selected
|
|
575
|
-
|
|
576
|
-
### ❌ Don't
|
|
577
|
-
|
|
578
|
-
1. **Don't hide critical information**: Important content shouldn't be buried in secondary tabs
|
|
579
|
-
|
|
580
|
-
2. **Don't use for sequential processes**: Use Stepper for ordered workflows
|
|
581
|
-
|
|
582
|
-
```tsx
|
|
583
|
-
// ❌ Bad: Sequential steps as tabs
|
|
584
|
-
<Tab>Step 1: Personal Info</Tab>
|
|
585
|
-
<Tab>Step 2: Payment</Tab>
|
|
586
|
-
<Tab>Step 3: Confirm</Tab>
|
|
587
|
-
|
|
588
|
-
// ✅ Good: Use Stepper instead
|
|
589
|
-
<Stepper activeStep={activeStep}>
|
|
590
|
-
<Step>Personal Info</Step>
|
|
591
|
-
<Step>Payment</Step>
|
|
592
|
-
<Step>Confirm</Step>
|
|
593
|
-
</Stepper>
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
3. **Don't nest tabs within tabs**: Creates confusing navigation
|
|
597
|
-
|
|
598
|
-
4. **Don't use inconsistent tab counts**: If tabs are dynamic, consider alternative UI patterns
|
|
599
|
-
|
|
600
|
-
## Performance Considerations
|
|
601
|
-
|
|
602
|
-
### Lazy Loading Tab Content
|
|
342
|
+
```tsx
|
|
343
|
+
{/* ✅ Good: Descriptive labels */}
|
|
344
|
+
<Tab>Account Settings</Tab>
|
|
345
|
+
<Tab>Notifications</Tab>
|
|
603
346
|
|
|
604
|
-
|
|
347
|
+
{/* ❌ Bad: Vague labels */}
|
|
348
|
+
<Tab>Tab 1</Tab>
|
|
349
|
+
<Tab>Other</Tab>
|
|
350
|
+
```
|
|
605
351
|
|
|
606
|
-
|
|
607
|
-
<TabPanel value={0}>
|
|
608
|
-
{/* Only rendered when this tab is active */}
|
|
609
|
-
<HeavyComponent />
|
|
610
|
-
</TabPanel>
|
|
611
|
-
```
|
|
352
|
+
- **Limit the number of tabs.** Aim for 3-6 tabs. More than 6 tabs become difficult to scan and navigate. If you need more sections, consider a sidebar navigation, nested navigation, or a different layout pattern.
|
|
612
353
|
|
|
613
|
-
|
|
354
|
+
- **Order tabs by importance.** Place the most frequently used or most important tab first. Users expect the default (leftmost or topmost) tab to contain the primary content.
|
|
614
355
|
|
|
615
|
-
|
|
356
|
+
- **Do not use Tabs for sequential workflows.** Tabs are for parallel content sections that can be accessed in any order. For ordered multi-step processes, use a Stepper component instead.
|
|
616
357
|
|
|
617
|
-
```tsx
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
<
|
|
621
|
-
</
|
|
622
|
-
```
|
|
358
|
+
```tsx
|
|
359
|
+
{/* ❌ Bad: Sequential steps as tabs */}
|
|
360
|
+
<Tab>Step 1: Info</Tab>
|
|
361
|
+
<Tab>Step 2: Payment</Tab>
|
|
362
|
+
<Tab>Step 3: Confirm</Tab>
|
|
623
363
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
364
|
+
{/* ✅ Good: Independent content sections */}
|
|
365
|
+
<Tab>Profile</Tab>
|
|
366
|
+
<Tab>Security</Tab>
|
|
367
|
+
<Tab>Billing</Tab>
|
|
368
|
+
```
|
|
627
369
|
|
|
628
|
-
|
|
629
|
-
const tabContent = useMemo(() => (
|
|
630
|
-
<ExpensiveComponent data={data} />
|
|
631
|
-
), [data]);
|
|
370
|
+
- **Keep styling consistent.** All tabs in a group should share the same variant and color. Mixing styles within the same TabList creates visual inconsistency.
|
|
632
371
|
|
|
633
|
-
|
|
634
|
-
```
|
|
372
|
+
## Accessibility
|
|
635
373
|
|
|
636
|
-
|
|
374
|
+
- **WAI-ARIA Tabs pattern**: TabList receives `role="tablist"`, each Tab receives `role="tab"`, and each TabPanel receives `role="tabpanel"`. The `aria-selected`, `aria-controls`, and `aria-labelledby` attributes are set automatically.
|
|
375
|
+
- **Keyboard navigation**: Arrow Left/Right moves between tabs in horizontal orientation. Arrow Up/Down moves between tabs in vertical orientation. Home jumps to the first tab, End to the last. Enter or Space activates the focused tab.
|
|
376
|
+
- **Tab key behavior**: Pressing Tab from outside the tab list focuses the active tab. Pressing Tab again moves focus into the active panel content, not to the next tab button.
|
|
377
|
+
- **Provide an accessible label**: Use `aria-label` on the Tabs container to give screen readers context for the tab group (e.g., `aria-label="Account settings"`).
|