# Accordions

## Introduction

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.

```tsx
<Accordions {...args} />
```

| Field | Description                              | Default                                                                                                                                                                                                                                                      |
| ----- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 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> }] |

> ⚠️ **Usage Warning** ⚠️
>
> Consider these guidelines when using Accordions:
>
> - **Don't hide critical content**: Important information should be visible by default
> - **Limit nesting**: Avoid nesting accordions within accordions
> - **Keep headers concise**: Users should understand content without expanding
> - **Consider Tabs**: Use Tabs for content that users need to compare side-by-side

## Usage

```tsx
import { Accordions } from '@ceed/cds';

function FAQSection() {
  return (
    <Accordions
      items={[
        {
          summary: 'What is your return policy?',
          details: <p>We offer a 30-day return policy for all unused items.</p>,
        },
        {
          summary: 'How long does shipping take?',
          details: <p>Standard shipping takes 3-5 business days.</p>,
        },
        {
          summary: 'Do you ship internationally?',
          details: <p>Yes, we ship to over 50 countries worldwide.</p>,
        },
      ]}
    />
  );
}
```

## Examples

### Playground

Interactive example with all controls.

```tsx
<Accordions {...args} />
```

### Basics

Basic accordion with multiple collapsible sections.

```tsx
<Accordions
  items={[{
  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>
}]}
/>
```

### Sizes

Available size options: `sm`, `md`, `lg`.

```tsx
<>
  <Accordions {...args} size="sm" />
  <Accordions {...args} size="md" />
  <Accordions {...args} size="lg" />
</>
```

### Variants

Style variants: `plain`, `outlined`, `soft`, `solid`.

```tsx
<>
  <div>
    <h3>Plain</h3>
    <Accordions {...args} variant="plain" />
  </div>
  <div>
    <h3>Outlined</h3>
    <Accordions {...args} variant="outlined" />
  </div>
  <div>
    <h3>Soft</h3>
    <Accordions {...args} variant="soft" />
  </div>
  <div>
    <h3>Solid</h3>
    <Accordions {...args} variant="solid" />
  </div>
</>
```

### Colors

Color options: `primary`, `neutral`, `danger`, `success`, `warning`.

```tsx
<>
  <div>
    <h3>Primary</h3>
    <Accordions {...args} color="primary" />
  </div>
  <div>
    <h3>Neutral</h3>
    <Accordions {...args} color="neutral" />
  </div>
  <div>
    <h3>Danger</h3>
    <Accordions {...args} color="danger" />
  </div>
  <div>
    <h3>Success</h3>
    <Accordions {...args} color="success" />
  </div>
  <div>
    <h3>Warning</h3>
    <Accordions {...args} color="warning" />
  </div>
</>
```

### Removing Divider

Use `disableDivider` to remove the divider lines between accordion items.

```tsx
<Accordions {...args} disableDivider />
```

## When to Use

### ✅ Good Use Cases

- **FAQs**: Frequently asked questions with expandable answers
- **Settings panels**: Grouped settings that can be expanded individually
- **Product details**: Specifications, reviews, shipping info sections
- **Documentation**: Collapsible sections for detailed explanations
- **Navigation menus**: Expandable category menus
- **Form sections**: Long forms grouped into logical sections
- **Help content**: Step-by-step instructions or troubleshooting guides

### ❌ When Not to Use

- **Critical information**: Don't hide must-read content in accordions
- **Short content**: Use a list or cards if content is minimal
- **Comparison content**: Use Tabs when users need to compare sections
- **Primary navigation**: Use Navigator or Tabs for main navigation
- **Single item**: Don't use accordion for a single collapsible section
- **Deeply nested content**: Avoid nesting accordions within accordions

## Common Use Cases

### FAQ Section

