Skip to main content
Pricing

Modal

Free

A centered overlay dialog with a backdrop that blocks interaction with the page beneath until dismissed. Contains a close button, title, body content, and footer actions. Used for focused tasks that require the user's full attention — form submissions, record editing, or displaying critical information. Includes a focus trap, scroll lock on the body, and animated entrance. Available in Small, Medium, Large, and Full-screen width variants.

8 variants

Confirm action
Are you sure you want to continue? This action cannot be undone.
Cancel
Confirm

Variants

VariantDescriptionWhen to use
Default
With Form
Confirmation
Full Screen

Usage Guidelines

Do

Trap focus within the modal while it is open.

Don't

Open a modal from within another modal — restructure the flow instead.

Do

Return focus to the trigger element when the modal closes.

Don't

Use modals for non-blocking information — use a Toast or Banner instead.

Do

Provide a visible close button and allow Escape to dismiss.

Don't

Remove the backdrop — users need a clear signal that the page is inactive.

Do

Use a descriptive title that explains the modal's purpose.

Don't

Auto-open modals on page load without user initiation.

Code

HTML
<div class="lex-modal-backdrop" aria-hidden="true"></div>
<div class="lex-modal" role="dialog" aria-modal="true"
  aria-labelledby="modal-title">
  <header class="lex-modal__header">
    <h2 id="modal-title" class="lex-modal__title">Edit profile</h2>
    <button class="lex-icon-button lex-icon-button--tertiary"
      aria-label="Close dialog">
      <svg width="20" height="20" viewBox="0 0 24 24" fill="none"
        stroke="currentColor" stroke-width="2">
        <line x1="18" y1="6" x2="6" y2="18" />
        <line x1="6" y1="6" x2="18" y2="18" />
      </svg>
    </button>
  </header>
  <div class="lex-modal__body">
    <p>Modal body content goes here.</p>
  </div>
  <footer class="lex-modal__footer">
    <button class="lex-button lex-button--secondary lex-button--md">Cancel</button>
    <button class="lex-button lex-button--primary lex-button--md">Save</button>
  </footer>
</div>
CSS Custom Properties
.lex-modal-backdrop {
  position: fixed;
  inset: 0;
  background: var(--lex-modal-backdrop-bg, rgba(0, 0, 0, 0.5));
  z-index: var(--lex-z-modal-backdrop, 999);
}

.lex-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--lex-modal-bg, var(--lex-bg-surface-raised));
  border-radius: var(--lex-modal-radius, var(--lex-radius-xl));
  box-shadow: var(--lex-modal-shadow, var(--lex-shadow-xl));
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  display: flex;
  flex-direction: column;
  z-index: var(--lex-z-modal, 1000);
}

.lex-modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--lex-space-16) var(--lex-space-24);
  border-bottom: 1px solid var(--lex-border-default);
}

.lex-modal__title {
  font-size: var(--lex-font-size-lg);
  font-weight: 600;
  margin: 0;
}

.lex-modal__body {
  padding: var(--lex-space-24);
  overflow-y: auto;
  flex: 1;
}

.lex-modal__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--lex-space-8);
  padding: var(--lex-space-16) var(--lex-space-24);
  border-top: 1px solid var(--lex-border-default);
}
React
// Using Lexicon CSS classes with React

export function EditProfileModal({ open, onClose }: { open: boolean; onClose: () => void }) {
  return (
    <Modal open={open} onClose={onClose} size="md" title="Edit profile">
      <Modal.Body>
        <p>Modal body content goes here.</p>
      </Modal.Body>
      <Modal.Footer>
        <Button variant="secondary" onClick={onClose}>Cancel</Button>
        <Button variant="primary">Save</Button>
      </Modal.Footer>
    </Modal>
  );
}

Design Tokens

TokenValue (dark)Value (light)CSS property
--lex-modal-bgbg
--lex-modal-borderborder
--lex-modal-radiusradius
--lex-modal-shadowshadow
--lex-modal-paddingpadding
--lex-modal-max-widthwidth
--lex-modal-backdropbackdrop

Accessibility

  • Use role="dialog" with aria-modal="true" to indicate a modal context.
  • Focus must be trapped inside the modal — Tab and Shift+Tab cycle within.
  • On open, move focus to the first focusable element or the close button.
  • On close, return focus to the element that triggered the modal.
  • Provide aria-labelledby pointing to the modal title.
  • The backdrop should have aria-hidden="true" — it is decorative.
  • Body scroll must be locked while the modal is open.

Keyboard Interactions

TabMoves focus to the next focusable element within the modal.
Shift + TabMoves focus to the previous focusable element within the modal.
EscapeCloses the modal and returns focus to the trigger.
EnterActivates the focused button or form control.