Skip to content
v1.3.1
Fit

Collapsible

A single show/hide region with a trigger — lighter than Accordion when you only need one panel. The region animates open with a grid-template-rows transition (no fixed heights). ghost (bare) or bordered (wrapped in a card), with the chevron at the start or end.

bash
jlds add collapsible

Usage

html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/css/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/css/collapsible.css">
<!-- behavior layer: open/close on click -->
<script src="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/js/core.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/js/collapsible.js" defer></script>

<div class="jl-collapsible jl-collapsible--ghost" data-open="true">
  <button type="button" class="jl-collapsible__trigger" data-chevron="end" aria-expanded="true" aria-controls="c1">
    <span class="jl-collapsible__label">Show advanced options</span>
    <span class="jl-collapsible__chevron"><svg viewBox="0 0 24 24" fill="none"><path d="m9 6 6 6-6 6" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
  </button>
  <div class="jl-collapsible__region" id="c1" role="region">
    <div class="jl-collapsible__content"><div class="jl-collapsible__inner">Hidden content.</div></div>
  </div>
</div>
vue
<script setup lang="ts">
import { Collapsible } from "@/components/ui/collapsible"
</script>

<template>
  <Collapsible trigger="Show advanced options" :default-open="true">
    These settings are hidden by default.
  </Collapsible>
</template>
tsx
import { Collapsible } from "@/components/ui/collapsible"

<Collapsible trigger="Show advanced options" defaultOpen>
  These settings are hidden by default.
</Collapsible>

Bordered

variant="bordered" wraps the region in a surface card. Use chevronPosition="start" to move the chevron before the label, and pass an icon to lead the trigger.

html
<div class="jl-collapsible jl-collapsible--bordered">
  <button type="button" class="jl-collapsible__trigger" data-chevron="start" aria-expanded="false" aria-controls="c2">
    <span class="jl-collapsible__leadicon"><!-- icon svg --></span>
    <span class="jl-collapsible__label">Order details</span>
  </button>
  <div class="jl-collapsible__region" id="c2" role="region">
    <div class="jl-collapsible__content"><div class="jl-collapsible__inner">Panel body.</div></div>
  </div>
</div>
vue
<template>
  <Collapsible variant="bordered" chevron-position="start" trigger="Order details">
    <template #icon><MenuIcon /></template>
    Panel body.
  </Collapsible>
</template>
tsx
<Collapsible variant="bordered" chevronPosition="start" icon={<MenuIcon />} trigger="Order details">
  Panel body.
</Collapsible>

Props

React

Collapsible extends React.HTMLAttributes<HTMLDivElement>.

PropTypeDefaultDescription
triggerReact.ReactNodeHeader label content
iconReact.ReactNodeOptional leading icon
openbooleanControlled open state
defaultOpenbooleanfalseUncontrolled initial state
onOpenChange(open: boolean) => voidFires on toggle
disabledbooleanfalseDisable the trigger
variant"ghost" | "bordered""ghost"Visual treatment
chevronPosition"start" | "end""end"Chevron placement

Vue

Same options. open is a v-model (v-model:open); pass the header via the trigger prop or the trigger slot, and the leading icon via the icon slot. Emits update:open.

CSS classes (HTML)

ClassPurpose
.jl-collapsible + --ghost / --borderedContainer and variant
[data-open="true"]On the container: open state
.jl-collapsible__trigger + [data-chevron="start|end"]The header button and chevron placement
.jl-collapsible__leadicon / __label / __chevronTrigger parts
.jl-collapsible__region / __content / __innerThe animated collapsible panel