A modal is the most disruptive notification. It blocks the page until the user makes a decision, so use it only when the user must confirm or choose before going on.
Tokens, sizes, measurements and requirements. Values come from the live token package and from E-Com Modal.
Resolved from the live token package.
| Part | Role | Token | Value |
|---|
Two sizes to choose between, Small and Large (see Sizes on Examples & Guidelines). The modal has three setups, one per Figma breakpoint group. Tablet (sm) is not the desktop setup and not the mobile one: it has the narrower Large (576px) and the mobile type, but still a centered card.
| Desktop (769px+) | Tablet, sm (640–768px) | Mobile, xs (below 640px) | |
|---|---|---|---|
| Figma breakpoint | XLarge-Large-Medium | Small | XSmall (Large and Small only) |
| Small | 440px wide, centered | 440px wide (adapts to the available width), centered | Centered card, height by content, at least 16px side margin: max width 440px; 343px on a 375px viewport |
| Large | 705px wide, centered | 576px wide (adapts to the available width), centered | Fills the whole screen (375 × 100dvh): content on top, buttons pinned to the bottom |
| Height | By content, limited by the available screen height | By content, limited by the available screen height | Small: by content, limited by the screen height. Large: 100dvh |
| Padding | 24px | 24px | 16px |
| Type | Desktop sizes (label 14, title 20, body 17) | Mobile sizes (label 12, title 18, body 16) | Mobile sizes |
| Space above the buttons | 32px | 24px | 24px |
| Overlay | Dims the page | Dims the page | Small: dims the page. Large: covers it completely |
The panel is never wider than the window: max-width: calc(100vw - 32px) and max-height: calc(100dvh - 32px); longer content scrolls inside the panel. On mobile Small keeps 16px to the edges (max-width: calc(100vw - 32px), 343px on a 375px viewport). Large is top: 0; left: 0; width: 100%; height: 100dvh; max-width: none, so it has no margin to the screen.
The padding of the modal panel. The numbers are read from the component.
| Attribute | Desktop (769px+) and tablet | Mobile (below 640px) |
|---|---|---|
| Padding | 24px | 16px |
| Space above the buttons | 32px (tablet: 24px) | 24px |
| Header gap, label to title | 8px | 8px |
| Title to body | 8px (desktop 8px) | 8px |
| Gap between the buttons | 8px | 8px |
| Buttons | 32px high | 32px high |
The same distances as spacing tokens.
| Part | Value |
|---|---|
| Padding, tablet and desktop | space-24 |
| Padding, mobile | space-16 |
| Above the buttons, desktop | space-32 |
| Above the buttons, mobile | space-24 |
| Label, title and body gaps | space-8 |
| Between the buttons | space-8 |
| Header to × | space-12 |
| Part | Token | Value |
|---|---|---|
| Panel | shadow-elevation-b-100 | The highest bottom shadow. |
| Overlay | surface-opacity-black-20 | Dims the page behind the panel. Z-index 9998; the panel is 9999. |
| Element | Desktop (769px+) | Tablet and mobile |
|---|---|---|
| Label | alt-label-sm 14/14, 0.56px, 500, uppercase | 12/12, 0.48px |
| Title | title-md 20/24, bold | 18/22, bold |
| Body | body-md 17/24, 0.32px | 16/22, 0.32px |
| Buttons | 32px high, label-sm 14px, 0.56px | 32px high, 14px, 0.42px |
| Space above buttons | 32px | 24px |
| Gap label to title, header to body | 8px | 8px |
| Element | Class | Notes |
|---|---|---|
| Overlay | modal-overlay | Fixed, covers the page. |
| Panel | modal modal--small|large | role="dialog", aria-modal="true", aria-labelledby. |
| Header | modal__header, modal__header-text | Label, title and the ×. |
| Label, title, body | modal__label, modal__title, modal__body | The label is optional. |
| Footer | modal__footer | Blank Cancel and Primary buttons. |
| Close | icon-btn icon-btn--close | Always present. |
Two sizes: Small and Large. With or without a label. No status and no emphasis.
See Sizes above.
| Motion | Property | Duration | Easing |
|---|---|---|---|
| Enter | Overlay: opacity. Panel: opacity and translate from 48px below (space-48) to 0 | duration-fast-4 (200ms), --modal-in-duration | ease-decelerate-emphasized, --modal-in-ease |
| Exit | Overlay: opacity. Panel: opacity and translate from 0 to 48px below (space-48) | duration-fast-3 (150ms), --modal-out-duration | ease-accelerate-generic, --modal-out-ease |
A short move (48px) with the fade, no scale. Why 48px, and why no token: the move is short (a small share of the view), so it uses the Fast durations of eco-motion; 48px is a trial (24px was the first value): it is a clear lift, and still a short move, so the Fast durations apply. The value is space-48, an existing spacing token, written in the keyframes. There is no distance token and no custom property for it: Carbon, Adobe Spectrum and Atlassian keep duration and easing as the motion tokens and leave the travel distance to the component (Atlassian puts it inside a per-component token such as motion.modal.enterModal). Add a shared distance scale only if a second component needs the same move. The panel uses translate, not transform, so the centering is untouched. The exit is shorter than the enter and accelerates, as when an element disappears (eco-motion); Material, Carbon and Atlassian also close faster than they open. The script adds is-leaving and removes the dialog when the animation has run. Reduced motion: no fade, removed at once. Compare other values in the live Motion section on Examples & Guidelines.
| Input | Result |
|---|---|
| Click × | Closes the modal. |
| ESC | Closes the modal. |
| Click the overlay | Closes the modal. A click inside the panel does not. |
| Open | Focus moves into the dialog. Page scroll is locked (body.has-modal). |
| Tab / Shift+Tab | Stays inside the dialog. |
| Close | Focus returns to the button that opened it. |
| Markup | role="dialog", aria-modal="true", aria-labelledby pointing at the title. |
| Topic | Requirement |
|---|---|
| Markup | role="dialog", aria-modal="true", aria-labelledby pointing at the title. |
| Focus | Moves into the dialog on open, is trapped inside it and returns to the opener on close (WCAG 2.4.3). The ring is 2px, border-focus (WCAG 2.4.7). |
| Close | Three ways, always: ×, ESC and the overlay. |
| Scroll | The page behind does not scroll while the modal is open. |
| Reduced motion | No fade under prefers-reduced-motion: reduce. |