Skip to content

Modal

Overlay dialog that forces user focus on specific content while the rest of the page is deactivated. Use for confirmations, destructive actions, or critical decisions.


When to use

Use a modal when the user must see and respond to specific content before continuing. Examples: confirm deletion, accept terms, approve permissions.

Do not use for page content, long text, multi-field forms, live updates, or as a default pattern. Avoid on mobile. Do not auto-open (except session timeout).


Preview


Variants

Standard

Can be closed via Escape, backdrop click, or close button.

html
<div class="modal" id="my-modal" aria-hidden="true" style="display: none">
  <div class="modal__dialog" role="dialog" aria-modal="true" aria-labelledby="my-modal-title">
    <div class="modal__header">
      <h2 class="modal__title" id="my-modal-title">Bekræft handling</h2>
      <button class="modal__close" aria-label="Luk">&times;</button>
    </div>
    <div class="modal__body">
      <p>Er du sikker på at du vil fortsætte?</p>
    </div>
    <div class="modal__footer">
      <button class="gt-btn gt-btn--outlined gt-btn--sm">Annuller</button>
      <button class="gt-btn gt-btn--primary gt-btn--sm">Bekræft</button>
    </div>
  </div>
</div>

<!-- Trigger -->
<button data-modal-target="#my-modal" class="gt-btn gt-btn--primary gt-btn--sm">
  Åbn modal
</button>

Persistent (kræver handling)

Cannot be closed except via action buttons. Add data-persistent attribute.

html
<div class="modal modal--persistent" data-persistent id="confirm-delete"
  aria-hidden="true" style="display: none">
  <div class="modal__dialog" role="dialog" aria-modal="true"
    aria-labelledby="delete-title">
    <div class="modal__header">
      <h2 class="modal__title" id="delete-title">Slet konto?</h2>
    </div>
    <div class="modal__body">
      <p>Denne handling kan ikke fortrydes.</p>
    </div>
    <div class="modal__footer">
      <button class="gt-btn gt-btn--outlined gt-btn--sm">Annuller</button>
      <button class="gt-btn gt-btn--negative gt-btn--sm">Slet</button>
    </div>
  </div>
</div>

Transitions

Set data-transition on .modal: scale (default), fade, slide-up, slide-down, slide-right, none.


Do's and don'ts

Do
Use for critical confirmations and destructive actions
Do
Keep content short and concise — no scrolling
Do
Use meaningful titles and clear action buttons
Do
Let user see background behind modal (context)
Don't
Use for page content or long text
Don't
Use forms with multiple fields inside modal
Don't
Have multiple open modals simultaneously
Don't
Auto-open modals (except session timeout)
Don't
Use on mobile viewports

Classes

ClassDescription
.modalFixed backdrop overlay
.modal__dialogCentered dialog container
.modal__headerTitle + close button row
.modal__titleHeading text
.modal__closeClose button (hidden in persistent)
.modal__bodyContent area
.modal__footerAction buttons area
.modal--persistentHides close button, blocks escape/backdrop close
.modal--enteringEnter animation state
.modal--leavingExit animation state

Accessibility

  • role="dialog" and aria-modal="true" on the dialog
  • aria-labelledby points to the title element
  • Focus trapped inside dialog (Tab/Shift+Tab cycles)
  • Escape closes the modal (standard variant)
  • Focus restored to trigger element on close
  • Body scroll locked while open

JavaScript behavior

Initialize with Grundtone.initAll() or new Modal(element).

Events: gt:modal-open, gt:modal-close (cancelable).

Trigger buttons use data-modal-target="#modal-id".


References