Accordion

A set of collapsible sections for showing one or many panels.

Native source
<div mlk-accordion-root data-type="single" data-collapsible>
  <div mlk-accordion-item>
    <h3 mlk-accordion-header>
      <a mlk-accordion-trigger draggable="false" href="#native-content">Native package</a>
    </h3>
    <div mlk-accordion-content id="native-content" data-open>
      <div class="demo__content-inner">@milkui/core/accordion</div>
    </div>
  </div>
  <div mlk-accordion-item>
    <h3 mlk-accordion-header>
      <a mlk-accordion-trigger draggable="false" href="#react-content">React adapter</a>
    </h3>
    <div mlk-accordion-content id="react-content" hidden="until-found">
      <div class="demo__content-inner">@milkui/react/accordion</div>
    </div>
  </div>
  <div mlk-accordion-item>
    <h3 mlk-accordion-header>
      <a mlk-accordion-trigger draggable="false" href="#primitive-content">Shared primitive</a>
    </h3>
    <div mlk-accordion-content id="primitive-content" hidden="until-found">
      <div class="demo__content-inner">@milkui/core/primitive</div>
    </div>
  </div>
</div>

<script type="module">
  import { Root, Item, Header, Trigger, Content } from '@milkui/core/accordion';
  Root.define(document);
  Item.define(document);
  Header.define(document);
  Trigger.define(document);
  Content.define(document);
</script>

Features

  • Fragment links work before JavaScript loads.
  • Single or multiple item selection.
  • Controlled or uncontrolled value state.
  • Collapsible single-item mode.
  • Disabled roots and disabled items.
  • Vertical or horizontal orientation with direction support.

Anatomy

Triggers link to content marked hidden="until-found" before JavaScript. Following a link reveals that panel without resetting others. Enhancement adds toggle and keyboard behavior.

Native anatomy
<div mlk-accordion-root>
  <div mlk-accordion-item>
    <h3 mlk-accordion-header>
      <a mlk-accordion-trigger></a>
    </h3>
    <div mlk-accordion-content></div>
  </div>
</div>

API reference

Closed content uses hidden="until-found"; initially open content uses data-open without hidden. Use instance.update() for later state changes.

Root

PropTypeDefaultDescription
type"single" | "multiple""single"Selection mode.
valuestring | string[]undefinedControlled value.
defaultValuestring | string[]undefinedInitial uncontrolled value.
onValueChange(value) => voidundefinedValue change request callback.
collapsiblebooleanfalseAllow the open single item to close.
disabledbooleanfalseDisable every item.
orientation"vertical" | "horizontal""vertical"Keyboard orientation.
dir"ltr" | "rtl""ltr"Direction for horizontal navigation.

Item

PropTypeDefaultDescription
disabledbooleanfalseDisable this item.

Header

PropTypeDefaultDescription
elementh3h3Heading wrapper for the trigger.

Trigger

PropTypeDefaultDescription
hrefstringContent fragmentLink enhanced with disclosure behavior.

Content

PropTypeDefaultDescription
idstringrequired for fragment linksUnique content ID; also its native selection value.
Data attributeValues
[data-open]Present when the item is open
[data-disabled]Present when disabled
[mlk-accordion-root]Root marker
[mlk-accordion-item]Item marker
[mlk-accordion-header]Header marker
[mlk-accordion-trigger]Trigger marker
[mlk-accordion-content]Content marker
CSS variableDescription
--mlk-accordion-content-widthMeasured content width.
--mlk-accordion-content-heightMeasured content height.

Examples

Independent accordions

Disable JavaScript and reload, then open Returns and Privacy. Both remain visible. Links reveal panels without closing others. Enhancement adds independent single-selection toggling.