```tsx
function FAQAccordion() {
  const faqs = [
    {
      summary: 'What payment methods do you accept?',
      details: (
        <Typography>
          We accept Visa, MasterCard, American Express, and PayPal.
          All payments are processed securely through our payment gateway.
        </Typography>
      ),
    },
    {
      summary: 'How can I track my order?',
      details: (
        <Typography>
          Once your order ships, you'll receive an email with a tracking
          number. You can use this number on our website or the carrier's
          site to track your package.
        </Typography>
      ),
    },
    {
      summary: 'What is your refund policy?',
      details: (
        <Stack gap={1}>
          <Typography>Our refund policy includes:</Typography>
          <ul>
            <li>30-day money-back guarantee</li>
            <li>Full refund for unused items</li>
            <li>Refunds processed within 5-7 business days</li>
          </ul>
        </Stack>
      ),
    },
  ];

  return (
    <Box sx={{ maxWidth: 600 }}>
      <Typography level="h2" sx={{ mb: 2 }}>
        Frequently Asked Questions
      </Typography>
      <Accordions items={faqs} variant="outlined" />
    </Box>
  );
}
```

### Settings Panel

```tsx
function SettingsAccordion({ settings, onSettingChange }) {
  const settingsItems = [
    {
      summary: 'Account Settings',
      details: (
        <Stack gap={2}>
          <FormControl>
            <FormLabel>Display Name</FormLabel>
            <Input
              value={settings.displayName}
              onChange={(e) => onSettingChange('displayName', e.target.value)}
            />
          </FormControl>
          <FormControl>
            <FormLabel>Email</FormLabel>
            <Input
              type="email"
              value={settings.email}
              onChange={(e) => onSettingChange('email', e.target.value)}
            />
          </FormControl>
        </Stack>
      ),
    },
    {
      summary: 'Notification Preferences',
      details: (
        <Stack gap={1}>
          <Checkbox
            label="Email notifications"
            checked={settings.emailNotifications}
            onChange={(e) =>
              onSettingChange('emailNotifications', e.target.checked)
            }
          />
          <Checkbox
            label="Push notifications"
            checked={settings.pushNotifications}
            onChange={(e) =>
              onSettingChange('pushNotifications', e.target.checked)
            }
          />
          <Checkbox
            label="Weekly digest"
            checked={settings.weeklyDigest}
            onChange={(e) => onSettingChange('weeklyDigest', e.target.checked)}
          />
        </Stack>
      ),
    },
    {
      summary: 'Privacy Settings',
      details: (
        <Stack gap={1}>
          <Checkbox
            label="Make profile public"
            checked={settings.publicProfile}
            onChange={(e) => onSettingChange('publicProfile', e.target.checked)}
          />
          <Checkbox
            label="Show activity status"
            checked={settings.showActivity}
            onChange={(e) => onSettingChange('showActivity', e.target.checked)}
          />
        </Stack>
      ),
    },
  ];

  return (
    <Box sx={{ maxWidth: 500 }}>
      <Typography level="h3" sx={{ mb: 2 }}>
        Settings
      </Typography>
      <Accordions items={settingsItems} variant="soft" color="neutral" />
    </Box>
  );
}
```

### Product Details

```tsx
function ProductAccordion({ product }) {
  const productDetails = [
    {
      summary: 'Description',
      details: (
        <Typography>{product.description}</Typography>
      ),
    },
    {
      summary: 'Specifications',
      details: (
        <Table>
          <tbody>
            {Object.entries(product.specs).map(([key, value]) => (
              <tr key={key}>
                <td>
                  <Typography level="body-sm" fontWeight="md">
                    {key}
                  </Typography>
                </td>
                <td>
                  <Typography level="body-sm">{value}</Typography>
                </td>
              </tr>
            ))}
          </tbody>
        </Table>
      ),
    },
    {
      summary: 'Shipping & Returns',
      details: (
        <Stack gap={2}>
          <Box>
            <Typography level="title-sm">Shipping</Typography>
            <Typography level="body-sm">
              Free shipping on orders over $50. Standard delivery 3-5 days.
            </Typography>
          </Box>
          <Box>
            <Typography level="title-sm">Returns</Typography>
            <Typography level="body-sm">
              30-day return policy. Items must be unused with original tags.
            </Typography>
          </Box>
        </Stack>
      ),
    },
    {
      summary: `Reviews (${product.reviewCount})`,
      details: (
        <Stack gap={2}>
          {product.reviews.slice(0, 3).map((review) => (
            <Card key={review.id} variant="outlined">
              <CardContent>
                <Stack direction="row" justifyContent="space-between">
                  <Typography level="title-sm">{review.author}</Typography>
                  <Typography level="body-xs" color="neutral">
                    {review.date}
                  </Typography>
                </Stack>
                <Typography level="body-sm">{review.content}</Typography>
              </CardContent>
            </Card>
          ))}
          <Button variant="plain" size="sm">
            View all reviews
          </Button>
        </Stack>
      ),
    },
  ];

  return <Accordions items={productDetails} variant="plain" />;
}
```

