@magic-spells/collapsible-content

Collapse with
elegance

An accessible collapsible & accordion web component with smooth height animation, ARIA support, and CSS-variable theming. No Shadow DOM — style it directly.

Install npm i @magic-spells/collapsible-content
Size (gzip) ~1.8 KB

Markup

Drop-in HTML structure. A button triggers the collapsible panel — all ARIA wiring is automatic.

<collapsible-component> <button type="button">Section Title</button> <collapsible-content> <div class="content-wrapper"> <p>Your content here...</p> </div> </collapsible-content> </collapsible-component> <!-- Start open with the open attribute --> <collapsible-content open>...</collapsible-content> <!-- Custom animation speed (px/sec) --> <collapsible-content speed="800">...</collapsible-content>

Accordion

Animation duration is calculated dynamically from content height using a speed attribute (default: 900 px/sec). Short panels animate quickly, tall panels take proportionally longer — no fixed duration that feels too slow or too fast.

The native <details> element snaps open instantly with no animation. This component provides smooth height transitions, proper ARIA attributes, inert management for hidden content, and programmatic control via the .collapsed property.

Yes. The component automatically manages aria-expanded on the button, aria-hidden and inert on the content panel, and links the two with aria-controls and aria-labelledby. Focus is trapped out of collapsed content.

Absolutely. Each <collapsible-component> operates independently. Nest them as deeply as you need — the height animation recalculates correctly for all levels.

There's no Shadow DOM, so you style everything with plain CSS. Customize animation timing with --collapsible-duration and --collapsible-easing. Style buttons, content, and layout with any selectors you like.

Accordion Group

Add a group attribute to <collapsible-component> to create accordion behavior — opening one item automatically closes others in the same group.

The group attribute links collapsible components together. When one opens, all others in the same group close automatically — classic accordion behavior.

Yes. Clicking the open item closes it without forcing another open. All items in a group can be collapsed at the same time.

The closing and opening animations run simultaneously. Try clicking between items — even mid-animation, everything stays smooth.

<collapsible-component group="faq"> <button type="button">Question One</button> <collapsible-content>...</collapsible-content> </collapsible-component> <collapsible-component group="faq"> <button type="button">Question Two</button> <collapsible-content>...</collapsible-content> </collapsible-component>

Initially Open

Add the open attribute to <collapsible-content> to start expanded.

Include the CSS and JS, then drop the markup into your page. The component registers itself automatically — no initialization code required. This section starts open by default.

Control state programmatically with the .collapsed setter. Read the current state with the .collapsed getter. Listen for the collapsible-error event if child elements are missing.

Custom Easing

Override --collapsible-easing for different animation feels.

A longer duration with a spring overshoot curve. Great for playful interfaces where motion adds personality.

A lower min-duration of 150ms lets short panels snap open faster than the default 250ms clamp. Paired with ease-out for a crisp feel.

The built-in default. A balanced ease curve that works well for most use cases.

A slow spring on a tall panel really shows off the animation curve. Watch the overshoot as it settles into place.

The spring easing overshoots its target height briefly before easing back — the taller the content, the more dramatic the effect. This is useful for reveal moments where you want the user to notice the motion.

  • Easing: cubic-bezier(0.34, 1.56, 0.64, 1)
  • Content height drives the visual impact
  • Works well for onboarding flows, feature tours, and settings panels

Pair a longer duration with generous content to create an intentional, cinematic feel. For shorter content, consider dialing the duration back to avoid the animation feeling sluggish.

Dynamic Speed

The speed attribute sets animation speed in px/sec. Duration scales with content height so short and tall panels feel proportional. Default is 900.

A short paragraph. The animation is quick because there's less distance to cover.

This panel has a lot more content, so the animation takes proportionally longer. The speed stays consistent — only the duration changes.

  • Duration scales linearly with content height
  • Short panels feel snappy
  • Tall panels feel smooth, not sluggish
  • Mid-animation reversals stay proportional
  • Duration is clamped between 250ms and 1s

Try clicking the button rapidly to see mid-animation reversal. The component captures the current in-progress height and calculates a new duration for the remaining distance.

The clamping ensures that very short panels don't animate so fast they feel instant, and very tall panels don't drag on forever. This creates a consistent feel regardless of content size.

  • Minimum duration: 250ms — keeps short panels visible during transition
  • Maximum duration: 1s — prevents tall panels from feeling sluggish
  • Everything in between scales linearly with height

With min-duration="0.15", the minimum clamp drops to 150ms so short panels animate faster. The ease-out curve gives a crisp, snappy feel.

With speed="80", the animation moves at 80px/sec. Useful for dramatic reveals or onboarding flows.

<collapsible-content> <!-- default: 900px/sec --> </collapsible-content> <collapsible-content speed="1200"> <!-- faster: 1200px/sec --> </collapsible-content> <collapsible-content speed="80"> <!-- slower: 80px/sec --> </collapsible-content>

Rich Content

Collapsible panels handle any content — lists, code, nested elements. Height recalculates automatically.

Package Manager

Install via npm and import into your build:

  • npm i @magic-spells/collapsible-content
  • Import the module and CSS in your entry file
  • The component self-registers on import

Available Properties

  • --collapsible-duration — animation length (dynamic, based on content height)
  • --collapsible-easing — timing function (default: ease-out)

Set these on the <collapsible-content> element or any ancestor. The component also respects prefers-reduced-motion — transitions are disabled automatically when the user prefers reduced motion.

Components can be nested. Each instance operates independently:

This is a nested collapsible. The parent panel's height adjusts automatically when inner panels open or close.

Try opening both inner sections while the parent is open. The animation stays smooth at every level.

Keyboard & Accessibility

Full keyboard support and ARIA attributes are managed automatically.

Tab Focus trigger button
Enter Toggle panel
Space Toggle panel
aria-expanded Set on button
aria-controls Links button to panel
aria-labelledby Labels panel by button
aria-hidden Hides collapsed content
inert Prevents focus in hidden panels
focus-visible Keyboard-only outline