Modal - National Design System

Focuses the user on a single task or decision by overlaying a dialog that must be addressed before continuing

Modal Dialog

Use when the user needs to confirm an action, acknowledge a warning, or complete a short form before the app can continue

Built-in Features

Auto-initialization

Modals wire up from data-modal-target and data-modal-close attributes with no JavaScript init call required.

Focus Trap

Tab and Shift+Tab cycle through focusable elements inside the modal, keeping keyboard users contained until they dismiss it.

Keyboard Dismissal

Pressing Escape closes the modal and its backdrop without any extra wiring.

Backdrop Overlay

A dimmed, blurred overlay covers the page behind the modal. Clicking the backdrop closes the modal automatically.

Body Scroll Lock

Page scrolling is disabled while a modal is open and restored when it closes.

Mobile Bottom Sheet

On small screens the modal slides up from the bottom with rounded top corners, and action buttons expand to full width.

Animated Transitions

Fade and scale on desktop, slide on mobile. The closing state drives the exit animation automatically.

Programmatic Control

Open, close, and check state with NDS.Modal.open(), NDS.Modal.close(), and NDS.Modal.isOpen().

Usage Guidelines

Best Practices

  • Use a modal when the user must confirm or decide something before the app can continue, such as approving a submission or accepting terms
  • Use for presenting critical warnings, compliance notices, or destructive action confirmations where the user must acknowledge before proceeding
  • Use for short forms or detail views that benefit from focused attention without navigating away from the current page
  • Don't use for success messages or non-blocking notifications. Use an Alert or Toast instead
  • For multi-step workflows inside a modal, use the Stepper component to guide users through each stage. For workflows too complex for a modal, use a dedicated page instead
  • Don't stack modals. If one modal needs to open another, restructure the flow so a single modal handles the decision
  • Choose nds-sm for simple confirmation prompts with one or two buttons. Use the default size when the modal includes a short form or longer description. Use nds-lg for content-heavy modals like terms of service or data previews. Use nds-full for immersive tasks like image editing or document previews
  • Keep titles clear and contextual to the required action
  • Limit content to a single focused message or task
  • Provide explicit primary and secondary actions (Confirm/Cancel)

Modifier Classes

Class Description
nds-sm Small modal, max-width 400px. Use for simple confirmation dialogs.
nds-md Medium modal, max-width 600px. This is the default size when no class is added.
nds-lg Large modal, max-width 800px. Use for content-heavy dialogs.
nds-full Full-width modal with minimal side margins. Use for immersive tasks.

Data Attributes

Attribute Description
data-modal-target="id" Set on a trigger button. Opens the modal with the matching id when clicked.
data-modal-close Set on any element inside a modal. Closes the currently open modal when clicked.

JavaScript API

NDS.Modal initializes automatically on page load. The modal script depends on nds-backdrop.js, which must load first.

// ── Open ──────────────────────────────────────────────── // By ID string NDS.Modal.open('modal-id'); // By DOM element const modalEl = document.getElementById('modal-id'); NDS.Modal.open(modalEl); // ── Close ─────────────────────────────────────────────── // Closes the currently active modal NDS.Modal.close(); // ── Check state ───────────────────────────────────────── NDS.Modal.isOpen(); // returns true if any modal is open // ── Re-initialize ─────────────────────────────────────── // Call after dynamically adding new modal triggers to the page NDS.Modal.init(); // ── Events ────────────────────────────────────────────── // Fired on the modal element, bubbles up to document const modal = document.getElementById('modal-id'); modal.addEventListener('nds-modal-opened', (e) => { // Modal is now visible }); modal.addEventListener('nds-modal-closed', (e) => { // Modal is now hidden, reset forms or clean up modal.querySelector('form')?.reset(); }); // ── Keyboard ──────────────────────────────────────────── // Escape: close modal and backdrop // Tab/Shift+Tab: cycle through focusable elements (focus trap)
Was this page useful?
60% of users said Yes from 2843 Feedbacks