### Navigation Menu

```tsx
function NavigationAccordion() {
  const menuItems = [
    {
      summary: 'Products',
      details: (
        <List size="sm">
          <ListItem>
            <ListItemButton onClick={() => navigate('/products/electronics')}>
              Electronics
            </ListItemButton>
          </ListItem>
          <ListItem>
            <ListItemButton onClick={() => navigate('/products/clothing')}>
              Clothing
            </ListItemButton>
          </ListItem>
          <ListItem>
            <ListItemButton onClick={() => navigate('/products/home')}>
              Home & Garden
            </ListItemButton>
          </ListItem>
        </List>
      ),
    },
    {
      summary: 'Services',
      details: (
        <List size="sm">
          <ListItem>
            <ListItemButton onClick={() => navigate('/services/consulting')}>
              Consulting
            </ListItemButton>
          </ListItem>
          <ListItem>
            <ListItemButton onClick={() => navigate('/services/support')}>
              Support
            </ListItemButton>
          </ListItem>
        </List>
      ),
    },
    {
      summary: 'Resources',
      details: (
        <List size="sm">
          <ListItem>
            <ListItemButton onClick={() => navigate('/docs')}>
              Documentation
            </ListItemButton>
          </ListItem>
          <ListItem>
            <ListItemButton onClick={() => navigate('/blog')}>
              Blog
            </ListItemButton>
          </ListItem>
          <ListItem>
            <ListItemButton onClick={() => navigate('/tutorials')}>
              Tutorials
            </ListItemButton>
          </ListItem>
        </List>
      ),
    },
  ];

  return (
    <Box sx={{ width: 250 }}>
      <Accordions items={menuItems} variant="plain" size="sm" disableDivider />
    </Box>
  );
}
```

### Help Documentation

```tsx
function HelpAccordion() {
  const helpTopics = [
    {
      summary: 'Getting Started',
      details: (
        <Stack gap={2}>
          <Typography level="title-sm">Step 1: Create an Account</Typography>
          <Typography level="body-sm">
            Click the "Sign Up" button and fill in your details.
          </Typography>
          <Typography level="title-sm">Step 2: Verify Email</Typography>
          <Typography level="body-sm">
            Check your inbox for a verification email and click the link.
          </Typography>
          <Typography level="title-sm">Step 3: Complete Profile</Typography>
          <Typography level="body-sm">
            Add your profile picture and bio to personalize your account.
          </Typography>
        </Stack>
      ),
    },
    {
      summary: 'Troubleshooting',
      details: (
        <Stack gap={2}>
          <Box>
            <Typography level="title-sm" color="danger">
              Can't log in?
            </Typography>
            <Typography level="body-sm">
              Try resetting your password or clearing your browser cache.
            </Typography>
          </Box>
          <Box>
            <Typography level="title-sm" color="danger">
              Page not loading?
            </Typography>
            <Typography level="body-sm">
              Check your internet connection and try refreshing the page.
            </Typography>
          </Box>
        </Stack>
      ),
    },
    {
      summary: 'Contact Support',
      details: (
        <Stack gap={2}>
          <Typography level="body-sm">
            Need more help? Reach out to our support team:
          </Typography>
          <Stack gap={1}>
            <Typography level="body-sm">
              Email: support@example.com
            </Typography>
            <Typography level="body-sm">
              Live Chat: Available 9am-5pm EST
            </Typography>
            <Typography level="body-sm">
              Phone: 1-800-EXAMPLE
            </Typography>
          </Stack>
        </Stack>
      ),
    },
  ];

  return (
    <Box sx={{ maxWidth: 600 }}>
      <Typography level="h2" sx={{ mb: 2 }}>
        Help Center
      </Typography>
      <Accordions items={helpTopics} variant="outlined" color="primary" />
    </Box>
  );
}
```

