@ceed/cds 1.28.1 → 1.29.1

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 (60) hide show
  1. package/dist/Overview.md +5 -5
  2. package/dist/components/DatePicker/DatePicker.d.ts +10 -0
  3. package/dist/components/DateRangePicker/DateRangePicker.d.ts +10 -0
  4. package/dist/components/data-display/Avatar.md +110 -69
  5. package/dist/components/data-display/Badge.md +91 -39
  6. package/dist/components/data-display/Chip.md +49 -20
  7. package/dist/components/data-display/DataTable.md +93 -0
  8. package/dist/components/data-display/InfoSign.md +12 -0
  9. package/dist/components/data-display/Table.md +72 -55
  10. package/dist/components/data-display/Tooltip.md +40 -40
  11. package/dist/components/data-display/Typography.md +53 -70
  12. package/dist/components/feedback/Alert.md +88 -72
  13. package/dist/components/feedback/CircularProgress.md +17 -0
  14. package/dist/components/feedback/Skeleton.md +17 -0
  15. package/dist/components/inputs/Button.md +94 -68
  16. package/dist/components/inputs/ButtonGroup.md +17 -0
  17. package/dist/components/inputs/Calendar.md +118 -457
  18. package/dist/components/inputs/Checkbox.md +185 -430
  19. package/dist/components/inputs/CurrencyInput.md +22 -0
  20. package/dist/components/inputs/DatePicker.md +84 -0
  21. package/dist/components/inputs/DateRangePicker.md +88 -0
  22. package/dist/components/inputs/FilterableCheckboxGroup.md +20 -3
  23. package/dist/components/inputs/FormControl.md +32 -2
  24. package/dist/components/inputs/IconButton.md +18 -0
  25. package/dist/components/inputs/Input.md +198 -136
  26. package/dist/components/inputs/MonthPicker.md +25 -0
  27. package/dist/components/inputs/MonthRangePicker.md +23 -0
  28. package/dist/components/inputs/PercentageInput.md +25 -0
  29. package/dist/components/inputs/RadioButton.md +23 -0
  30. package/dist/components/inputs/RadioList.md +20 -1
  31. package/dist/components/inputs/RadioTileGroup.md +37 -3
  32. package/dist/components/inputs/Select.md +56 -0
  33. package/dist/components/inputs/Slider.md +23 -0
  34. package/dist/components/inputs/Switch.md +20 -0
  35. package/dist/components/inputs/Textarea.md +32 -8
  36. package/dist/components/inputs/Uploader/Uploader.md +21 -0
  37. package/dist/components/layout/Box.md +52 -41
  38. package/dist/components/layout/Grid.md +87 -81
  39. package/dist/components/layout/Stack.md +88 -68
  40. package/dist/components/navigation/Breadcrumbs.md +57 -46
  41. package/dist/components/navigation/Drawer.md +17 -0
  42. package/dist/components/navigation/Dropdown.md +13 -0
  43. package/dist/components/navigation/IconMenuButton.md +17 -0
  44. package/dist/components/navigation/InsetDrawer.md +130 -292
  45. package/dist/components/navigation/Link.md +78 -0
  46. package/dist/components/navigation/Menu.md +17 -0
  47. package/dist/components/navigation/MenuButton.md +18 -0
  48. package/dist/components/navigation/Pagination.md +13 -0
  49. package/dist/components/navigation/Stepper.md +15 -0
  50. package/dist/components/navigation/Tabs.md +27 -0
  51. package/dist/components/surfaces/Accordions.md +804 -49
  52. package/dist/components/surfaces/Card.md +173 -97
  53. package/dist/components/surfaces/Divider.md +246 -82
  54. package/dist/components/surfaces/Sheet.md +15 -0
  55. package/dist/index.browser.js +2 -2
  56. package/dist/index.browser.js.map +3 -3
  57. package/dist/index.cjs +173 -6
  58. package/dist/index.js +173 -6
  59. package/framer/index.js +1 -1
  60. package/package.json +1 -1
@@ -2,9 +2,7 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- Accordions is a component that displays a vertically stacked list of collapsible sections. Each section has a header that users can click to expand or collapse the associated content. This pattern efficiently organizes information through progressive disclosure, reducing visual clutter by allowing users to focus on one section at a time.
6
-
7
- Accordions are ideal for FAQ pages, settings panels, product detail sections, and any content that benefits from being grouped into expandable categories. The component supports multiple variants, colors, and sizes for flexible visual customization.
5
+ Accordions is a component that displays a vertically stacked list of collapsible sections, each with a header that users can click to expand or collapse the content below. It efficiently organizes content by allowing users to focus on one section at a time while keeping other sections collapsed, reducing visual clutter and improving content navigation. Accordions are commonly used for FAQs, settings panels, navigation menus, and any content that benefits from progressive disclosure.
8
6
 
