Build Assets
UI Component
Modal / Dialog
Focused overlay for decisions, confirmations, or short forms that require immediate attention.
Platform: bothTags: modal, dialog, overlay, focus
Purpose
Interrupts the current flow to require a focused decision or acknowledgement. Use sparingly — only for actions that cannot safely proceed without explicit user intent.
Anatomy
- Scrim: full-screen semi-transparent overlay (rgba 40% opacity)
- Container: white card, centered, max-width 480–560px, 16px corner radius
- Header: title (16–18px semibold) + optional close (×) button top-right
- Body: content area, 24px padding, max-height with scroll for long content
- Footer: action buttons right-aligned — primary action rightmost
States
- Open: scrim visible, container centered with entrance animation
- Closing: fade out scrim + slide down container (150ms)
- Scroll: body scrolls independently when content exceeds max-height
- Loading: primary action button shows loading state after submission
Tokens
spacing
padding: 24pxfooterGap: 8pxheaderBottomGap: 16px
sizing
maxWidth: 560pxminWidth: 320pxborderRadius: 16px
Do
- Trap focus inside the modal while open (Tab cycles within)
- Close on Escape key and scrim click (unless destructive action)
- Keep to a single decision or task — no multi-step wizards in modals
- Label the primary action with a verb ("Delete", "Save", "Confirm")
Don't
- Open a modal from inside a modal (nested modals)
- Use for informational content that doesn't require action
- Use for multi-step flows — use a dedicated page instead
- Use "OK" or "Yes" as button labels — use the action verb
Accessibility
- role="dialog" with aria-modal="true" and aria-labelledby pointing to title
- Focus moves to first focusable element on open
- Focus returns to trigger element on close
- Escape key closes the dialog
- Scrim must not be focusable
Code Example
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Delete item?</h2>
<p>This action cannot be undone.</p>
<div class="dialog-footer">
<button type="button">Cancel</button>
<button type="button" class="btn-danger">Delete</button>
</div>
</div>