### Collapsible Form Sections

```tsx
function MultiSectionForm({ onSubmit }) {
  const [formData, setFormData] = useState({
    personal: { firstName: '', lastName: '', email: '' },
    address: { street: '', city: '', zipCode: '' },
    payment: { cardNumber: '', expiry: '', cvv: '' },
  });

  const updateField = (section, field, value) => {
    setFormData((prev) => ({
      ...prev,
      [section]: { ...prev[section], [field]: value },
    }));
  };

  const formSections = [
    {
      summary: 'Personal Information',
      details: (
        <Stack gap={2}>
          <Stack direction="row" gap={2}>
            <FormControl sx={{ flex: 1 }}>
              <FormLabel>First Name</FormLabel>
              <Input
                value={formData.personal.firstName}
                onChange={(e) =>
                  updateField('personal', 'firstName', e.target.value)
                }
              />
            </FormControl>
            <FormControl sx={{ flex: 1 }}>
              <FormLabel>Last Name</FormLabel>
              <Input
                value={formData.personal.lastName}
                onChange={(e) =>
                  updateField('personal', 'lastName', e.target.value)
                }
              />
            </FormControl>
          </Stack>
          <FormControl>
            <FormLabel>Email</FormLabel>
            <Input
              type="email"
              value={formData.personal.email}
              onChange={(e) =>
                updateField('personal', 'email', e.target.value)
              }
            />
          </FormControl>
        </Stack>
      ),
    },
    {
      summary: 'Shipping Address',
      details: (
        <Stack gap={2}>
          <FormControl>
            <FormLabel>Street Address</FormLabel>
            <Input
              value={formData.address.street}
              onChange={(e) =>
                updateField('address', 'street', e.target.value)
              }
            />
          </FormControl>
          <Stack direction="row" gap={2}>
            <FormControl sx={{ flex: 2 }}>
              <FormLabel>City</FormLabel>
              <Input
                value={formData.address.city}
                onChange={(e) =>
                  updateField('address', 'city', e.target.value)
                }
              />
            </FormControl>
            <FormControl sx={{ flex: 1 }}>
              <FormLabel>ZIP Code</FormLabel>
              <Input
                value={formData.address.zipCode}
                onChange={(e) =>
                  updateField('address', 'zipCode', e.target.value)
                }
              />
            </FormControl>
          </Stack>
        </Stack>
      ),
    },
    {
      summary: 'Payment Details',
      details: (
        <Stack gap={2}>
          <FormControl>
            <FormLabel>Card Number</FormLabel>
            <Input
              placeholder="1234 5678 9012 3456"
              value={formData.payment.cardNumber}
              onChange={(e) =>
                updateField('payment', 'cardNumber', e.target.value)
              }
            />
          </FormControl>
          <Stack direction="row" gap={2}>
            <FormControl sx={{ flex: 1 }}>
              <FormLabel>Expiry Date</FormLabel>
              <Input
                placeholder="MM/YY"
                value={formData.payment.expiry}
                onChange={(e) =>
                  updateField('payment', 'expiry', e.target.value)
                }
              />
            </FormControl>
            <FormControl sx={{ flex: 1 }}>
              <FormLabel>CVV</FormLabel>
              <Input
                type="password"
                placeholder="123"
                value={formData.payment.cvv}
                onChange={(e) =>
                  updateField('payment', 'cvv', e.target.value)
                }
              />
            </FormControl>
          </Stack>
        </Stack>
      ),
    },
  ];

  return (
    <Box sx={{ maxWidth: 500 }}>
      <Accordions items={formSections} variant="outlined" />
      <Button sx={{ mt: 2 }} fullWidth onClick={() => onSubmit(formData)}>
        Submit Order
      </Button>
    </Box>
  );
}
```