9
7
  ```tsx
10
8
  <Accordions {...args} />
@@ -14,6 +12,15 @@ Accordions are ideal for FAQ pages, settings panels, product detail sections, an
14
12
  | ----- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
15
13
  | items | Array of accordion summaries and details | \[\{ summary: 'First header', details: \<h3>Content of the first accordion.\</h3> }, \{ summary: 'Second header', details: \<h3>Content of the second accordion.\</h3> }, \{ summary: 'Third header', details: \<h3>Content of the third accordion.\</h3> }] |
16
14
 
15
+ > ⚠️ **Usage Warning** ⚠️
16
+ >
17
+ > Consider these guidelines when using Accordions:
18
+ >
19
+ > - **Don't hide critical content**: Important information should be visible by default
20
+ > - **Limit nesting**: Avoid nesting accordions within accordions
21
+ > - **Keep headers concise**: Users should understand content without expanding
22
+ > - **Consider Tabs**: Use Tabs for content that users need to compare side-by-side
23
+
17
24
  ## Usage
18
25
 
19
26
  ```tsx
@@ -41,9 +48,19 @@ function FAQSection() {
41
48
  }
42
49
  ```
43
50
 
44
- ## Basics
51
+ ## Examples
52
+
53
+ ### Playground
54
+
55
+ Interactive example with all controls.
56
+
57
+ ```tsx
58
+ <Accordions {...args} />
59
+ ```
60
+
61
+ ### Basics
45
62
 
46
- A basic accordion with multiple collapsible sections.
63
+ Basic accordion with multiple collapsible sections.
47
64
 
48
65
  ```tsx
49
66
  <Accordions
@@ -60,9 +77,9 @@ A basic accordion with multiple collapsible sections.
60
77
  />
61
78
  ```
62
79
 
63
- ## Sizes
80
+ ### Sizes
64
81
 
65
- Available in three sizes: `sm`, `md`, and `lg`. Use smaller sizes for sidebars or navigation menus, and larger sizes for main content areas.
82
+ Available size options: `sm`, `md`, `lg`.
66
83
 
67
84
  ```tsx
68
85
  <>
@@ -72,9 +89,9 @@ Available in three sizes: `sm`, `md`, and `lg`. Use smaller sizes for sidebars o
72
89
  </>
73
90
  ```
74
91
 
75
- ## Variants
92
+ ### Variants
76
93
 
77
- Four style variants are available: `plain`, `outlined`, `soft`, and `solid`. Choose the variant that best matches the surrounding context.
94
+ Style variants: `plain`, `outlined`, `soft`, `solid`.
78
95
 
79
96
  ```tsx
80
97
  <>
@@ -97,9 +114,9 @@ Four style variants are available: `plain`, `outlined`, `soft`, and `solid`. Cho
97
114
  </>
98
115
  ```
99
116
 
100
- ## Colors
117
+ ### Colors
101
118
 
102
- Apply semantic colors to match the accordion's purpose: `primary`, `neutral`, `danger`, `success`, or `warning`.
119
+ Color options: `primary`, `neutral`, `danger`, `success`, `warning`.
103
120
 
104
121
  ```tsx
105
122
  <>
@@ -126,21 +143,40 @@ Apply semantic colors to match the accordion's purpose: `primary`, `neutral`, `d
126
143
  </>
127
144
  ```
128
145
 
129
- ## Removing Divider
146
+ ### Removing Divider
130
147
 
131
- Use the `disableDivider` prop to remove the divider lines between accordion items for a cleaner look.
148
+ Use `disableDivider` to remove the divider lines between accordion items.
132
149
 
133
150
  ```tsx
134
151
  <Accordions {...args} disableDivider />
135
152
  ```
136
153
 
154
+ ## When to Use
155
+
156
+ ### ✅ Good Use Cases
157
+
158
+ - **FAQs**: Frequently asked questions with expandable answers
159
+ - **Settings panels**: Grouped settings that can be expanded individually
160
+ - **Product details**: Specifications, reviews, shipping info sections
161
+ - **Documentation**: Collapsible sections for detailed explanations
162
+ - **Navigation menus**: Expandable category menus
163
+ - **Form sections**: Long forms grouped into logical sections
164
+ - **Help content**: Step-by-step instructions or troubleshooting guides
165
+
166
+ ### ❌ When Not to Use
167
+
168
+ - **Critical information**: Don't hide must-read content in accordions
169
+ - **Short content**: Use a list or cards if content is minimal
170
+ - **Comparison content**: Use Tabs when users need to compare sections
171
+ - **Primary navigation**: Use Navigator or Tabs for main navigation
172
+ - **Single item**: Don't use accordion for a single collapsible section
173
+ - **Deeply nested content**: Avoid nesting accordions within accordions
174
+
137
175
  ## Common Use Cases
138
176
 
139
177
  ### FAQ Section
140
178
 
141
179
  ```tsx
