Skip to main content
Urbicon UI

Collapsible

A single expand/collapse panel with smooth animation, a default or custom trigger, and full ARIA support. The low-level primitive behind Accordion — use it standalone for simple show/hide patterns or as a building block for compound components.

Playground

Design tokens are named values — colors, spacing, radii — that form the single source of truth for your design system. They bridge the gap between design tools and code.

Variant Style variant (tailwind-variants)
Size Style variant (tailwind-variants)
<Collapsible>
  <p class="text-text-secondary text-sm">
    Design tokens are named values — colors, spacing, radii — that form the single source of
    truth for your design system. They bridge the gap between design tools and code.
  </p>
</Collapsible>

01 Examples

FAQ item

Stack multiple independent Collapsibles to build a FAQ list. Each panel opens and closes on its own — for single-open-at-a-time coordination, reach for Accordion instead.

Named values — colors, spacing, radii — that form the single source of truth for your design system. They bridge the gap between design tools and code.

No. Every component ships with sensible defaults. Tailwind helps when you want to override styles via slotClasses, but it's optional.

Yes. The components are SSR-safe and hydrate without layout shift. See the SvelteKit adapter docs for setup details.

<div class="flex w-full max-w-lg flex-col gap-3">
  <Collapsible variant="card" title="What are design tokens?" defaultOpen>
    <p class="text-text-secondary text-sm leading-relaxed">
      Named values — colors, spacing, radii — that form the single source of truth for your
      design system. They bridge the gap between design tools and code.
    </p>
  </Collapsible>
  <Collapsible variant="card" title="Do I need to learn Tailwind?">
    <p class="text-text-secondary text-sm leading-relaxed">
      No. Every component ships with sensible defaults. Tailwind helps when you want to
      override styles via <code class="bg-surface-base rounded px-1.5 py-0.5 text-xs"
        >slotClasses</code
      >, but it's optional.
    </p>
  </Collapsible>
  <Collapsible variant="card" title="Can I use this with SvelteKit?">
    <p class="text-text-secondary text-sm leading-relaxed">
      Yes. The components are SSR-safe and hydrate without layout shift. See the SvelteKit
      adapter docs for setup details.
    </p>
  </Collapsible>
</div>

Controlled section toggle

Drive the open state from outside via bind:open — useful for filter panels, settings sections, or any UI that needs to coordinate state with the rest of the page.
closed

This panel is controlled via bind:open. Toggle it with the button above or by clicking the trigger.

<div class="flex w-full max-w-lg flex-col gap-4">
  <div class="flex items-center gap-3">
    <Button size="sm" variant="outlined" onclick={() => (controlledOpen = !controlledOpen)}>
      {controlledOpen ? 'Hide filters' : 'Show filters'}
    </Button>
    <Badge size="xs" intent={controlledOpen ? 'success' : 'neutral'} variant="soft">
      {controlledOpen ? 'open' : 'closed'}
    </Badge>
  </div>
  <Collapsible variant="card" bind:open={controlledOpen} title="Advanced filters">
    <p class="text-text-secondary text-sm">
      This panel is controlled via <code class="bg-surface-base rounded px-1.5 py-0.5 text-xs"
        >bind:open</code
      >. Toggle it with the button above or by clicking the trigger.
    </p>
  </Collapsible>
</div>

Release-notes item with custom trigger

Replace the default trigger via the trigger snippet to surface rich metadata — icons, badges, secondary text — while keeping the expand/collapse mechanics.

Features: New Collapsible component, improved Accordion internals, Stepper navigation.

Fixes: Dialog focus trap on Safari, Tooltip positioning near edges.

<div class="w-full max-w-lg">
  <Collapsible variant="card" defaultOpen>
    {#snippet trigger({
      open,
      toggle
    }: {
      open: boolean;
      toggle: () => void;
      disabled: boolean;
      triggerId: string;
      contentId: string;
    })}
      <button
        onclick={toggle}
        class="hover:bg-surface-hover flex w-full items-center gap-3 px-4 py-3 text-left transition-colors"
      >
        <div
          class="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg"
        >
          <ClipboardListIcon size={16} />
        </div>
        <div class="flex-1">
          <p class="text-text-primary text-sm font-semibold">Release Notes v3.2</p>
          <p class="text-text-tertiary text-xs">3 new features, 2 bug fixes</p>
        </div>
        <Badge size="xs" intent={open ? 'primary' : 'neutral'} variant="soft">
          {open ? 'Expanded' : 'Collapsed'}
        </Badge>
      </button>
    {/snippet}
    <div class="space-y-2 px-4 pb-4">
      <p class="text-text-secondary text-sm">
        <strong>Features:</strong> New Collapsible component, improved Accordion internals, Stepper
        navigation.
      </p>
      <p class="text-text-secondary text-sm">
        <strong>Fixes:</strong> Dialog focus trap on Safari, Tooltip positioning near edges.
      </p>
    </div>
  </Collapsible>