## Props and Customization

### Key Props

| Prop             | Type                                                           | Default     | Description                                       |
| ---------------- | -------------------------------------------------------------- | ----------- | ------------------------------------------------- |
| `items`          | `Array<{ summary: string; details: ReactNode }>`               | -           | Array of accordion items with headers and content |
| `size`           | `'sm' \| 'md' \| 'lg'`                                         | `'md'`      | Size of the accordion                             |
| `variant`        | `'plain' \| 'outlined' \| 'soft' \| 'solid'`                   | `'plain'`   | Visual style variant                              |
| `color`          | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'neutral'` | Color theme                                       |
| `disableDivider` | `boolean`                                                      | `false`     | Remove divider lines between items                |

### Items Structure

```tsx
interface AccordionItem {
  summary: string;    // Header text displayed when collapsed
  details: ReactNode; // Content shown when expanded
}

// Example items array
const items = [
  {
    summary: 'Section Title',
    details: <Typography>Section content...</Typography>,
  },
  {
    summary: 'Another Section',
    details: (
      <Stack gap={2}>
        <Typography>Multiple elements...</Typography>
        <Button>Action</Button>
      </Stack>
    ),
  },
];
```

### Variant Options

```tsx
// Plain - minimal styling, no background
<Accordions items={items} variant="plain" />

// Outlined - bordered container
<Accordions items={items} variant="outlined" />

// Soft - subtle background color
<Accordions items={items} variant="soft" />

// Solid - full background color
<Accordions items={items} variant="solid" />
```

### Size Options

```tsx
// Small - compact for sidebars/menus
<Accordions items={items} size="sm" />

// Medium - default size
<Accordions items={items} size="md" />

// Large - spacious for main content
<Accordions items={items} size="lg" />
```

### Color Options

```tsx
// Primary - brand color emphasis
<Accordions items={items} color="primary" />

// Neutral - subtle, default
<Accordions items={items} color="neutral" />

// Status colors
<Accordions items={items} color="success" />
<Accordions items={items} color="warning" />
<Accordions items={items} color="danger" />
```

### Without Dividers

```tsx
// Clean look without divider lines
<Accordions items={items} disableDivider />
```

## Accessibility

Accordions includes built-in accessibility features:

### ARIA Attributes

- Accordion headers have `role="button"` with `aria-expanded`
- Content regions have `role="region"` with `aria-labelledby`
- Proper focus management between sections

### Keyboard Navigation

- **Tab**: Move focus between accordion headers
- **Enter/Space**: Expand or collapse focused accordion
- **Arrow Down**: Move focus to next accordion header
- **Arrow Up**: Move focus to previous accordion header
- **Home**: Move focus to first accordion header
- **End**: Move focus to last accordion header

### Screen Reader Support

```tsx
// Headers announce: "Section Title, collapsed/expanded, button"
<Accordions
  items={[
    {
      summary: 'Shipping Information',  // Announced as header
      details: <Typography>Content...</Typography>,
    },
  ]}
/>
```

### Focus Visibility

- Clear focus indicators on headers
- Focus remains on header after expand/collapse
- Tab navigation works within expanded content

## Best Practices

### ✅ Do

1. **Use descriptive headers**: Make content clear without expanding

```tsx
// ✅ Good: Descriptive headers
<Accordions
  items={[
    { summary: 'Return Policy (30 days)', details: '...' },
    { summary: 'Shipping Options & Costs', details: '...' },
  ]}
/>
```

2. **Group related content**: Keep sections logically organized

```tsx
// ✅ Good: Logical grouping
<Accordions
  items={[
    { summary: 'Account Settings', details: accountSettings },
    { summary: 'Notification Settings', details: notificationSettings },
    { summary: 'Privacy Settings', details: privacySettings },
  ]}
