Skip to content

Layer

P-06 · Primitives · Primitives

All elements · Layer on the Primitives page

Last checked 2026-10-22 · Awaiting approval · Accessibility tests

Copies the bun run a11y:approve command for Layer.

Layer

P-06WCAG 2.2 AA · Passed #433

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)
Modal layer with scrim (rendered inline for the specimen)
A panel inside the layer

Guidance

When to use

  • Stack a floating surface in a host that cannot use the native <dialog> or popover top 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 popover attribute. See Popover.
  • When a message should appear briefly without interrupting, use Toast. See Toast.

Usability

Do
  • Prefer the platform: <dialog> with showModal() for modal and drawer, popover for popover, menu and tooltip.
  • Use data-depth in order when one dialog opens another, and close them in reverse order.
  • Keep drawers on the edge users expect: right for detail panes, bottom on narrow screens.
  • Close the layer with data-state="closed" so it is removed from layout and pointer input.
Don’t
  • 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

Layer 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" and aria-modal="true" (or a native <dialog> opened with showModal()), plus aria-labelledby pointing at its title.
  • Content behind a modal layer must be made inert so 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-motion removes 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

WCAG 2.2 AA · Passed

Passed the gate (run 358b316 on 2026-10-22): no finding in any theme. Awaiting a person’s approval of the checklist below.

Layer test checklist
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.

Layer package
Source css/classic/primitives/behavior.css
Specimens also use Box · Card · Chip · Icon · Surface · Table · Text · Toolbar
Sprites sprites/icons.svg
React Not yet available: the React binding has not started.

Latest updates

No recorded changes yet.

All release notes