</div>

02 Customization

slotClasses Override

Restyle individual slots — trigger background, content padding, chevron color.
Override individual slots without touching the component source. The card gets a primary-tinted border, the trigger uses a semibold font, and the chevron matches the primary intent.
<div class="w-full max-w-lg">
  <Collapsible
    variant="card"
    defaultOpen
    title="Custom styled"
    slotClasses={{
      base: 'border-primary/20',
      trigger: 'hover:text-primary font-semibold',
      chevron: 'text-primary',
      contentInner: 'text-text-secondary text-sm leading-relaxed'
    }}
  >
    Override individual slots without touching the component source. The card gets a
    primary-tinted border, the trigger uses a semibold font, and the chevron matches the
    primary intent.
  </Collapsible>
</div>

Glassmorphism (unstyled)

Fully custom frosted-glass panel built entirely with class overrides.
The component strips all defaults in unstyled mode. Every visual detail is hand-crafted through class props — background, blur, text color, spacing, and border radius.
<div class="w-full max-w-lg">
  <Collapsible
    unstyled
    defaultOpen
    title="Frosted Glass"
    class="overflow-hidden rounded-xl"
    slotClasses={{
      trigger:
        'flex w-full items-center justify-between rounded-t-xl bg-white/15 px-5 py-3.5 text-sm font-medium text-white backdrop-blur-md transition-colors hover:bg-white/20',
      chevron: 'size-4 text-white/60 transition-transform duration-200',
      contentInner: 'bg-white/10 px-5 py-4 text-sm text-white/80 backdrop-blur-sm'
    }}
  >
    The component strips all defaults in unstyled mode. Every visual detail is hand-crafted
    through class props — background, blur, text color, spacing, and border radius.
  </Collapsible>
</div>

Terminal (unstyled)

Monospace hacker aesthetic built entirely with class overrides.

NODE_ENV=production

PORT=3000

LOG_LEVEL=info

Process exited with code 0

<div class="w-full max-w-lg">
  <Collapsible
    unstyled
    defaultOpen
    title="$ cat /etc/config"
    class="font-mono"
    slotClasses={{
      trigger:
        'flex w-full items-center justify-between py-3 text-xs text-emerald-300 transition-colors hover:text-emerald-400',
      chevron: 'size-3.5 text-emerald-400 transition-transform duration-200',
      contentInner: 'pb-3 text-xs leading-relaxed text-emerald-200'
    }}
  >
    <p>NODE_ENV=production</p>
    <p>PORT=3000</p>
    <p>LOG_LEVEL=info</p>
    <p class="mt-2 text-emerald-300">Process exited with code 0</p>
  </Collapsible>
</div>

Panel skins that recur (glass, terminal) belong in a BlocksProvider preset (presets.Collapsible), applied per instance via preset — see Customization.

03 Accessibility

Built-in ARIA

The default trigger uses aria-expanded and aria-controls to link to the content panel. The content panel has role="region" with aria-labelledby pointing back to the trigger. The data-state attribute exposes open / closed for CSS-only styling.

Keyboard Navigation

Tab moves focus to the trigger. Enter / Space toggle the content. Focus rings use focus-visible: so they only appear on keyboard navigation.

Custom Triggers

When using the trigger snippet, the component passes triggerId and contentId so you can wire up aria-expanded and aria-controls yourself. The content region always gets the correct aria-labelledby.

Reduced Motion

The expand/collapse animation uses CSS grid-template-rows transitions. When prefers-reduced-motion is enabled, transition durations are reduced via the design token system.

04 API Reference

18 props
18 props 1 required
Prop
Type
Default
Description

05 Types

Local type definitions used by this component.

3 types
Name
Kind
Category
Used by
Description

06 Installation

Import

import { Collapsible } from '@urbicon-ui/blocks';