What is it?

It is a transition helper for panels that you want to show or hide.

The package exports three classes. Two main classes: Panel and PanelSet and one additional class PanelControl. Both main classes animate a size between values using an LMAU* cycle:

  • Panel animates height or width
  • PanelSet animates height of a whole set of panels, showing one at a time
  • PanelControl is an addition to PanelSet that handles navigation for tabs

The two main classes use the same animation core and share most config options. Animation is CSS-only: JS just measures and sets pixel values, then lets your browser do the rest with CSS. For async content, you can pass a promise to let the panel wait until it resolves. A spinner kicks in if loading takes longer than what you set it to be. Both also handle focus management.

* LMAU: Lock-Measure-Animate-Unlock (I should trademark the term).


Panel

A single element that can be opened and closed. Use it for sidebars, show-more blocks, accordions, or any element that expands and collapses.

Triggers are any element with aria-controls pointing to the panel’s ID. Or a button with data-panel-trigger if it is a sibling of a panel, which sets up the id and ARIA for you.

You can add a close button that uses data-panel-close.

PanelSet

A container that switches between some panels. Use it for tabs, wizards, or multi-step interfaces. Only one panel is (fully) visible at a time. Height transitions as well.

Triggers are any element with aria-controls pointing to an internal panel’s ID.

It can also be opened and closed as a whole, just like a regular Panel.

Add PanelControl for activation, keyboard navigation, roving tabindex, and state reflection.


Panel example

Key Features

  • This is our most important feature ever!
Panel docs

PanelSet example

Key Features

  • This is our most important feature ever!
PanelSet docs

Features

CSS-driven animation

JS measures and sets a pixel value. After that, the browser animates. No JS animation loop.

Async content

Pass a promise to onBeforeOpen() and the panel waits. A spinner appears if loading takes longer than loadingDelay ms.

Interrupt-safe

Click again mid-animation and it reverses from exactly where it is. No jumping.

Accessible

aria-expanded, aria-controls, aria-selected (or aria-current) and inert are managed for you. Opt into autoFocus and returnFocus for focus handling.

Persist & deep-link

persist: true saves the active panel to localStorage so it survives reloads. deepLink: true reflects it in the ?panel= URL for shareable links. And a ?panel= link is always honoured on load.

Reduced motion

Respects prefers-reduced-motion. Users who prefer less motion get instant state changes instead.

Setup

Installing Panel, PanelSet and PanelControl

Terminal
npm install panelset

Using Panel, PanelSet and PanelControl


Support this project

PanelSet is free. If it saved you time, a coffee goes a long way.

Support my work on Ko-fi