Starting Point
v0.25.0
Start typing to search...
Go to Page
escClose

Collapsible

The collapsible expands and collapses a single panel of content from a trigger, like extra details behind a "show more". For a set of stacked panels where opening one closes the others, use an accordion.

<div class="flex w-full gap-3">
  <span class="avatar">
    <img
      class="avatar-image"
      src="https://images.unsplash.com/photo-1750390200282-bf7f669a9946?w=640&h=640&fit=facearea&facepad=3.2&auto=format&q=100"
      alt="Leila Navarro"
    />
    <span class="avatar-fallback">LN</span>
  </span>
  <div class="flex min-w-0 flex-col items-start gap-1">
    <p class="text-sm">
      <span class="font-medium">Leila Navarro</span>
      <span class="text-muted-foreground">· 2h</span>
    </p>
    <p class="text-sm">
      Just shipped the new onboarding flow. Huge thanks to everyone who helped
      with the design reviews!
    </p>
    <button
      id="collapsible-demo-trigger"
      class="group btn btn-ghost btn-sm -ml-2.5 text-muted-foreground"
      type="button"
    >
      <svg class="icon-start transition-transform group-aria-expanded:rotate-180" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
      <span class="group-aria-expanded:hidden">Show 2 replies</span>
      <span class="hidden group-aria-expanded:inline">Hide replies</span>
    </button>
    <div class="collapsible" data-sp-toggle="#collapsible-demo-trigger">
      <div class="ml-[3.5px] flex flex-col gap-4 border-l pt-2 pl-4">
        <div class="flex gap-3">
          <span class="avatar">
            <img
              class="avatar-image"
              src="https://images.unsplash.com/photo-1752486268240-0507bb1ebc7e?w=640&h=640&fit=facearea&facepad=2.6&auto=format&q=100"
              alt="Camille Laurent"
            />
            <span class="avatar-fallback">EW</span>
          </span>
          <div class="flex flex-col gap-1">
            <p class="text-sm">
              <span class="font-medium">Camille Laurent</span>
              <span class="text-muted-foreground">· 1h</span>
            </p>
            <p class="text-sm">Congrats! The new flow feels so much faster.</p>
          </div>
        </div>
        <div class="flex gap-3">
          <span class="avatar">
            <img
              class="avatar-image"
              src="https://images.unsplash.com/photo-1750390200293-92d5a788d3a2?w=640&h=640&fit=facearea&facepad=3&auto=format&q=100"
              alt="Theo Marchetti"
            />
            <span class="avatar-fallback">MC</span>
          </span>
          <div class="flex flex-col gap-1">
            <p class="text-sm">
              <span class="font-medium">Theo Marchetti</span>
              <span class="text-muted-foreground">· 45m</span>
            </p>
            <p class="text-sm">Love the progress indicators. Great work!</p>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Usage

Add the collapsible class to the panel and point it at its trigger with data-sp-toggle. The trigger's aria-expanded follows the state, which is the styling hook for rotating chevrons or swapping labels.

ClassDescription
collapsibleAdd to the panel; carries the data-sp-* options. expanded starts it open
collapsible-contentAdd to an inner wrapper for consistent spacing between children
<!-- This is the trigger -->
<button id="my-trigger" class="btn btn-outline">Show more</button>
 
<!-- This is the panel -->
<div class="collapsible" data-sp-toggle="#my-trigger">
  <div class="collapsible-content">Hidden until expanded.</div>
</div>

Examples

Show more

Reveal the rest of a long text in place; the trigger swaps its label against the expanded state.

<div class="flex w-full flex-col items-start gap-2">
  <h4 class="text-sm font-medium">About this project</h4>
  <p class="text-sm text-muted-foreground">
    Starting Point UI is an open-source component library for Tailwind CSS,
    inspired by shadcn/ui. It gives you the same beautiful design system as
    plain HTML classes, so it works in any project.
  </p>
  <div class="collapsible" data-sp-toggle="#collapsible-more-trigger">
    <div class="flex flex-col gap-2 text-sm text-muted-foreground">
      <p>
        Components render correctly from markup and CSS before any JavaScript
        loads, and the optional script layer adds behavior like this collapsible
        on top.
      </p>
      <p>
        Everything is themeable with CSS variables, and plain utility classes
        always win over component styles, so you can override anything inline.
      </p>
    </div>
  </div>
  <button
    id="collapsible-more-trigger"
    class="group btn btn-link btn-sm px-0"
    type="button"
  >
    <span class="group-aria-expanded:hidden">Show more</span>
    <span class="hidden group-aria-expanded:inline">Show less</span>
  </button>
