# Accordion Component

A collapsible content component that creates expandable/collapsible sections using native HTML `<details>`/`<summary>` elements. It supports multiple style variants, icon options, and flexible content.

## Import path
```tsx
import Accordion, { AccordionItem, AccordionTitle, AccordionContent } from '@hubspot/cms-component-library/Accordion';
```

## Purpose

The Accordion component provides a consistent way to render collapsible content sections in HubSpot CMS projects. It leverages native HTML `<details>` and `<summary>` elements for built-in expand/collapse functionality without requiring JavaScript. The component supports multiple style variants, customizable expand/collapse icons, and can contain any content within the expandable panels.

## Compositional Component Pattern

The Accordion component follows a **parent/child compositional pattern**, consisting of four separate but related components:

| Component | Role | Renders |
|-----------|------|---------|
| `Accordion` | Container | Flex column layout |
| `AccordionItem` | Item wrapper | `<details>` |
| `AccordionTitle` | Clickable trigger | `<summary>` |
| `AccordionContent` | Expandable panel | `<div>` |

**Why this pattern?**
- **Separation of concerns**: Each component handles a specific responsibility (layout, state, trigger, content)
- **Native HTML semantics**: Uses `<details>`/`<summary>` for built-in accessibility and keyboard support
- **Flexibility**: Each subcomponent can be styled and customized independently
- **HubSpot CMS integration**: Each component has its own field definitions, allowing RepeatedGroup usage for dynamic accordions
- **Type safety**: Props are scoped to their respective components, preventing invalid configurations

**Key relationships:**
- `AccordionItem` should **always** contain both `AccordionTitle` and `AccordionContent`
- `AccordionItem` should **always** be used inside an `Accordion` component
- `AccordionItem` controls variant styling via CSS variables that cascade to child components
- `AccordionTitle` renders the clickable summary with configurable expand/collapse icons
- `AccordionContent` contains the expandable content that shows/hides

This pattern is distinct from the "Compound Component Pattern" (e.g., `AccordionItem.StyleFields`) which attaches field definitions—Accordion uses *both* patterns together.

## Component Structure

```
Accordion/
├── index.tsx                     # Main Accordion container component
├── types.ts                      # TypeScript type definitions for Accordion
├── AccordionItem/
│   ├── index.tsx                 # AccordionItem wrapper component
│   ├── types.ts                  # AccordionItem TypeScript types
│   ├── StyleFields.tsx           # HubSpot field definitions for variant
│   └── index.module.scss         # CSS module for item styling
├── AccordionTitle/
│   ├── index.tsx                 # AccordionTitle component
│   ├── types.ts                  # AccordionTitle TypeScript types
│   ├── ContentFields.tsx         # HubSpot field definitions for title text
│   ├── StyleFields.tsx           # HubSpot field definitions for icon
│   ├── icons.tsx                 # SVG icon components (Chevron, Plus, Minus)
│   └── index.module.scss         # CSS module for title styling
├── AccordionContent/
│   ├── index.tsx                 # AccordionContent component
│   ├── types.ts                  # AccordionContent TypeScript types
│   ├── ContentFields.tsx         # HubSpot field definitions for content
│   └── index.module.scss         # CSS module for content styling
└── stories/
    └── Accordion.stories.tsx     # Storybook examples
```

## Components

### Accordion (Main Container)

**Purpose:** Container component that provides vertical layout with configurable gap spacing between accordion items.

**Props:**
```tsx
{
  gap?: string;                    // Space between accordion items (default: '24px')
  className?: string;              // Additional CSS classes
  style?: React.CSSProperties;     // Inline styles
  children: React.ReactNode;       // AccordionItem components
}
```

### AccordionItem (Item Wrapper)

**Purpose:** Wrapper component that renders a `<details>` element, providing the expand/collapse container with variant styling.

**Props:**
```tsx
{
  variant?: 'card1' | 'card2' | 'card3' | 'card4';  // Visual style variant (default: 'card1')
  className?: string;                                           // Additional CSS classes
  style?: React.CSSProperties;                                  // Inline styles
  children: React.ReactNode;                                    // Should contain AccordionTitle and AccordionContent
}
```

**Expected children:** Always include both `AccordionTitle` and `AccordionContent` as children.

### AccordionTitle (Trigger)

**Purpose:** Clickable trigger component that renders a `<summary>` element with rich text and an expand/collapse icon. Uses the `Text` component internally for CMS inline editing support.

**Props:**
```tsx
{
  icon?: 'chevron' | 'plus' | 'caret';  // Icon type (default: 'chevron')
  fieldPath?: string;                   // Path to the RichText field in HubSpot CMS (preferred for CMS modules)
  className?: string;                   // Additional CSS classes
  style?: React.CSSProperties;          // Inline styles
  children?: React.ReactNode;           // Fallback title content (used when fieldPath is not provided, e.g. in Storybook)
}
```

### AccordionContent (Panel)

**Purpose:** Content panel component that renders a `<div>` containing the expandable content. Uses the `Text` component internally when `fieldPath` is provided for CMS inline editing support.

