# Collapse

> Bootstrap's collapse component built with Svelte 5. Toggle the visibility of content across your project with the Collapse component.

Source: https://bootstrap-svelte.vercel.app/components/collapse — part of the Bootstrap Svelte documentation (index: https://bootstrap-svelte.vercel.app/llms.txt).

## Basic Example

Click the button below to expand and collapse the collapsible content. The `isExpanded` prop controls the visibility of the content.

```html
<script>
    import { collapseAria } from '@winkintel/bootstrap-svelte';

    let isBasicExpanded = $state(false);
</script>

<Button
    {@attach collapseAria({
        ariaControls: 'basicExample',
        ariaExpanded: isBasicExpanded
    })}
    colorVariant="primary"
    isExpanded={isBasicExpanded}
    onclick={() => (isBasicExpanded = !isBasicExpanded)}>
    {isBasicExpanded ? 'Collapse Content' : 'Expand Content'}
</Button>

<Collapse id="basicExample" isExpanded={isBasicExpanded}>
    This is some placeholder content for a collapsible element.
    Click the button to toggle visibility.
</Collapse>
```

## Horizontal Collapse

The Collapse component can collapse content horizontally instead of vertically by setting the `isHorizontal` prop to `true`.

```html
<script>
    import { collapseAria } from '@winkintel/bootstrap-svelte';

    let isHorizontalCollapsed = $state(false);
</script>

<Button
    {@attach collapseAria({
        ariaControls: 'horizontalExample',
        ariaExpanded: isHorizontalCollapsed
    })}
    colorVariant="primary"
    isExpanded={isHorizontalCollapsed}
    onclick={() => isHorizontalCollapsed = !isHorizontalCollapsed}>
    {isHorizontalCollapsed ? 'Collapse Content' : 'Expand Content'}
</Button>

<div class="p-4">
    <Collapse id="horizontalExample" isExpanded={isHorizontalCollapsed} isHorizontal={true}>
        <div style="width: 300px;">
            This content collapses horizontally instead of vertically.
        </div>
    </Collapse>
</div>
```

## Multiple Targets

A button can expand and collapse multiple collapsible elements by controlling the `isExpanded` prop of multiple Collapse components.

```html
<script>
    import { collapseAria } from '@winkintel/bootstrap-svelte';

    let isMultiCollapseExpanded1 = $state(false);
    let isMultiCollapseExpanded2 = $state(false);

    function toggleMultiCollapse(target: string) {
        if (target === 'multiCollapse1') {
            isMultiCollapseExpanded1 = !isMultiCollapseExpanded1;
        } else if (target === 'multiCollapse2') {
            isMultiCollapseExpanded2 = !isMultiCollapseExpanded2;
        } else if (target === 'both') {
            isMultiCollapseExpanded1 = !isMultiCollapseExpanded1;
            isMultiCollapseExpanded2 = !isMultiCollapseExpanded2;
        }
    }
</script>

<p>
    <Button
        {@attach collapseAria({
            ariaControls: 'multiCollapse1',
            ariaExpanded: isMultiCollapseExpanded1
        })}
        colorVariant="primary"
        onclick={() => toggleMultiCollapse('multiCollapse1')}>
        Toggle First Element
    </Button>
    <Button
        {@attach collapseAria({
            ariaControls: 'multiCollapse2',
            ariaExpanded: isMultiCollapseExpanded2
        })}
        colorVariant="secondary"
        onclick={() => toggleMultiCollapse('multiCollapse2')}>
        Toggle Second Element
    </Button>
    <Button
        {@attach collapseAria({
            ariaControls: 'multiCollapse1 multiCollapse2',
            ariaExpanded: isMultiCollapseExpanded1 && isMultiCollapseExpanded2
        })}
        colorVariant="info"
        onclick={() => toggleMultiCollapse('both')}>
        Toggle Both Elements
    </Button>
</p>

<div class="row">
    <div class="col">
        <Collapse id="multiCollapse1" isExpanded={isMultiCollapseExpanded1}>
            <div class="card card-body">
            First collapsible element content
            </div>
        </Collapse>
    </div>
    <div class="col">
        <Collapse id="multiCollapse2" isExpanded={isMultiCollapseExpanded2}>
            <div class="card card-body">
            Second collapsible element content
            </div>
        </Collapse>
    </div>
</div>
```

## Events

The Collapse component emits events when it shows and hides. You can attach event handlers with the `onExpand`, `onExpanded`, `onCollapse`, and `onCollapsed` props.