</div>

Initially open

Author the expanded class on the panel to start it open, and mirror aria-expanded="true" onto the trigger so state-driven styling, like a rotated chevron, paints correctly before JavaScript loads.

<nav class="sidebar-menu mx-auto w-full max-w-64">
  <div class="sidebar-menu-item">
    <button
      id="collapsible-open-trigger"
      class="group sidebar-menu-button"
      type="button"
      aria-expanded="true"
    >
      <svg class="transition-transform group-aria-expanded:rotate-90" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>
      <span>Documents</span>
    </button>
    <div
      class="collapsible expanded"
      data-sp-toggle="#collapsible-open-trigger"
    >
      <ul class="sidebar-menu-sub">
        <li class="sidebar-menu-sub-item">
          <button class="sidebar-menu-sub-button w-full" type="button">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/></svg>
            <span>resume.pdf</span>
          </button>
        </li>
        <li class="sidebar-menu-sub-item">
          <button class="sidebar-menu-sub-button w-full" type="button">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/></svg>
            <span>invoice.pdf</span>
          </button>
        </li>
        <li class="sidebar-menu-sub-item">
          <button class="sidebar-menu-sub-button w-full" type="button">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/></svg>
            <span>notes.md</span>
          </button>
        </li>
      </ul>
    </div>
  </div>
</nav>

Nested

Collapsibles nest, so a file tree is triggers and panels all the way down. The sidebar menu classes give it a ready-made look, indent line included.

<nav class="sidebar-menu mx-auto w-full max-w-64">
  <div class="sidebar-menu-item">
    <button
      id="ct-src"
      class="group sidebar-menu-button"
      type="button"
      aria-expanded="true"
    >
      <svg class="transition-transform group-aria-expanded:rotate-90" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>
      <span>src</span>
    </button>
    <div class="collapsible expanded" data-sp-toggle="#ct-src">
      <ul class="sidebar-menu-sub">
        <li class="sidebar-menu-sub-item">
          <button
            id="ct-components"
            class="group sidebar-menu-sub-button w-full"
            type="button"
            aria-expanded="true"
          >
            <svg class="transition-transform group-aria-expanded:rotate-90" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>
            <span>components</span>
          </button>
          <div class="collapsible expanded" data-sp-toggle="#ct-components">
            <ul class="sidebar-menu-sub">
              <li class="sidebar-menu-sub-item">
                <button class="sidebar-menu-sub-button w-full" type="button">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/></svg>
                  <span>button.tsx</span>
                </button>
              </li>
              <li class="sidebar-menu-sub-item">
                <button class="sidebar-menu-sub-button w-full" type="button">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/></svg>
                  <span>card.tsx</span>
                </button>
              </li>
            </ul>
          </div>
        </li>
        <li class="sidebar-menu-sub-item">
          <button class="sidebar-menu-sub-button w-full" type="button">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/></svg>
            <span>index.ts</span>
          </button>
        </li>
      </ul>
    </div>
  </div>
</nav>

Options

Collapsible

Set each option with its own data-sp-* attribute on the collapsible element.

AttributeValuesDefault
data-sp-toggleCSS selector
<div class="collapsible" data-sp-toggle="#my-trigger"></div>

JavaScript

Events

Lifecycle events fire on the panel; before* events are cancelable.

EventWhen
sp-beforeexpandBefore expanding (cancelable)
sp-expandExpanding
sp-expandedExpanded and settled
sp-beforecollapseBefore collapsing (cancelable)
sp-collapseCollapsing
sp-collapsedCollapsed and settled
// Veto collapsing, e.g. while a form inside has unsaved changes.
panel.addEventListener("sp-beforecollapse", (e) => {
  if (unsavedChanges) e.preventDefault();
});

Methods

Get the instance with sp.collapsible(el), then call its methods:

MethodDescription
expand()Expand the panel
collapse()Collapse the panel
toggle()Expand if collapsed, else collapse
const collapsible = sp.collapsible(document.querySelector("#my-panel"));
 
collapsible.expand();