v1.6.x-dev NDS IQ v6

Panels - National Design System

A content-agnostic surface that slides in from any edge of the viewport, for settings, filters, details, or any secondary content you want to reveal on demand without leaving the page.

Slide-in Panel

A trigger opens the panel from the chosen edge. Change the side, then add a backdrop with Modal or block dismissal with Static. Panel content is yours: header, body, and footer are all optional.

<button class="nds-btn nds-primary nds-lg" data-panel-toggle="settings-panel"> <span class="nds-label">Open Panel</span> </button> <aside id="settings-panel" class="nds-panel" data-panel-side="end" aria-label="Settings panel" hidden> <div class="nds-panel-header"> <span class="nds-featured-icon nds-circle"> <i class="hgi hgi-stroke hgi-stars"></i> </span> <div class="nds-panel-text"> <span class="nds-panel-title">Settings</span> <p class="nds-panel-description">Adjust how this page behaves.</p> </div> <button class="nds-btn nds-subtle nds-icon-only" type="button" data-panel-close aria-label="Close panel"> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> </div> <div class="nds-panel-body"> <p>Panel content lives here.</p> </div> <div class="nds-panel-footer"> <button class="nds-btn nds-primary nds-full" type="button" data-panel-close> <span class="nds-label">Done</span> </button> </div> </aside>

Sheets

A full-width surface that slides from the top or bottom edge. It fits its content up to --panel-height, and a tall sheet is capped so it never covers the sticky header.

<button class="nds-btn nds-primary nds-lg" data-panel-toggle="details-sheet"> <span class="nds-label">Open Sheet</span> </button> <aside id="details-sheet" class="nds-panel" data-panel-side="bottom" aria-label="Details sheet" hidden> <div class="nds-panel-header"> <span class="nds-featured-icon nds-circle"> <i class="hgi hgi-stroke hgi-stars"></i> </span> <div class="nds-panel-text"> <span class="nds-panel-title">Details</span> <p class="nds-panel-description">A closer look at the selected item.</p> </div> <button class="nds-btn nds-subtle nds-icon-only" type="button" data-panel-close aria-label="Close sheet"> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> </div> <div class="nds-panel-body"> <p>A bottom sheet is a good fit for mobile actions or a compact form.</p> </div> </aside>

Built-in Features

Auto-initialization

Activates when a .nds-panel is on the page. Toggle and close buttons wire up from data attributes with no init call.

Six Slide Directions

Slides in from the inline start or end, the physical left or right, or as a full-width top or bottom sheet.

Optional Modal Mode

Add data-panel-modal for a dimming backdrop and scroll lock; leave it off and the page behind stays fully interactive.

Focus Management

Focus moves into the panel on open and returns to the trigger on close. Escape and outside clicks dismiss it unless data-panel-static is set.

One Panel at a Time

Opening a panel closes any other first and waits for it to finish sliding out, so two panels never overlap.

Header-Aware Positioning

Side panels sit below the sticky header and track it as the topbar scrolls in and out; tall sheets cap their height to match.

Direction-Aware

Logical start and end sides flip with text direction in RTL and LTR; physical left and right stay on the same edge.

Programmatic Control

Open, close, toggle, and query any panel through the JS API, and react to the opened and closed events it dispatches.

Usage Guidelines

Best Practices

  • Use a panel for secondary content the user reveals on demand: settings, filters, a details view, or a form that supports the main task without replacing it
  • Keep the default non-modal (no data-panel-modal) when users benefit from seeing the panel affect live content, such as filter or accessibility controls
  • Add data-panel-modal when the panel demands a decision and the page behind it should be inert, for example a checkout step or a destructive confirmation with detail
  • Do not use a panel for a short blocking confirmation with no extra content. Use a Modal instead
  • Do not use a panel for primary site navigation. Use the Drawer or Sidemenu
  • Prefer logical start and end sides so the panel follows reading direction. Reserve left and right for cases that must anchor to a physical edge regardless of language
  • Choose a bottom sheet for mobile-first actions and a side panel for desktop-oriented settings or filters
  • Add data-panel-static to a panel holding an unsaved form so a stray Escape or outside click cannot discard the user's work
  • Header, body, and footer are all optional. Drop content straight into .nds-panel-body, or swap it for an Scroll More wrapper for long scrolling content with fade edges
  • Keep the header's title inside .nds-panel-text, the same title-plus-description pairing Cards use. Add a .nds-panel-description beside the title when the panel needs a subtitle, and drop the wrapper only for a bare title: it still fills the row on its own, so a leading icon and the close button stay where they are
  • Set --panel-content-width: 100% on a sheet whose content should span the full width (a media row, a wide table) instead of capping to the page max-width
  • Ship the panel with the hidden attribute so it never flashes before the script loads

Data Attributes

AttributeDescription
data-panel-sideSet on .nds-panel to pick the edge. Values: end (default) or start (logical, flip with direction); left or right (physical); top or bottom (full-width sheet)
data-panel-toggleSet on any button, anywhere on the page, with the target panel's id as its value. Clicking it opens or closes that panel
data-panel-closeSet on any element inside the panel to make it a close control
data-panel-modalPresent on .nds-panel to add a dimming backdrop, a scroll lock, and a focus trap. Omit for a non-blocking disclosure
data-panel-staticPresent on .nds-panel to disable both Escape and outside-click dismissal. A close button and a swap to another panel still work

CSS Custom Properties

PropertyDefaultDescription
--panel-widthmin(420px, 100vw)Width of an inline side panel (start, end, left, right)
--panel-height60svhMaximum height of a top or bottom sheet. The sheet fits its content up to this, and is capped so it never covers the header
--panel-content-width--nds-content-MaxWidthMax width of a top or bottom sheet's content, centered within the full-bleed sheet and aligned to the page gutter on narrower screens. Set to 100% for full-width content
--panel-topsticky header bottomBlock-start offset. Set it to override the automatic header tracking with a fixed value
--panel-padding--spacing-lgPadding of the header, body, and footer
--panel-gap0Gap between the panel's header, body and footer
--panel-radius0, --radius-lg for sheetsCorner radius of the panel surface. Top and bottom sheets round the two corners facing the page at the modal radius
--panel-z999Stacking order of the panel

JavaScript API

The NDS.Panel API opens, closes, and toggles panels programmatically. Every method accepts a panel element or its id string. Toggle and close buttons work from markup alone, so most pages never touch this API.

// ── Open / close / toggle ──────────────────────────── // Accepts a panel element or its id string NDS.Panel.open('settings-panel'); NDS.Panel.close('settings-panel'); NDS.Panel.toggle('settings-panel'); // ── Query ──────────────────────────────────────────── NDS.Panel.isOpen('settings-panel'); // → true | false // ── Events (bubble from the panel element) ─────────── const panel = document.getElementById('settings-panel'); panel.addEventListener('nds:panel:opened', (e) => { console.log('opened', e.detail.panel); }); panel.addEventListener('nds:panel:closed', (e) => { console.log('closed', e.detail.panel); }); // During a swap, the outgoing panel's closed event fires // before the incoming panel's opened event. // ── Lifecycle (for dynamically added panels) ───────── NDS.Panel.create(panel); // wire up a single panel element NDS.Panel.init(); // scan and wire every .nds-panel NDS.Panel.destroy(panel); // tear a panel's listeners down
Last Modified Date: 26/07/2026 - 09:40 PM
Was this page useful?
60% of users said Yes from 2843 Feedbacks