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 ​