Native source
<div class="independent-accordions">
  <section>
    <h3>Delivery</h3>
    <div mlk-accordion-root data-type="single" data-collapsible>
      <div mlk-accordion-item>
        <h3 mlk-accordion-header><a mlk-accordion-trigger draggable="false" href="#delivery-shipping-content">Shipping</a></h3>
        <div mlk-accordion-content id="delivery-shipping-content" data-open><div class="demo__content-inner">Shipping information.</div></div>
      </div>
      <div mlk-accordion-item>
        <h3 mlk-accordion-header><a mlk-accordion-trigger draggable="false" href="#delivery-returns-content">Returns</a></h3>
        <div mlk-accordion-content id="delivery-returns-content" hidden="until-found"><div class="demo__content-inner">Returns information.</div></div>
      </div>
    </div>
  </section>
  <section>
    <h3>Account</h3>
    <div mlk-accordion-root data-type="single" data-collapsible>
      <div mlk-accordion-item>
        <h3 mlk-accordion-header><a mlk-accordion-trigger draggable="false" href="#account-billing-content">Billing</a></h3>
        <div mlk-accordion-content id="account-billing-content" data-open><div class="demo__content-inner">Billing information.</div></div>
      </div>
      <div mlk-accordion-item>
        <h3 mlk-accordion-header><a mlk-accordion-trigger draggable="false" href="#account-privacy-content">Privacy</a></h3>
        <div mlk-accordion-content id="account-privacy-content" hidden="until-found"><div class="demo__content-inner">Privacy information.</div></div>
      </div>
    </div>
  </section>
</div>

Controlled value

The root requests value changes. Your state decides whether to accept them.

Native source
<script type="module">
  import { Root, Item, Header, Trigger, Content } from '@milkui/core/accordion';
  Root.define(document);
  Item.define(document);
  Header.define(document);
  Trigger.define(document);
  Content.define(document);

  await Promise.resolve();
  let value = Root.get(document.getElementById('accordion-controlled')).values[0] ?? '';
  const root = Root.mount(document.getElementById('accordion-controlled'), {
    type: 'single',
    value,
    onValueChange(nextValue) {
      if (accept.checked) setValue(nextValue);
    },
  });

  function setValue(nextValue: string) {
    value = nextValue;
    root.update({ value });
  }
</script>

Multiple items

Multiple accordions can keep more than one item open at a time.

Native source
<div mlk-accordion-root data-type="multiple">
  <div mlk-accordion-item>
    <h3 mlk-accordion-header>
      <a mlk-accordion-trigger draggable="false" href="#multiple-native-content">Native package</a>
    </h3>
    <div mlk-accordion-content id="multiple-native-content" data-open>
      <div class="demo__content-inner">@milkui/core/accordion</div>
    </div>
  </div>
  <div mlk-accordion-item>
    <h3 mlk-accordion-header>
      <a mlk-accordion-trigger draggable="false" href="#multiple-react-content">React adapter</a>
    </h3>
    <div mlk-accordion-content id="multiple-react-content" data-open>
      <div class="demo__content-inner">@milkui/react/accordion</div>
    </div>
  </div>
  <div mlk-accordion-item>
    <h3 mlk-accordion-header>
      <a mlk-accordion-trigger draggable="false" href="#multiple-primitive-content">Shared primitive</a>
    </h3>
    <div mlk-accordion-content id="multiple-primitive-content" hidden="until-found">
      <div class="demo__content-inner">@milkui/core/primitive</div>
    </div>
  </div>
</div>

Horizontal orientation

Orientation is a root-level option for keyboard navigation and layout.

Native source
<div
  mlk-accordion-root
  class="accordion-horizontal"
  data-type="single"

  data-orientation="horizontal"
  dir="ltr"
>
  <div mlk-accordion-item>
    <h3 mlk-accordion-header>
      <a mlk-accordion-trigger draggable="false" href="#horizontal-one-content">One</a>
    </h3>
    <div mlk-accordion-content id="horizontal-one-content" data-open>
      <div class="demo__content-inner">Horizontal item one.</div>
    </div>
  </div>
  <div mlk-accordion-item>
    <h3 mlk-accordion-header>
      <a mlk-accordion-trigger draggable="false" href="#horizontal-two-content">Two</a>
    </h3>
    <div mlk-accordion-content id="horizontal-two-content" hidden="until-found">
      <div class="demo__content-inner">Horizontal item two.</div>
    </div>
  </div>
</div>

Keyboard interactions

Before enhancement, triggers are links to sections. Once enhanced, Enter and Space toggle them without changing the URL.

KeyDescription
SpaceOpens or closes the focused trigger.
EnterOpens or closes the focused trigger.
ArrowDown / ArrowRightMoves focus to the next enabled trigger.
ArrowUp / ArrowLeftMoves focus to the previous enabled trigger.
HomeMoves focus to the first enabled trigger.
EndMoves focus to the last enabled trigger.