**Props:**
```tsx
{
  fieldPath?: string;              // Path to the RichText field in HubSpot CMS (preferred for CMS modules)
  className?: string;              // Additional CSS classes
  style?: React.CSSProperties;     // Inline styles
  children?: React.ReactNode;      // Fallback content (used when fieldPath is not provided, e.g. in Storybook)
}
```

## Usage Examples

### Basic Accordion

```tsx
import Accordion, { AccordionItem, AccordionTitle, AccordionContent } from '@hubspot/cms-component-library/Accordion';

<Accordion>
  <AccordionItem>
    <AccordionTitle>What is HubSpot?</AccordionTitle>
    <AccordionContent>
      <p>HubSpot is a leading CRM platform that provides software and support to help businesses grow better.</p>
    </AccordionContent>
  </AccordionItem>
  <AccordionItem>
    <AccordionTitle>How do I get started?</AccordionTitle>
    <AccordionContent>
      <p>Sign up for a free account and explore our comprehensive onboarding guides and tutorials.</p>
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

### With Plus/Minus Icon

```tsx
import Accordion, { AccordionItem, AccordionTitle, AccordionContent } from '@hubspot/cms-component-library/Accordion';

<Accordion>
  <AccordionItem>
    <AccordionTitle icon="plus">Plus becomes minus when expanded</AccordionTitle>
    <AccordionContent>
      <p>The plus icon switches to a minus icon when expanded, clearly indicating toggle state.</p>
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

### Dynamic Rendering from Array

```tsx
import Accordion, { AccordionItem, AccordionTitle, AccordionContent } from '@hubspot/cms-component-library/Accordion';

const faqs = [
  { question: 'What payment methods do you accept?', answer: 'We accept all major credit cards and PayPal.' },
  { question: 'Can I cancel anytime?', answer: 'Yes, you can cancel your subscription at any time.' },
  { question: 'Do you offer refunds?', answer: 'We offer a 30-day money-back guarantee.' },
];

<Accordion>
  {faqs.map(({ question, answer }, index) => (
    <AccordionItem key={index}>
      <AccordionTitle icon="plus">{question}</AccordionTitle>
      <AccordionContent>{answer}</AccordionContent>
    </AccordionItem>
  ))}
</Accordion>
```

### With Rich Content

```tsx
import Accordion, { AccordionItem, AccordionTitle, AccordionContent } from '@hubspot/cms-component-library/Accordion';

<Accordion>
  <AccordionItem>
    <AccordionTitle>Getting Started Guide</AccordionTitle>
    <AccordionContent>
      <p>Welcome to HubSpot! Here are the steps to get started:</p>
      <ol>
        <li>Complete your profile setup</li>
        <li>Connect your email and calendar</li>
        <li>Import your existing contacts</li>
        <li>Set up your first campaign</li>
      </ol>
      <p><strong>Pro tip:</strong> Start with our interactive tutorials.</p>
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

## HubSpot CMS Integration

### Field Definitions

The Accordion component provides field definitions for easy integration with HubSpot CMS modules.

#### AccordionItem.StyleFields

Configurable props for variant selection:

```tsx
<AccordionItem.StyleFields
  variantName="accordionVariant"
  variantLabel="Accordion style"
  variantDefault={{ variant_name: 'card1' }}
/>
```

**Fields:**
- `accordionVariant`: VariantSelectionField for selecting visual style using the card variant definition (card1, card2, card3, card4)

#### AccordionTitle.ContentFields

Configurable props for title text. Uses `Text.ContentFields` internally with the `heading` feature set:

```tsx
<AccordionTitle.ContentFields
  titleName="title"
  titleLabel="Title"
  titleDefault="<p>Accordion title</p>"
/>
```

**Fields:**
- `title`: RichTextField for accordion title text (heading feature set)

#### AccordionTitle.StyleFields

Configurable props for icon style:

```tsx
<AccordionTitle.StyleFields
  iconName="icon"
  iconLabel="Icon"
  iconDefault="chevron"
/>
```

**Fields:**
- `icon`: ChoiceField for selecting icon type (caret, chevron, plus)

#### AccordionContent.ContentFields

Configurable props for content. Uses `Text.ContentFields` internally with the `bodyContent` feature set:

```tsx
<AccordionContent.ContentFields
  contentName="content"
  contentLabel="Content"
  contentDefault="<p>Add a short description</p>"
/>
```

**Fields:**
- `content`: RichTextField for accordion content (bodyContent feature set)

### Module Usage Example

```tsx
import { ModuleMeta } from '../types/modules.js';
import Accordion, {
  AccordionItem,
  AccordionTitle,
  AccordionContent,
} from '@hubspot/cms-component-library/Accordion';
import { AccordionVariant } from '@hubspot/cms-component-library/Accordion/AccordionItem/types';
import { AccordionIconType } from '@hubspot/cms-component-library/Accordion/AccordionTitle/types';