142
- import { Accordions, Typography, Stack, Box } from '@ceed/cds';
143
-
144
180
  function FAQAccordion() {
145
181
  const faqs = [
146
182
  {
@@ -148,7 +184,7 @@ function FAQAccordion() {
148
184
  details: (
149
185
  <Typography>
150
186
  We accept Visa, MasterCard, American Express, and PayPal.
151
- All payments are processed securely.
187
+ All payments are processed securely through our payment gateway.
152
188
  </Typography>
153
189
  ),
154
190
  },
@@ -156,8 +192,9 @@ function FAQAccordion() {
156
192
  summary: 'How can I track my order?',
157
193
  details: (
158
194
  <Typography>
159
- Once your order ships, you will receive an email with a tracking
160
- number that you can use on our website.
195
+ Once your order ships, you'll receive an email with a tracking
196
+ number. You can use this number on our website or the carrier's
197
+ site to track your package.
161
198
  </Typography>
162
199
  ),
163
200
  },
@@ -190,9 +227,7 @@ function FAQAccordion() {
190
227
  ### Settings Panel
191
228
 
192
229
  ```tsx
193
- import { Accordions, Stack, FormControl, FormLabel, Input, Checkbox } from '@ceed/cds';
194
-
195
- function SettingsPanel() {
230
+ function SettingsAccordion({ settings, onSettingChange }) {
196
231
  const settingsItems = [
197
232
  {
198
233
  summary: 'Account Settings',
@@ -200,11 +235,18 @@ function SettingsPanel() {
200
235
  <Stack gap={2}>
201
236
  <FormControl>
202
237
  <FormLabel>Display Name</FormLabel>
203
- <Input placeholder="Enter your name" />
238
+ <Input
239
+ value={settings.displayName}
240
+ onChange={(e) => onSettingChange('displayName', e.target.value)}
241
+ />
204
242
  </FormControl>
205
243
  <FormControl>
206
244
  <FormLabel>Email</FormLabel>
207
- <Input type="email" placeholder="your@email.com" />
245
+ <Input
246
+ type="email"
247
+ value={settings.email}
248
+ onChange={(e) => onSettingChange('email', e.target.value)}
249
+ />
208
250
  </FormControl>
209
251
  </Stack>
210
252
  ),
@@ -213,32 +255,179 @@ function SettingsPanel() {
213
255
  summary: 'Notification Preferences',
214
256
  details: (
215
257
  <Stack gap={1}>
216
- <Checkbox label="Email notifications" />
217
- <Checkbox label="Push notifications" />
218
- <Checkbox label="Weekly digest" />
258
+ <Checkbox
259
+ label="Email notifications"
260
+ checked={settings.emailNotifications}
261
+ onChange={(e) =>
262
+ onSettingChange('emailNotifications', e.target.checked)
263
+ }
264
+ />
265
+ <Checkbox
266
+ label="Push notifications"
267
+ checked={settings.pushNotifications}
268
+ onChange={(e) =>
269
+ onSettingChange('pushNotifications', e.target.checked)
270
+ }
271
+ />
272
+ <Checkbox
273
+ label="Weekly digest"
274
+ checked={settings.weeklyDigest}
275
+ onChange={(e) => onSettingChange('weeklyDigest', e.target.checked)}
276
+ />
277
+ </Stack>
278
+ ),
279
+ },
280
+ {
281
+ summary: 'Privacy Settings',
282
+ details: (
283
+ <Stack gap={1}>
284
+ <Checkbox
285
+ label="Make profile public"
286
+ checked={settings.publicProfile}
287
+ onChange={(e) => onSettingChange('publicProfile', e.target.checked)}
288
+ />
289
+ <Checkbox
290
+ label="Show activity status"
291
+ checked={settings.showActivity}
292
+ onChange={(e) => onSettingChange('showActivity', e.target.checked)}
293
+ />
219
294
  </Stack>
220
295
  ),
221
296
  },
222
297
  ];
223
298
 
224
- return <Accordions items={settingsItems} variant="soft" color="neutral" />;
299
+ return (
300
+ <Box sx={{ maxWidth: 500 }}>
301
+ <Typography level="h3" sx={{ mb: 2 }}>
302
+ Settings
303
+ </Typography>
304
+ <Accordions items={settingsItems} variant="soft" color="neutral" />
305
+ </Box>
306
+ );
225
307
  }
226
308
  ```
227
309
 
228
- ### Navigation Menu
310
+ ### Product Details
229
311
 
230
312
  ```tsx
231
- import { Accordions, List, ListItem, ListItemButton, Box } from '@ceed/cds';
313
+ function ProductAccordion({ product }) {
314
+ const productDetails = [
315
+ {
316
+ summary: 'Description',
317
+ details: (
318
+ <Typography>{product.description}</Typography>
319
+ ),
320
+ },
321
+ {
322
+ summary: 'Specifications',
323
+ details: (
324
+ <Table>
325
+ <tbody>
326
+ {Object.entries(product.specs).map(([key, value]) => (
327
+ <tr key={key}>
328
+ <td>
329
+ <Typography level="body-sm" fontWeight="md">
330
+ {key}
331
+ </Typography>
332
+ </td>
333
+ <td>
334
+ <Typography level="body-sm">{value}</Typography>
335
+ </td>
336
+ </tr>
337
+ ))}
338
+ </tbody>
339
+ </Table>
340
+ ),
341
+ },
342
+ {
343
+ summary: 'Shipping & Returns',
344
+ details: (
345
+ <Stack gap={2}>
346
+ <Box>
347
+ <Typography level="title-sm">Shipping</Typography>
348
+ <Typography level="body-sm">
349
+ Free shipping on orders over $50. Standard delivery 3-5 days.
350
+ </Typography>
351
+ </Box>
352
+ <Box>
353
+ <Typography level="title-sm">Returns</Typography>
354
+ <Typography level="body-sm">
355
+ 30-day return policy. Items must be unused with original tags.
356
+ </Typography>
357
+ </Box>
358
+ </Stack>
359
+ ),
360
+ },
361
+ {
362
+ summary: `Reviews (${product.reviewCount})`,
363
+ details: (
364
+ <Stack gap={2}>
365
+ {product.reviews.slice(0, 3).map((review) => (
366
+ <Card key={review.id} variant="outlined">
367
+ <CardContent>
368
+ <Stack direction="row" justifyContent="space-between">
369
+ <Typography level="title-sm">{review.author}</Typography>
370
+ <Typography level="body-xs" color="neutral">
371
+ {review.date}
372
+ </Typography>
373
+ </Stack>
374
+ <Typography level="body-sm">{review.content}</Typography>
375
+ </CardContent>
376
+ </Card>
377
+ ))}
378
+ <Button variant="plain" size="sm">
379
+ View all reviews
380
+ </Button>
381
+ </Stack>
382
+ ),
383
+ },
384
+ ];
385
+
386
+ return <Accordions items={productDetails} variant="plain" />;
387
+ }
388
+ ```
232
389
 
233
- function SidebarMenu() {
390
+ ### Navigation Menu
391
+
392
+ ```tsx
393
+ function NavigationAccordion() {
234
394
  const menuItems = [
235
395
  {
236
396
  summary: 'Products',
237
397
  details: (
238
398
  <List size="sm">
239
- <ListItem><ListItemButton>Electronics</ListItemButton></ListItem>
240
- <ListItem><ListItemButton>Clothing</ListItemButton></ListItem>
241
- <ListItem><ListItemButton>Home & Garden</ListItemButton></ListItem>
399
+ <ListItem>
400
+ <ListItemButton onClick={() => navigate('/products/electronics')}>
401
+ Electronics
402
+ </ListItemButton>
403
+ </ListItem>
404
+ <ListItem>
405
+ <ListItemButton onClick={() => navigate('/products/clothing')}>
406
+ Clothing
407
+ </ListItemButton>
408
+ </ListItem>
409
+ <ListItem>
410
+ <ListItemButton onClick={() => navigate('/products/home')}>
411
+ Home & Garden
412
+ </ListItemButton>
413
+ </ListItem>
414
+ </List>
415
+ ),
416
+ },
417
+ {
418
+ summary: 'Services',
419
+ details: (
420
+ <List size="sm">
421
+ <ListItem>
422
+ <ListItemButton onClick={() => navigate('/services/consulting')}>
423
+ Consulting
424
+ </ListItemButton>
425
+ </ListItem>
426
+ <ListItem>
427
+ <ListItemButton onClick={() => navigate('/services/support')}>
428
+ Support
429
+ </ListItemButton>
430
+ </ListItem>
242
431
  </List>
243
432
  ),
244
433
  },
@@ -246,8 +435,21 @@ function SidebarMenu() {
246
435
  summary: 'Resources',
247
436
  details: (
248
437
  <List size="sm">
249
- <ListItem><ListItemButton>Documentation</ListItemButton></ListItem>
250
- <ListItem><ListItemButton>Blog</ListItemButton></ListItem>
438
+ <ListItem>
439
+ <ListItemButton onClick={() => navigate('/docs')}>
440
+ Documentation
441
+ </ListItemButton>
442
+ </ListItem>
443
+ <ListItem>
444
+ <ListItemButton onClick={() => navigate('/blog')}>
445
+ Blog
446
+ </ListItemButton>
447
+ </ListItem>
448
+ <ListItem>
449
+ <ListItemButton onClick={() => navigate('/tutorials')}>
450
+ Tutorials
451
+ </ListItemButton>
452
+ </ListItem>
251
453
  </List>
252
454
  ),
253
455
  },
@@ -261,25 +463,578 @@ function SidebarMenu() {
261
463
  }
262
464
  ```
263
465
 
264
- ## Best Practices
466
+ ### Help Documentation
265
467
 
266
- - **Use descriptive headers.** Users should understand what content a section contains without needing to expand it.
267
- - `"Shipping Options & Estimated Costs"`
268
- - `"Section 3"`
468
+ ```tsx
469
+ function HelpAccordion() {
470
+ const helpTopics = [
471
+ {
472
+ summary: 'Getting Started',
473
+ details: (
474
+ <Stack gap={2}>
475
+ <Typography level="title-sm">Step 1: Create an Account</Typography>
476
+ <Typography level="body-sm">
477
+ Click the "Sign Up" button and fill in your details.
478
+ </Typography>
479
+ <Typography level="title-sm">Step 2: Verify Email</Typography>
480
+ <Typography level="body-sm">
481
+ Check your inbox for a verification email and click the link.
482
+ </Typography>
483
+ <Typography level="title-sm">Step 3: Complete Profile</Typography>
484
+ <Typography level="body-sm">
485
+ Add your profile picture and bio to personalize your account.
486
+ </Typography>
487
+ </Stack>
488
+ ),
489
+ },
490
+ {
491
+ summary: 'Troubleshooting',
492
+ details: (
493
+ <Stack gap={2}>
494
+ <Box>
495
+ <Typography level="title-sm" color="danger">
496
+ Can't log in?
497
+ </Typography>
498
+ <Typography level="body-sm">
499
+ Try resetting your password or clearing your browser cache.
500
+ </Typography>
501
+ </Box>
502
+ <Box>
503
+ <Typography level="title-sm" color="danger">
504
+ Page not loading?
505
+ </Typography>
506
+ <Typography level="body-sm">
507
+ Check your internet connection and try refreshing the page.
508
+ </Typography>
509
+ </Box>
510
+ </Stack>
511
+ ),
512
+ },
513
+ {
514
+ summary: 'Contact Support',
515
+ details: (
516
+ <Stack gap={2}>
517
+ <Typography level="body-sm">
518
+ Need more help? Reach out to our support team:
519
+ </Typography>
520
+ <Stack gap={1}>
521
+ <Typography level="body-sm">
522
+ 📧 Email: support@example.com
523
+ </Typography>
524
+ <Typography level="body-sm">
525
+ 💬 Live Chat: Available 9am-5pm EST
526
+ </Typography>
527
+ <Typography level="body-sm">
528
+ 📞 Phone: 1-800-EXAMPLE
529
+ </Typography>
530
+ </Stack>
531
+ </Stack>
532
+ ),
533
+ },
534
+ ];
535
+
536
+ return (
537
+ <Box sx={{ maxWidth: 600 }}>
538
+ <Typography level="h2" sx={{ mb: 2 }}>
539
+ Help Center
540
+ </Typography>
541
+ <Accordions items={helpTopics} variant="outlined" color="primary" />
542
+ </Box>
543
+ );
544
+ }
545
+ ```
269
546
 
270
- - **Keep the number of items manageable.** Aim for 3 to 7 items per accordion group. If you have more, consider grouping them into categories or adding a search filter.
547
+ ### Collapsible Form Sections
271
548
 
272
- - **Do not hide critical information.** Important warnings, legal notices, or mandatory instructions should be displayed prominently rather than tucked inside an accordion.
273
- - Show safety warnings with an Alert component
274
- - Hide safety warnings inside an accordion
549
+ ```tsx
550
+ function MultiSectionForm({ onSubmit }) {
551
+ const [formData, setFormData] = useState({
552
+ personal: { firstName: '', lastName: '', email: '' },
553
+ address: { street: '', city: '', zipCode: '' },
554
+ payment: { cardNumber: '', expiry: '', cvv: '' },
555
+ });
275
556
 
276
- - **Avoid nesting accordions within accordions.** Deeply nested structures are confusing and difficult to navigate. Flatten the hierarchy or use a different pattern such as tabs.
557
+ const updateField = (section, field, value) => {
558
+ setFormData((prev) => ({
559
+ ...prev,
560
+ [section]: { ...prev[section], [field]: value },
561
+ }));
562
+ };
277
563
 
278
- - **Match the variant to the context.** Use `outlined` for form sections, `plain` for navigation menus, and `soft` for informational panels.
564
+ const formSections = [
565
+ {
566
+ summary: 'Personal Information',
567
+ details: (
568
+ <Stack gap={2}>
569
+ <Stack direction="row" gap={2}>
570
+ <FormControl sx={{ flex: 1 }}>
571
+ <FormLabel>First Name</FormLabel>
572
+ <Input
573
+ value={formData.personal.firstName}
574
+ onChange={(e) =>
575
+ updateField('personal', 'firstName', e.target.value)
576
+ }
577
+ />
578
+ </FormControl>
579
+ <FormControl sx={{ flex: 1 }}>
580
+ <FormLabel>Last Name</FormLabel>
581
+ <Input
582
+ value={formData.personal.lastName}
583
+ onChange={(e) =>
584
+ updateField('personal', 'lastName', e.target.value)
585
+ }
586
+ />
587
+ </FormControl>
588
+ </Stack>
589
+ <FormControl>
590
+ <FormLabel>Email</FormLabel>
591
+ <Input
592
+ type="email"
593
+ value={formData.personal.email}
594
+ onChange={(e) =>
595
+ updateField('personal', 'email', e.target.value)
596
+ }
597
+ />
598
+ </FormControl>
599
+ </Stack>
600
+ ),
601
+ },
602
+ {
603
+ summary: 'Shipping Address',
604
+ details: (
605
+ <Stack gap={2}>
606
+ <FormControl>
607
+ <FormLabel>Street Address</FormLabel>
608
+ <Input
609
+ value={formData.address.street}
610
+ onChange={(e) =>
611
+ updateField('address', 'street', e.target.value)
612
+ }
613
+ />
614
+ </FormControl>
615
+ <Stack direction="row" gap={2}>
616
+ <FormControl sx={{ flex: 2 }}>
617
+ <FormLabel>City</FormLabel>
618
+ <Input
619
+ value={formData.address.city}
620
+ onChange={(e) =>
621
+ updateField('address', 'city', e.target.value)
622
+ }
623
+ />
624
+ </FormControl>
625
+ <FormControl sx={{ flex: 1 }}>
626
+ <FormLabel>ZIP Code</FormLabel>
627
+ <Input
628
+ value={formData.address.zipCode}
629
+ onChange={(e) =>
630
+ updateField('address', 'zipCode', e.target.value)
631
+ }
632
+ />
633
+ </FormControl>
634
+ </Stack>
635
+ </Stack>
636
+ ),
637
+ },
638
+ {
639
+ summary: 'Payment Details',
640
+ details: (
641
+ <Stack gap={2}>
642
+ <FormControl>
643
+ <FormLabel>Card Number</FormLabel>
644
+ <Input
645
+ placeholder="1234 5678 9012 3456"
646
+ value={formData.payment.cardNumber}
647
+ onChange={(e) =>
648
+ updateField('payment', 'cardNumber', e.target.value)
649
+ }
650
+ />
651
+ </FormControl>
652
+ <Stack direction="row" gap={2}>
653
+ <FormControl sx={{ flex: 1 }}>
654
+ <FormLabel>Expiry Date</FormLabel>
655
+ <Input
656
+ placeholder="MM/YY"
657
+ value={formData.payment.expiry}
658
+ onChange={(e) =>
659
+ updateField('payment', 'expiry', e.target.value)
660
+ }
661
+ />
662
+ </FormControl>
663
+ <FormControl sx={{ flex: 1 }}>
664
+ <FormLabel>CVV</FormLabel>
665
+ <Input
666
+ type="password"
667
+ placeholder="123"
668
+ value={formData.payment.cvv}
669
+ onChange={(e) =>
670
+ updateField('payment', 'cvv', e.target.value)
671
+ }
672
+ />
673
+ </FormControl>
674
+ </Stack>
675
+ </Stack>
676
+ ),
677
+ },
678
+ ];
679
+
680
+ return (
681
+ <Box sx={{ maxWidth: 500 }}>
682
+ <Accordions items={formSections} variant="outlined" />
683
+ <Button sx={{ mt: 2 }} fullWidth onClick={() => onSubmit(formData)}>
684
+ Submit Order
685
+ </Button>
686
+ </Box>
687
+ );
688
+ }
689
+ ```
690
+
691
+ ## Props and Customization
692
+
693
+ ### Key Props
694
+
695
+ | Prop | Type | Default | Description |
696
+ | ---------------- | -------------------------------------------------------------- | ----------- | ------------------------------------------------- |
697
+ | `items` | `Array<{ summary: string; details: ReactNode }>` | - | Array of accordion items with headers and content |
698
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the accordion |
699
+ | `variant` | `'plain' \| 'outlined' \| 'soft' \| 'solid'` | `'plain'` | Visual style variant |
700
+ | `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'neutral'` | Color theme |
701
+ | `disableDivider` | `boolean` | `false` | Remove divider lines between items |
702
+
703
+ ### Items Structure
704
+
705
+ ```tsx
706
+ interface AccordionItem {
707
+ summary: string; // Header text displayed when collapsed
708
+ details: ReactNode; // Content shown when expanded
709
+ }
710
+
711
+ // Example items array
712
+ const items = [
713
+ {
714
+ summary: 'Section Title',
715
+ details: <Typography>Section content...</Typography>,
716
+ },
717
+ {
718
+ summary: 'Another Section',
719
+ details: (
720
+ <Stack gap={2}>
721
+ <Typography>Multiple elements...</Typography>
722
+ <Button>Action</Button>
723
+ </Stack>
724
+ ),
725
+ },
726
+ ];
727
+ ```
728
+
729
+ ### Variant Options
730
+
731
+ ```tsx
732
+ // Plain - minimal styling, no background
733
+ <Accordions items={items} variant="plain" />
734
+
735
+ // Outlined - bordered container
736
+ <Accordions items={items} variant="outlined" />
737
+
738
+ // Soft - subtle background color
739
+ <Accordions items={items} variant="soft" />
740
+
741
+ // Solid - full background color
742
+ <Accordions items={items} variant="solid" />
743
+ ```
744
+
745
+ ### Size Options
746
+
747
+ ```tsx
748
+ // Small - compact for sidebars/menus
749
+ <Accordions items={items} size="sm" />
750
+
751
+ // Medium - default size
752
+ <Accordions items={items} size="md" />
753
+
754
+ // Large - spacious for main content
755
+ <Accordions items={items} size="lg" />
756
+ ```
757
+
758
+ ### Color Options
759
+
760
+ ```tsx
761
+ // Primary - brand color emphasis
762
+ <Accordions items={items} color="primary" />
763
+
764
+ // Neutral - subtle, default
765
+ <Accordions items={items} color="neutral" />
766
+
767
+ // Status colors
768
+ <Accordions items={items} color="success" />
769
+ <Accordions items={items} color="warning" />
770
+ <Accordions items={items} color="danger" />
771
+ ```
772
+
773
+ ### Without Dividers
774
+
775
+ ```tsx
776
+ // Clean look without divider lines
777
+ <Accordions items={items} disableDivider />
778
+ ```
279
779
 
280
780
  ## Accessibility
281
781
 
282
- - Accordion headers use `role="button"` with `aria-expanded` to indicate their current state to screen readers.
283
- - Content regions use `role="region"` with `aria-labelledby` to associate them with their headers.
284
- - Keyboard navigation is fully supported: **Tab** moves focus between headers, **Enter/Space** toggles expand/collapse, **Arrow Up/Down** moves between headers, and **Home/End** jumps to the first or last header.
285
- - Focus indicators are clearly visible on headers, and focus remains on the header after toggling to avoid disorienting the user.
782
+ Accordions includes built-in accessibility features:
783
+
784
+ ### ARIA Attributes
785
+
786
+ - Accordion headers have `role="button"` with `aria-expanded`
787
+ - Content regions have `role="region"` with `aria-labelledby`
788
+ - Proper focus management between sections
789
+
790
+ ### Keyboard Navigation
791
+
792
+ - **Tab**: Move focus between accordion headers
793
+ - **Enter/Space**: Expand or collapse focused accordion
794
+ - **Arrow Down**: Move focus to next accordion header
795
+ - **Arrow Up**: Move focus to previous accordion header
796
+ - **Home**: Move focus to first accordion header
797
+ - **End**: Move focus to last accordion header
798
+
799
+ ### Screen Reader Support
800
+
801
+ ```tsx
802
+ // Headers announce: "Section Title, collapsed/expanded, button"
803
+ <Accordions
804
+ items={[
805
+ {
806
+ summary: 'Shipping Information', // Announced as header
807
+ details: <Typography>Content...</Typography>,
808
+ },
809
+ ]}
810
+ />
811
+ ```
812
+
813
+ ### Focus Visibility
814
+
815
+ - Clear focus indicators on headers
816
+ - Focus remains on header after expand/collapse
817
+ - Tab navigation works within expanded content
818
+
819
+ ## Best Practices
820
+
821
+ ### ✅ Do
822
+
823
+ 1. **Use descriptive headers**: Make content clear without expanding
824
+
825
+ ```tsx
826
+ // ✅ Good: Descriptive headers
827
+ <Accordions
828
+ items={[
829
+ { summary: 'Return Policy (30 days)', details: '...' },
830
+ { summary: 'Shipping Options & Costs', details: '...' },
831
+ ]}
832
+ />
833
+ ```
834
+
835
+ 2. **Group related content**: Keep sections logically organized
836
+
837
+ ```tsx
838
+ // ✅ Good: Logical grouping
839
+ <Accordions
840
+ items={[
841
+ { summary: 'Account Settings', details: accountSettings },
842
+ { summary: 'Notification Settings', details: notificationSettings },
843
+ { summary: 'Privacy Settings', details: privacySettings },
844
+ ]}
845
+ />
846
+ ```
847
+
848
+ 3. **Keep content scannable**: Use lists and headings inside accordions
849
+
850
+ ```tsx
851
+ // ✅ Good: Scannable content
852
+ <Accordions
853
+ items={[
854
+ {
855
+ summary: 'Features',
856
+ details: (
857
+ <ul>
858
+ <li>Feature one</li>
859
+ <li>Feature two</li>
860
+ <li>Feature three</li>
861
+ </ul>
862
+ ),
863
+ },
864
+ ]}
865
+ />
866
+ ```
867
+
868
+ 4. **Match variant to context**: Use appropriate styling
869
+
870
+ ```tsx
871
+ // ✅ Good: Outlined for forms, plain for navigation
872
+ <Accordions items={formSections} variant="outlined" />
873
+ <Accordions items={menuItems} variant="plain" size="sm" />
874
+ ```
875
+
876
+ ### ❌ Don't
877
+
878
+ 1. **Don't hide critical information**: Important content should be visible
879
+
880
+ ```tsx
881
+ // ❌ Bad: Hiding important warnings
882
+ <Accordions
883
+ items={[
884
+ { summary: 'Important Safety Information', details: criticalWarning },
885
+ ]}
886
+ />
887
+
888
+ // ✅ Good: Show warnings prominently
889
+ <Alert color="warning">{criticalWarning}</Alert>
890
+ ```
891
+
892
+ 2. **Don't nest accordions**: Avoid complex hierarchies
893
+
894
+ ```tsx
895
+ // ❌ Bad: Nested accordions
896
+ <Accordions
897
+ items={[
898
+ {
899
+ summary: 'Parent',
900
+ details: (
901
+ <Accordions items={[{ summary: 'Child', details: '...' }]} />
902
+ ),
903
+ },
904
+ ]}
905
+ />
906
+
907
+ // ✅ Good: Flat structure or use alternative
908
+ <Accordions items={flattenedItems} />
909
+ ```
910
+
911
+ 3. **Don't use for single items**: Unnecessary complexity
912
+
913
+ ```tsx
914
+ // ❌ Bad: Single accordion item
915
+ <Accordions items={[{ summary: 'Details', details: content }]} />
916
+
917
+ // ✅ Good: Use simple disclosure or always show content
918
+ <Card>
919
+ <CardContent>{content}</CardContent>
920
+ </Card>
921
+ ```
922
+
923
+ 4. **Don't overload with too many sections**: Keep it manageable
924
+
925
+ ```tsx
926
+ // ❌ Bad: Too many accordion items (15+)
927
+ <Accordions items={arrayOf15Items} />
928
+
929
+ // ✅ Good: Group or paginate if needed
930
+ <Accordions items={arrayOf5To7Items} />
931
+ ```
932
+
933
+ ## Performance Considerations
934
+
935
+ ### Lazy Load Heavy Content
936
+
937
+ For accordions with heavy content, render only when expanded:
938
+
939
+ ```tsx
940
+ function LazyAccordionContent({ isExpanded, children }) {
941
+ const [hasBeenExpanded, setHasBeenExpanded] = useState(false);
942
+
943
+ useEffect(() => {
944
+ if (isExpanded) setHasBeenExpanded(true);
945
+ }, [isExpanded]);
946
+
947
+ // Render placeholder until first expansion
948
+ if (!hasBeenExpanded) {
949
+ return <Typography color="neutral">Loading...</Typography>;
950
+ }
951
+
952
+ return children;
953
+ }
954
+ ```
955
+
956
+ ### Memoize Items Array
957
+
958
+ Prevent unnecessary re-renders by memoizing items:
959
+
960
+ ```tsx
961
+ const accordionItems = useMemo(
962
+ () => [
963
+ {
964
+ summary: 'Section 1',
965
+ details: <ExpensiveComponent data={data1} />,
966
+ },
967
+ {
968
+ summary: 'Section 2',
969
+ details: <ExpensiveComponent data={data2} />,
970
+ },
971
+ ],
972
+ [data1, data2]
973
+ );
974
+
975
+ <Accordions items={accordionItems} />
976
+ ```
977
+
978
+ ### Avoid Inline Functions in Items
979
+
980
+ ```tsx
981
+ // ❌ Bad: Creates new objects on every render
982
+ <Accordions
983
+ items={data.map((item) => ({
984
+ summary: item.title,
985
+ details: <Content data={item} />,
986
+ }))}
987
+ />
988
+
989
+ // ✅ Good: Memoized transformation
990
+ const items = useMemo(
991
+ () =>
992
+ data.map((item) => ({
993
+ summary: item.title,
994
+ details: <Content data={item} />,
995
+ })),
996
+ [data]
997
+ );
998
+ <Accordions items={items} />
999
+ ```
1000
+
1001
+ ### Virtualize Long Lists
1002
+
1003
+ For many accordion items, consider virtualization:
1004
+
1005
+ ```tsx
1006
+ // For very long lists (20+ items), consider:
1007
+ // 1. Pagination with limited items per page
1008
+ // 2. Search/filter to reduce visible items
1009
+ // 3. Grouping into categories
1010
+
1011
+ function FilteredAccordions({ allItems }) {
1012
+ const [search, setSearch] = useState('');
1013
+
1014
+ const filteredItems = useMemo(
1015
+ () =>
1016
+ allItems.filter((item) =>
1017
+ item.summary.toLowerCase().includes(search.toLowerCase())
1018
+ ),
1019
+ [allItems, search]
1020
+ );
1021
+
1022
+ return (
1023
+ <Stack gap={2}>
1024
+ <Input
1025
+ placeholder="Search..."
1026
+ value={search}
1027
+ onChange={(e) => setSearch(e.target.value)}
1028
+ />
1029
+ <Accordions items={filteredItems.slice(0, 10)} />
1030
+ {filteredItems.length > 10 && (
1031
+ <Typography level="body-sm" color="neutral">
1032
+ Showing 10 of {filteredItems.length} results
1033
+ </Typography>
1034
+ )}
1035
+ </Stack>
1036
+ );
1037
+ }
1038
+ ```
1039
+
1040
+ Accordions provides an efficient way to organize and present collapsible content sections. Use descriptive headers, keep content scannable, and choose appropriate variants based on your context. For critical information, consider displaying it prominently rather than hiding it within an accordion.