/>
```

3. **Keep content scannable**: Use lists and headings inside accordions

```tsx
// ✅ Good: Scannable content
<Accordions
  items={[
    {
      summary: 'Features',
      details: (
        <ul>
          <li>Feature one</li>
          <li>Feature two</li>
          <li>Feature three</li>
        </ul>
      ),
    },
  ]}
/>
```

4. **Match variant to context**: Use appropriate styling

```tsx
// ✅ Good: Outlined for forms, plain for navigation
<Accordions items={formSections} variant="outlined" />
<Accordions items={menuItems} variant="plain" size="sm" />
```

### ❌ Don't

1. **Don't hide critical information**: Important content should be visible

```tsx
// ❌ Bad: Hiding important warnings
<Accordions
  items={[
    { summary: 'Important Safety Information', details: criticalWarning },
  ]}
/>

// ✅ Good: Show warnings prominently
<Alert color="warning">{criticalWarning}</Alert>
```

2. **Don't nest accordions**: Avoid complex hierarchies

```tsx
// ❌ Bad: Nested accordions
<Accordions
  items={[
    {
      summary: 'Parent',
      details: (
        <Accordions items={[{ summary: 'Child', details: '...' }]} />
      ),
    },
  ]}
/>

// ✅ Good: Flat structure or use alternative
<Accordions items={flattenedItems} />
```

3. **Don't use for single items**: Unnecessary complexity

```tsx
// ❌ Bad: Single accordion item
<Accordions items={[{ summary: 'Details', details: content }]} />

// ✅ Good: Use simple disclosure or always show content
<Card>
  <CardContent>{content}</CardContent>
</Card>
```

4. **Don't overload with too many sections**: Keep it manageable

```tsx
// ❌ Bad: Too many accordion items (15+)
<Accordions items={arrayOf15Items} />

// ✅ Good: Group or paginate if needed
<Accordions items={arrayOf5To7Items} />
```

## Performance Considerations

### Lazy Load Heavy Content

For accordions with heavy content, render only when expanded:

```tsx
function LazyAccordionContent({ isExpanded, children }) {
  const [hasBeenExpanded, setHasBeenExpanded] = useState(false);

  useEffect(() => {
    if (isExpanded) setHasBeenExpanded(true);
  }, [isExpanded]);

  // Render placeholder until first expansion
  if (!hasBeenExpanded) {
    return <Typography color="neutral">Loading...</Typography>;
  }

  return children;
}
```

### Memoize Items Array

Prevent unnecessary re-renders by memoizing items:

```tsx
const accordionItems = useMemo(
  () => [
    {
      summary: 'Section 1',
      details: <ExpensiveComponent data={data1} />,
    },
    {
      summary: 'Section 2',
      details: <ExpensiveComponent data={data2} />,
    },
  ],
  [data1, data2]
);

<Accordions items={accordionItems} />
```

### Avoid Inline Functions in Items

```tsx
// ❌ Bad: Creates new objects on every render
<Accordions
  items={data.map((item) => ({
    summary: item.title,
    details: <Content data={item} />,
  }))}
/>

// ✅ Good: Memoized transformation
const items = useMemo(
  () =>
    data.map((item) => ({
      summary: item.title,
      details: <Content data={item} />,
    })),
  [data]
);
<Accordions items={items} />
```

### Virtualize Long Lists

For many accordion items, consider virtualization:

```tsx
// For very long lists (20+ items), consider:
// 1. Pagination with limited items per page
// 2. Search/filter to reduce visible items
// 3. Grouping into categories

function FilteredAccordions({ allItems }) {
  const [search, setSearch] = useState('');

  const filteredItems = useMemo(
    () =>
      allItems.filter((item) =>
        item.summary.toLowerCase().includes(search.toLowerCase())
      ),
    [allItems, search]
  );

  return (
    <Stack gap={2}>
      <Input
        placeholder="Search..."
        value={search}
        onChange={(e) => setSearch(e.target.value)}
      />
      <Accordions items={filteredItems.slice(0, 10)} />
      {filteredItems.length > 10 && (
        <Typography level="body-sm" color="neutral">
          Showing 10 of {filteredItems.length} results
        </Typography>
      )}
    </Stack>
  );
}
```

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.
