How it works

Both components determine the initial state on every page load from three sources, checked in order:

  1. URL param (?panel=): Reading of the URL parameter will always be done, no config needed. See below.
  2. localStorage: used when persist is enabled and no URL param applied. See Persisting state.
  3. HTML default: the markup state like class="active" panel (PanelSet) or class="is-open" (Panel / closable PanelSet).

On this page we delve a bit deeper into deep linking.

Reading the URL parameter

Reading is always on. Panel and PanelSet always check the ?panel= param on load. Any link with ?panel=id opens that panel. For example, a link like index.html?panel=demo-panel-4 opens the panel with ID demo-panel-4 on page load. Note that we do not use just a hash, because that is much more limited. The ?panel= param is designed to be purely for panel state.

The ?panel= query param is a comma-separated list of panel IDs.

HTML
<!-- Open or activate a single panel -->
<a href="?panel=pricing">Link to Pricing</a>

<!-- Open or activate multiple panels at once -->
<a href="?panel=pricing,sidebar">Pricing tab open, sidebar open</a>

Because HTML IDs are (or at least should be) unique per HTML page, a panel ID always points to exactly one element, whether it is a standalone [data-panel] collapse or a [role="tabpanel"] inside a PanelSet.

Writing the URL parameter

Writing is optional. By default the URL is never modified.

However, you can enable deepLink to keep the URL in sync as the user opens and closes panels, so the current state can be shared from the address bar. When any panel (Panel or PanelSet) opens, its ID is added to ?panel=. When it closes, the ID is removed. Multiple open panels accumulate naturally: ?panel=faq-1,faq-2.

Deep linking in Panel

Key Features

  • This is our most important feature ever!
JavaScript
new Panel('#sidebar', { deepLink: true });
// or via init
Panel.init({ deepLink: true });
HTML
<div id="sidebar" data-panel data-panel-deeplink>...</div>

Deep linking in PanelSet

Key Features

  • This is our most important feature ever!
JavaScript
new PanelSet('#example', { deepLink: true });
// or via init
PanelSet.init({ deepLink: true });
HTML
<div id="example" data-panelset data-panel-deeplink>...</div>

Note: a PanelSet uses data-panel-deeplink (the panel- prefix), not data-panelset-deeplink. The deep-link attributes are shared with Panel, so the prefix stays panel- on both.

Play around with the two examples above, and reload the page. Both come back in the same state you left them. You can also send the link to a friend and the same panels will be opened.

For accordion groups, set data-panel-deeplink on the [data-panel-group] element to cover all panels in the group at once.

Suppressing transitions on load

When a panel opens from a URL parameter, it snaps open instantly without an animation. Any CSS transitions on the surrounding elements (such as a chevron rotation or a border color change) would still play, which can look wrong on a cold page load.

Panel adds an is-restored class to the element for exactly one paint cycle when this happens. You can use it to suppress those transitions:

CSS
:has(> [data-panel].is-restored),
:has(> [data-panel].is-restored) * {
	transition: none !important;
}

The direct-child combinator (>) keeps the selector scoped to the immediate parent of the panel, so only that container and its contents are affected. The class is removed after the first paint, so all subsequent opens and closes animate normally.

Mixed components

Because the param is a flat list of IDs, PanelSet panels and standalone panels can coexist in the same URL without any special syntax.

HTML
<!-- Activate the Pricing tab and open the sidebar in one link -->
<a href="?panel=pricing,sidebar">View pricing with sidebar open</a>