P-06 · Primitives · Primitives
All elements · Layer on the Primitives page
Last checked 2026-10-22 · Awaiting approval · Accessibility tests
Approve Layer
Approving as
Overlay positioning, scrim, focus containment and dismissal: the one primitive behind every floating
surface. In static HTML the platform provides most of it: <dialog> for modal/drawer,
popover for popover/menu/tooltip. The class exists for hosts that need manual stacking.
| Axis | Values |
|---|---|
data-kind |
modal · drawer · popover · tooltip · toast |
data-placement |
left · right · bottom (drawer) |
data-state · data-depth |
open · closed / 0 · 1 · 2 · 3 (stack offset, +10 z each) |
Guidance
When to use
-
Stack a floating surface in a host that cannot use the native
<dialog>orpopovertop layer. - Position a modal, drawer, popover, tooltip or toast region with
data-kind. - Put a scrim behind a modal or drawer with
.qf-layer__scrim. - Stack a second or third dialog above the first with
data-depth.
When to consider something else
- When you need the full overlay stack with focus trapping, return and dismissal rules, use the Overlay system composite. See OverlaySystem.
-
When the floating surface is a modal or drawer, use Overlay, which builds on
<dialog>. See Overlay. -
When the surface is anchored to a trigger, use Popover, Menu or Tooltip on the native
popoverattribute. See Popover. - When a message should appear briefly without interrupting, use Toast. See Toast.
Usability
-
Prefer the platform:
<dialog>withshowModal()for modal and drawer,popoverfor popover, menu and tooltip. -
Use
data-depthin order when one dialog opens another, and close them in reverse order. -
Keep drawers on the edge users expect:
rightfor detail panes,bottomon narrow screens. -
Close the layer with
data-state="closed"so it is removed from layout and pointer input.
- Don't stack more than three dialogs; rethink the flow instead.
- Don't use a scrim for non-modal content, since it suggests the page is blocked.
- Don't put a toast in a modal layer.
Accessibility
Keyboard
| Keys | Action |
|---|---|
| Escape | The binding must close the top-most modal, drawer or popover layer and return focus to its trigger. |
| Tab | In a modal or drawer, the binding must keep focus inside the panel, cycling from last to first. |
| Shift+Tab | In a modal or drawer, the binding must move focus backward and wrap from first to last. |
Roles and names
-
A modal panel needs
role="dialog"andaria-modal="true"(or a native<dialog>opened withshowModal()), plusaria-labelledbypointing at its title. -
Content behind a modal layer must be made
inertso assistive technology cannot reach it. - The scrim is decorative and carries no role.
- A tooltip layer's content is referenced from its trigger with
aria-describedby.
Focus
- On open, the binding must move focus into the panel: the first field, or the title or panel itself when there is no field.
- On close, focus must return to the element that opened the layer, or to a logical next element if that trigger is gone.
- Popover and tooltip layers do not trap focus.
- A focused element must not be hidden under a layer it does not belong to (2.4.11).
Other requirements
- The static class only positions and stacks; trapping, inert, Escape and focus return are the binding's job or the platform's.
- Layers that appear on hover or focus must be dismissible, hoverable and persistent (1.4.13).
- Drawers and modals must reflow at 320 CSS px without two-way scrolling (1.4.10).
- Pair with Transition for enter and exit motion;
prefers-reduced-motionremoves it.
Test your implementation too. This guidance covers the element as specified here. How a screen composes it (labels, order, announcements) still needs its own keyboard and screen reader check.
Accessibility tests
Passed the gate (run 358b316 on 2026-10-22): no finding in any theme. Awaiting a person’s approval of the checklist below.
| Area | Automated | A person checks |
|---|---|---|
| General | Passed | Color is never the only cue, text alternatives say the same thing as the graphic, and the element reads the same in the light and the dark theme. |
| Zoom | Passed | At 200% zoom, at 320 CSS pixels wide and with the WCAG text-spacing overrides, nothing is lost, clipped or overlapping. |
| Keyboard | Passed | Every row of the Keyboard table works, focus is always visible and never trapped, and the tab order follows the reading order. (3 rows) |
| Screen reader | Passed | With NVDA or JAWS on Windows and VoiceOver on macOS, names, roles and states are announced as Roles and names says, and changes are announced once. (4 items) |
Approving. When the checklist passes on the checked run, record it with
bun run a11y:approve -- layer --by "Your name" and commit
a11y/approvals.json with the rebuilt pages.
All test status
Package
Consumers link one stylesheet, css/classic/index.css, which includes every element. The
files below are where this element is defined.
Latest updates
No recorded changes yet.