Skip to content

Surface

P-04 · Primitives · Primitives

All elements · Surface on the Primitives page

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

Copies the bun run a11y:approve command for Surface.

Surface

P-04WCAG 2.2 AA · Passed #431

A bounded visual container: background, border, radius, elevation and the shared hover/selected pattern that cards, list rows and kanban cards all use.

Axis Values
data-tone (paper) · raised · sunken · primary · info · success · warning · error · inverse
data-radius none · sm · md · lg · xl
data-elevation xs · sm · md · lg · xl · 2xl
data-bordered · data-interactive 1px token border / hover + selected + focus states
aria-selected · data-state true / hover · selected
Tones
paper
raised · md
sunken
primary
success
warning
error
inverse
Interactive: rest · hover · selected
Rest (hover me)
Hover
Selected

Guidance

When to use

  • Give a pane, panel or region its own background, border and radius.
  • Raise a floating panel with data-elevation, or recess a well with data-tone="sunken".
  • Tint a region with a status tone, such as a success or error summary block.
  • Share the hover and selected look across cards, list rows and kanban cards with data-interactive.

When to consider something else

  • When you only need spacing and alignment, use Box; Surface is for visible containers. See Box.
  • When the container summarizes one record with a header and actions, use Card. See Card.
  • When the tinted block is a message the user must notice, use Banner, which has an icon and role. See Banner.
  • When the whole container is one action, build it on Pressable or Button rather than a clickable Surface. See Pressable.

Usability

Do
  • Use elevation sparingly: panes are flat, floating layers are raised.
  • Use data-bordered when a surface sits on the same tone as its parent.
  • Reflect selection with aria-selected or aria-pressed so the look and the state come from one attribute.
  • Keep status tones for content that is actually about that status.
Don’t
  • Don't add data-interactive to a surface that does nothing on click.
  • Don't nest raised surfaces inside raised surfaces to build hierarchy.
  • Don't use data-state="hover" or "selected" in the product; they exist to show states in the catalog.

Accessibility

Keyboard

Surface keyboard
Keys Action
Tab Moves focus to an interactive surface when it is focusable (a button, a or tabindex="0").
Enter The binding must activate an interactive surface that is not a native button or link.
Space The binding must activate an interactive surface that acts as a button or selectable option.

Roles and names

  • A plain surface adds no role. A named region needs role="region" or a landmark element with aria-label.
  • An interactive surface needs a role that matches what it does: a native button or a, or role="option" or row inside a listbox or grid.
  • Selection is aria-selected="true" (option, row, tab) or aria-pressed="true" (toggle button).

Focus

  • data-interactive surfaces show --qf-focus-ring on :focus-visible (2.4.7).
  • A focused surface must not be hidden behind sticky headers or other layers (2.4.11).

Other requirements

  • Selected and hover states must not rely on background color alone where that change is the only cue; the selected border helps meet 3:1 (1.4.11).
  • Text on status tones and on inverse needs 4.5:1 (1.4.3) in both themes.
  • Borders and edges that identify a control need 3:1 against adjacent colors (1.4.11).

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.

Surface 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. (3 items)

Approving. When the checklist passes on the checked run, record it with bun run a11y:approve -- surface --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.

Surface package
Source css/classic/primitives/layout.css
Specimens also use Box · Card · Chip · Icon · 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