type FAQModuleProps = {
  style?: {
    accordionVariant?: { variant_name: AccordionVariant };
    icon?: AccordionIconType;
  };
  accordionItems?: Array<{
    title?: string;
    content?: string;
  }>;
};

export const Component = ({
  style,
  accordionItems = [],
}: FAQModuleProps) => {
  const variant = style?.accordionVariant?.variant_name;
  const icon = style?.icon;

  return (
    <Accordion>
      {accordionItems.map((_item, index) => (
        <AccordionItem key={index} variant={variant}>
          <AccordionTitle
            icon={icon}
            fieldPath={`accordionItems[${index}].title`}
          />
          <AccordionContent
            fieldPath={`accordionItems[${index}].content`}
          />
        </AccordionItem>
      ))}
    </Accordion>
  );
};

export const meta: ModuleMeta = {
  label: 'FAQ Accordion',
  content_types: [],
  icon: '',
  categories: ['body_content'],
};
```

### Module Fields Example

```tsx
import {
  FieldGroup,
  ModuleFields,
  RepeatedFieldGroup,
} from '@hubspot/cms-components/fields';
import {
  AccordionContent,
  AccordionItem,
  AccordionTitle,
} from '@hubspot/cms-component-library/Accordion';

const defaultItem = {
  title: '<p>Accordion title</p>',
  content: '<p>Add a short description</p>',
};

export const fields = (
  <ModuleFields>
    <RepeatedFieldGroup
      label="Accordion Items"
      name="accordionItems"
      occurrence={{ min: 1, max: 20, default: 3 }}
      default={[defaultItem, defaultItem, defaultItem]}
    >
      <AccordionTitle.ContentFields />
      <AccordionContent.ContentFields />
    </RepeatedFieldGroup>
    <FieldGroup label="Style" name="style" tab="STYLE">
      <AccordionItem.StyleFields />
      <AccordionTitle.StyleFields />
    </FieldGroup>
  </ModuleFields>
);
```

## Styling

### CSS Variables

The Accordion component uses the shared card variant system for theming. Variant styles are applied via `data-card-variant` data attribute on `AccordionItem`, using the card variant definition from `VariantSelectionField`. The component consumes `--hs-card-*` theme CSS variables (e.g., `--hs-card-borderColor`, `--hs-card-borderRadius`, `--hs-card-backgroundColor`, `--hs-card-color`).

Layout values (padding, gap, font sizing) are hardcoded. The accordion icon uses `currentColor` for its fill, inheriting from the card's text color.

### CSS Module Classes

**AccordionItem (`AccordionItem/index.module.scss`):**
- `.accordionItem`: Base details element styling with border, radius, background

**AccordionTitle (`AccordionTitle/index.module.scss`):**
- `.accordionTitle`: Flex container for title and icon
- `.accordionTitleText`: Title text with flex grow
- `.accordionIcon`: Base icon styling
- `.accordionIconChevron`: Chevron-specific sizing (rotates 180° when open)
- `.accordionIconPlus`: Plus icon (hidden when open)
- `.accordionIconMinus`: Minus icon (shown when open)
- `.accordionIconCaret`: Caret icon (rotates 180° when open)

**AccordionContent (`AccordionContent/index.module.scss`):**
- `.accordionContent`: Content area with padding and typography

## Accessibility

The Accordion component follows accessibility best practices:

- **Semantic HTML**: Uses native `<details>` and `<summary>` elements for built-in accessibility support
- **Keyboard Navigation**:
  - `Enter` or `Space` on summary toggles the accordion
  - Tab navigation works natively between accordion items
- **Screen Reader Support**:
  - `<details>` element announces expanded/collapsed state
  - `<summary>` element is announced as a button
- **Icon Accessibility**:
  - All icons have `aria-hidden="true"` since they are decorative
  - Icons have `role="img"` for proper semantics
- **Focus States**: Native focus styles are preserved for keyboard users
- **No JavaScript Required**: Expand/collapse functionality works without JavaScript

## Best Practices

- **Choose appropriate icons**: Use `caret` for a compact filled triangle indicator, `chevron` for subtle indication, `plus` for clearer expand/collapse semantics (common in FAQs)
- **Consistent variants**: Apply the same variant to all items within an accordion for visual consistency
- **Gap selection**: Use any valid CSS length value (e.g., '8px', '16px', '24px', '48px') for spacing between items
- **Dynamic rendering**: Always provide unique `key` props when mapping arrays to AccordionItems
- **Rich content**: AccordionContent supports any HTML content including lists, paragraphs, images, and nested components
- **Prefer library components**: When adding content inside AccordionContent, prefer using library components (e.g., `List`, `Text` with `textFeatureSet="heading"` for headings) over raw HTML elements for consistent theming and styling
- **Theme Variables**: Styling is controlled by the card variant system via `--hs-card-*` theme variables
- **Single responsibility**: Keep each accordion item focused on one topic for better UX

## Related Components

- **Flex**: Used internally for Accordion container layout. The Accordion component wraps Flex with a column direction.