```html
<script>
    import { collapseAria } from '@winkintel/bootstrap-svelte';

    let isEventsExpanded = $state(false);
</script>

<Button
    {@attach collapseAria({
        ariaControls: 'eventsExample',
        ariaExpanded: isEventsExpanded
    })}
    colorVariant="primary"
    onclick={() => isEventsExpanded = !isEventsExpanded}>
    {isEventsExpanded ? 'Collapse Content' : 'Expand Content'}
</Button>

<Collapse
    id="eventsExample"
    isExpanded={isEventsExpanded}
    onExpand={e => console.log('onExpand event fired', e)}
    onExpanded={e => console.log('onExpanded event fired', e)}
    onCollapse={e => console.log('onCollapse event fired', e)}
    onCollapsed={e => console.log('onCollapsed event fired', e)}>
    <div class="card card-body">
        This collapse component has event handlers attached.
        Check the console to see the events as they fire.
    </div>
</Collapse>
```

## Accessibility

Be sure to add `aria-expanded` to the control element. This attribute explicitly conveys the current state of the collapsible element tied to the control to screen readers and similar assistive technologies. If the collapsible element is closed by default, the attribute on the control element should have a value of `aria-expanded="false"`. If you’ve set the collapsible element to be open by default using the show class, set `aria-expanded="true"` on the control instead.

The control element that targets the collapsible element should add the `aria-controls` attribute to the control element, containing the `id` of the collapsible element. Multiple collapsible elements are simply space delimited. Modern screen readers and similar assistive technologies make use of this attribute to provide users with additional shortcuts to navigate directly to the collapsible element itself.

The Collapse component provides a `collapseAria` attachment to easily add the necessary ARIA attributes to any control element. See the examples above for usage details.

## API Reference

### Collapse ARIA Attachment

The `collapseAria` function creates an attachment that can be applied to an element with the `{@attach ...}` directive.

The attachment preserves existing `aria-controls` and `role` attributes, and sets `aria-expanded` from the supplied state. On cleanup, it restores the previous expanded value or removes attributes it introduced, but only when their current values still match its own writes. Later consumer changes, including attribute removal, are left intact. Ownership is value-based: an identical later write cannot be distinguished from the attachment's write.

With Svelte-managed attributes, that identical-write limitation can leave the DOM out of sync with the bound value after cleanup. Svelte caches its last attribute value and may not write it again until the bound value changes. Prefer one owner for each attribute; supply consumer target and role overrides before attaching. Overlapping instances of this helper track their own writes so removing them in either order does not restore a value from an attachment that has already been removed.

Reactive option changes clean up and reattach: attachment-owned target IDs update, consumer target overrides remain, and the new `ariaExpanded` value is applied. A later consumer-expanded value is preserved on cleanup if it differs from the attachment's write, but the next attachment takes control of expanded state again.

This helper runs in the browser. For server-rendered accessibility, also supply `aria-controls` and `aria-expanded` directly in your markup. It does not discover or register a controlled component; `ariaControls` remains required and should identify your Collapse target. It does not replace Navbar or Offcanvas trigger ownership.

```html
// Type definition for CollapseAriaOptions
export type CollapseAriaOptions = {
    ariaControls: string;
    ariaExpanded: boolean;
};

// Usage example
<Button
    {@attach collapseAria({
        ariaControls: 'collapseExample',    // Single or space delimited list of the Collapse component IDs being controlled
        ariaExpanded: isExpanded            // Boolean indicating if the Collapse component is expanded (true) or collapsed (false)
    })}
    colorVariant="primary"
    onclick={() => isExpanded = !isExpanded}>
    {isExpanded ? 'Collapse Content' : 'Expand Content'}
</Button>
```

### Props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | `undefined` | Additional CSS classes to apply to the component. |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |
| `isHorizontal` | `boolean` | `false` | Makes the collapse transition horizontally instead of vertically. |
| `isExpanded` | `boolean` | `false` | Controls whether the collapse content is visible. |

### Events

| Name | Type | Description |
| --- | --- | --- |
| `onExpand` | `EventListener` | Fired when the collapse is about to expand (at the start of the transition). |
| `onExpanded` | `EventListener` | Fired when the collapse has been fully expanded (after the transition is complete). |
| `onCollapse` | `EventListener` | Fired when the collapse is about to collapse (at the start of the transition). |
| `onCollapsed` | `EventListener` | Fired when the collapse has been fully collapsed (after the transition is complete). |

### CSS Classes

The component applies Bootstrap's collapse classes based on the provided props:

- `collapse` - Always applied as the base class
- `collapse-horizontal` - Applied when isHorizontal is true
- `show` - Applied when isExpanded is true
