Skip to content

Accordion

A set of interactive headings that each reveal a section of content. The accordion keeps at most one panel open by default, supports keyboard navigation, and animates open/close transitions via CSS.

import { Accordion } from "@areia/slots";

document.querySelector("#root")!.innerHTML = `
  <div
    data-slot="accordion"
    class="w-72 border-2 border-t-black border-l-black border-r-white border-b-white bg-white"
  >
    <div
      data-slot="accordion-item"
      data-value="item-1"
      class="border-b border-neutral-500 last:border-b-0"
    >
      <button
        data-slot="accordion-trigger"
        class="win95-button flex w-full justify-between"
      >
        Item One
        <span data-slot="accordion-trigger-icon" aria-hidden="true"></span>
      </button>
      <div data-slot="accordion-content" class="bg-white px-3 py-2 text-sm">
        <div
          data-slot="accordion-content-inner"
          class="border-l-2 border-neutral-400 pl-2"
        >
          Content for item one.
        </div>
      </div>
    </div>
    <div
      data-slot="accordion-item"
      data-value="item-2"
      class="border-b border-neutral-500 last:border-b-0"
    >
      <button
        data-slot="accordion-trigger"
        class="win95-button flex w-full justify-between"
      >
        Item Two
        <span data-slot="accordion-trigger-icon" aria-hidden="true"></span>
      </button>
      <div data-slot="accordion-content" class="bg-white px-3 py-2 text-sm">
        <div
          data-slot="accordion-content-inner"
          class="border-l-2 border-neutral-400 pl-2"
        >
          Content for item two.
        </div>
      </div>
    </div>
  </div>
`;

const accordion = document.querySelector(
  '[data-slot="accordion"]',
)!;

const controller = Accordion.createAccordion(accordion, {
  multiple: false,
  orientation: "vertical",
});

Import

import { Accordion } from "@areia/slots";

Usage

import { Accordion } from "@areia/slots";

// Auto-bind a single root
const root = document.querySelector('[data-slot="accordion"]');
const controller = Accordion.createAccordion(root, {
  defaultValue: "item-1",
});

// Auto-bind all unbound roots in scope
const controllers = Accordion.create();

Expected Markup

The accordion expects the following data-slot structure. Each item requires a data-value attribute used to identify it during expand, collapse, and toggle operations.

<div data-slot="accordion">
  <div data-slot="accordion-item" data-value="item-1">
    <button data-slot="accordion-trigger">
      Item One
      <span
        data-slot="accordion-trigger-icon"
        aria-hidden="true"
      ></span>
    </button>
    <div data-slot="accordion-content">
      <div data-slot="accordion-content-inner">
        Content for item one.
      </div>
    </div>
  </div>
  <div data-slot="accordion-item" data-value="item-2">
    <button data-slot="accordion-trigger">
      Item Two
      <span
        data-slot="accordion-trigger-icon"
        aria-hidden="true"
      ></span>
    </button>
    <div data-slot="accordion-content">
      <div data-slot="accordion-content-inner">
        Content for item two.
      </div>
    </div>
  </div>
</div>

Data Slots

SlotRequiredDescription
accordionYesRoot container.
accordion-itemYesWraps a trigger and its associated content panel.
accordion-triggerYesButton that expands or collapses the item.
accordion-contentYesCollapsible panel controlled by the trigger.
accordion-content-innerNoInner wrapper for padding or animation isolation.
accordion-trigger-iconNoDecorative icon on the trigger (e.g. chevron).

CSS Custom Properties

  • --accordion-panel-height — Set to the panel’s scrollHeight (in px) during open/close transitions, then reset to auto when settled.
  • --accordion-panel-width — Set to the panel’s scrollWidth (in px) during open/close transitions, then reset to auto when settled.
  • --radix-accordion-content-height / --radix-accordion-content-width — Radix-compatible aliases of the above.

Generated Attributes

The controller writes the following attributes on initialization and updates them as state changes:

AttributeElement(s)Description
data-stateaccordion-item"open" or "closed".
data-openaccordion-itemPresent when the item is expanded.
data-closedaccordion-itemPresent when the item is collapsed.
data-disabledaccordion-item, accordion-triggerPresent when the item is disabled.
data-orientationRoot, items, triggers, contents"vertical" or "horizontal".
data-indexaccordion-itemZero-based index of the item.
data-starting-styleaccordion-contentSet during open transition, removed when settled.
data-ending-styleaccordion-contentSet during close transition, removed when settled.

Events

Outbound

EventDetailDescription
accordion:change{ value: string[] }Fired on the root when the set of expanded items changes.

Inbound

EventDetailDescription
accordion:set{ value: string | string[] }Dispatch on the root to expand the given item(s). Invalid values are silently ignored.

API Reference

Options

OptionTypeDefaultDescription
defaultValuestring | string[]Item value(s) to expand on initialization.
multiplebooleanfalseAllow more than one item to be expanded at the same time.
disabledbooleanfalseDisable the entire accordion.
orientation"horizontal" | "vertical""vertical"Layout direction. Controls roving-focus axis and ARIA attributes.
loopFocusbooleanfalseWhen true, arrow-key navigation wraps from the last trigger to the first (and vice versa).
hiddenUntilFoundbooleanfalseUse hidden="until-found" on closed panels instead of hidden.
collapsiblebooleanDeprecated. The accordion is collapsible by default. Kept for backward compatibility.
onValueChange(value: string[]) => voidCalled when the set of expanded items changes.

Controller

MemberTypeDescription
expand(v)(value: string) => voidExpand the item identified by value.
collapse(v)(value: string) => voidCollapse the item identified by value.
toggle(v)(value: string) => voidToggle the item identified by value.
valuestring[] (getter)Currently expanded values.
destroy()() => voidRemove all listeners and clean up.

Controller

The controller is returned by Accordion.createAccordion() and provides imperative control over the accordion’s state.

const controller = Accordion.createAccordion(root, {
  defaultValue: "item-1",
});

// Expand programmatically
controller.expand("item-2");

// Collapse programmatically
controller.collapse("item-1");

// Toggle an item
controller.toggle("item-3");

// Read current state
console.log(controller.value); // ["item-2"]

// Clean up
controller.destroy();