Choosing a layer
A decision table from surface kind to rung, five nesting rules, and the three questions that resolve almost every case.
Overview
Six rungs is a small enough set that the choice should never take long, and this page exists to make sure it does not. It is a lookup table and five rules, followed by the reasoning for the cases people argue about.
The failure this prevents is not an ugly screen. It is a screen that has drifted into five subtly different translucencies over eighteen months, at which point nobody can say what any of them means, the compositing budget is blown, and the contrast of half the surfaces depends on content the design team has never seen.
How it works
Three questions, in order
1. Does content scroll under it? If yes, it is overlay. This is the only
question whose answer is a single rung, and it is first because it is the
question people most often skip.
2. Does it cover the page, so that what is behind it is no longer usable? If
yes, it is sheet, and it needs a scrim beneath it. Both, or neither.
3. Otherwise: is it temporarily above the page, or part of it? A surface that
appears, is dismissed and leaves nothing behind, such as a menu, a popover or a
dragged card, is raised. A bounded piece of content that belongs to the page is
card. If it is neither, it is canvas and it needs no material at all.
overlay and sheet are the pair people get backwards, because two retired
vocabularies used those words the other way round. Chrome that content scrolls
beneath is overlay; the panel that covers the page is sheet. See
ADR 0014.
The decision table
| The surface | Rung | Why |
|---|---|---|
| Page background | canvas | nothing is behind it, so nothing shows through it |
| A section of a list or a form | card | a bounded piece of content, in place, not above the page |
| A card showing one reading, tappable | card | tapping is an affordance, not a depth; card is the default home for a health value |
| A card showing one reading, not tappable | card | the same rung, because the difference between the two rows is the affordance, and nothing else |
| Menu, dropdown, select list | raised | temporarily above the page, over arbitrary content |
| Popover, tooltip | raised | same, and small enough that translucency would be noise |
| Toast, snackbar | raised | appears over content it knows nothing about; must not depend on it |
| Sticky nav bar or app bar | overlay | content scrolls under it |
| Bottom tab bar | overlay | content scrolls under it |
| Sticky section header inside a list | overlay | content scrolls under it |
| Sticky summary showing today's value | overlay, conditionally | see the exception below |
| Bottom sheet | sheet | covers the page; needs a scrim |
| Modal dialog | sheet | covers the page; needs a scrim |
| Full-screen takeover, camera or scanner UI | sheet | covers the page; the scrim may be the camera feed's own dimming |
| The dimmer behind any of the above | scrim | its only job is to remove information |
| Side navigation on a wide screen, always visible | card | it is a region of the page, not a surface above it |
| Side navigation on a phone, dismissible | sheet | it covers the page; needs a scrim |
| Loading skeleton | same rung as the thing it replaces | a skeleton that changes rung makes the page jump |
The five nesting rules
N1. A translucent rung may never contain another translucent rung. A card
inside a sheet is card or raised, never overlay. Two stacked blurs cost
twice as much, and the composite of a translucent surface over a translucent
surface has no bounded worst case, so the contrast floor cannot be computed for
it.
N2. A scrim is a sibling of the rung it serves, never its child. It sits
between the sheet and everything below, in the stacking order, not inside the
sheet's box.
N3. At most one sheet at a time. A dialog opened from a dialog is a bug
in the flow, not a stack to be styled. If a decision genuinely requires two
steps, it requires two steps inside one sheet.
N4. When a sheet is open, the chrome on the overlay rung goes under the
scrim. A nav bar that stays bright and crisp above a scrim looks interactive
and is not. Dim it with everything else.
N5. raised's shadow is the only shadow you may add. canvas and card
have none, sheet and overlay carry their own, and scrim has none. "A small
shadow to make it pop" on a card is how a system acquires nine elevations, none
of which mean anything.
The one conditional case
A sticky summary bar that shows a health value, such as today's blood pressure or
the latest glucose reading, is the only place a number a person may act on is
allowed on a translucent rung, and it is allowed only when the same value also
appears on an opaque surface on the same screen. The bar is then a convenience
and not the record. If the bar is the only place the value appears, it is not
chrome: make it a card pinned to the top, opaque, and accept that content no
longer scrolls under it.
The reasoning is on The contrast floor: a translucent rung's contrast is bounded and measured, so it is not unsafe, but "bounded worst case" is a weaker guarantee than "known background", and the element carrying the number should have the stronger one.
Using it
Start at the lowest rung that works. The ladder is not a scale of quality.
Most surfaces on a well-designed health screen are canvas and card, a few are
raised, and one is an overlay. A screen where most things are raised has no
hierarchy, because everything is equally lifted.
Do not use a rung to communicate importance. Importance is type size, position and space. Material is depth. A more translucent card is not a more important card, and a reader will not read it as one.
Do not use a rung to communicate status. That is the colour axis's job, and mixing the two produces a screen where urgency is encoded in blur radius. See Two colour axes.
Do
A dismissible filter panel on sheet over a scrim, with its internal
grouping on card and its primary action as an ordinary button. One
translucent rung and one scrim: two composited surfaces.
Don’t
The same panel on sheet, its sections on overlay "for consistency with
the header", and the header still crisp above the scrim. Four composited
surfaces, a nested blur whose contrast cannot be computed, and a nav bar that
looks live while the sheet is modal.
When you cannot decide, the surface is doing two jobs. The recurring example
is a header that is both page chrome and a content card. Split it: the chrome is
overlay, the card is a card inside the scroll area, and the screen
immediately becomes easier to describe.
Tokens
| Token | What it controls | Used by |
|---|---|---|
| --opsin-material-canvas-tintvar(--opsin-neutral-0) | Rung 0 (canvas): the tint over what is behind it. The application background. Nothing is behind it, so nothing shows through it. | surface |
| --opsin-material-canvas-tint-alpha1 | Rung 0 (canvas): how opaque that tint is. | surface |
| --opsin-material-canvas-blur0px | Rung 0 (canvas): backdrop blur radius. | surface |
| --opsin-material-canvas-saturation1 | Rung 0 (canvas): backdrop saturation multiplier. | surface |
| --opsin-material-canvas-bordernone | Rung 0 (canvas): the boundary. | surface |
| --opsin-material-canvas-shadownone | Rung 0 (canvas): the shadow that separates it from what is behind. | surface |
| --opsin-material-canvas-scrim0 | Rung 0 (canvas): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | surface |
| --opsin-material-canvas-opaquevar(--opsin-neutral-0) | Rung 0 (canvas): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | surface |
| --opsin-material-inset-tintvar(--opsin-neutral-100) | Rung 0.5 (inset): the tint over what is behind it. The recessed well inside a card: the explanatory note a Callout draws inside the reading it annotates, and the track a RangeBar lays its range along. Its fill sits below the card fill in both themes, so the well reads as carved into the card rather than lifted off it. In light it is neutral-100, one step under the white card, which is where a Callout already sat. In dark it is a value between the page and the card, below the card so the well is a recess and above the page so a Callout used on its own is still visible. | callout, range-bar, surface |
| --opsin-material-inset-tint-alpha1 | Rung 0.5 (inset): how opaque that tint is. | surface |
| --opsin-material-inset-blur0px | Rung 0.5 (inset): backdrop blur radius. | surface |
| --opsin-material-inset-saturation1 | Rung 0.5 (inset): backdrop saturation multiplier. | surface |
| --opsin-material-inset-bordernone | Rung 0.5 (inset): the boundary. | surface |
| --opsin-material-inset-shadownone | Rung 0.5 (inset): the shadow that separates it from what is behind. | surface |
| --opsin-material-inset-scrim0 | Rung 0.5 (inset): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | surface |
| --opsin-material-inset-opaquevar(--opsin-neutral-100) | Rung 0.5 (inset): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | surface |
| --opsin-material-card-tintvar(--opsin-neutral-0) | Rung 1 (card): the tint over what is behind it. The default home for a health value. Opaque, bounded by a line rather than a shadow, and the rung every ResultCard, MetricTile and RangeBar sits on. | surface |
| --opsin-material-card-tint-alpha1 | Rung 1 (card): how opaque that tint is. | surface |
| --opsin-material-card-blur0px | Rung 1 (card): backdrop blur radius. | surface |
| --opsin-material-card-saturation1 | Rung 1 (card): backdrop saturation multiplier. | surface |
| --opsin-material-card-bordervar(--opsin-neutral-200) | Rung 1 (card): the boundary. | surface |
| --opsin-material-card-shadownone | Rung 1 (card): the shadow that separates it from what is behind. | surface |
| --opsin-material-card-scrim0 | Rung 1 (card): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | surface |
| --opsin-material-card-opaquevar(--opsin-neutral-0) | Rung 1 (card): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | surface |
| --opsin-material-raised-tintvar(--opsin-neutral-0) | Rung 2 (raised): the tint over what is behind it. Menus, popovers, tooltips, a dragged card. Still opaque: the shadow, not translucency, is what says 'above'. | surface |
| --opsin-material-raised-tint-alpha1 | Rung 2 (raised): how opaque that tint is. | surface |
| --opsin-material-raised-blur0px | Rung 2 (raised): backdrop blur radius. | surface |
| --opsin-material-raised-saturation1 | Rung 2 (raised): backdrop saturation multiplier. | surface |
| --opsin-material-raised-bordervar(--opsin-neutral-200) | Rung 2 (raised): the boundary. | surface |
| --opsin-material-raised-shadow0 1px 2px oklch(0 0 0 / 0.06), 0 4px 12px oklch(0 0 0 / 0.06) | Rung 2 (raised): the shadow that separates it from what is behind. | surface |
| --opsin-material-raised-scrim0 | Rung 2 (raised): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | surface |
| --opsin-material-raised-opaquevar(--opsin-neutral-0) | Rung 2 (raised): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | surface |
| --opsin-material-sheet-tintvar(--opsin-neutral-0) | Rung 3 (sheet): the tint over what is behind it. A bottom sheet or side panel the reader can dismiss by dragging. Its tint is now near opaque, so the panel lands close to the card colour and a white control inside it no longer floats above a greyer surface. What keeps the covered page recognisable is the scrim around the panel rather than the page reading through the panel, because at this opacity very little of the page shows through the tint. | surface |
| --opsin-material-sheet-tint-alpha0.93 | Rung 3 (sheet): how opaque that tint is. | surface |
| --opsin-material-sheet-blur20px | Rung 3 (sheet): backdrop blur radius. | surface |
| --opsin-material-sheet-saturation1.6 | Rung 3 (sheet): backdrop saturation multiplier. | surface |
| --opsin-material-sheet-borderoklch(0 0 0 / 0.08) | Rung 3 (sheet): the boundary. | surface |
| --opsin-material-sheet-shadow0 -1px 2px oklch(0 0 0 / 0.05) | Rung 3 (sheet): the shadow that separates it from what is behind. | sheet, surface |
| --opsin-material-sheet-scrim0.93 | Rung 3 (sheet): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | surface |
| --opsin-material-sheet-opaquevar(--opsin-neutral-0) | Rung 3 (sheet): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | surface |
| --opsin-material-overlay-tintvar(--opsin-neutral-0) | Rung 4 (overlay): the tint over what is behind it. A pinned toolbar, a tab bar and a floating action bar are all chrome that content scrolls beneath. | surface |
| --opsin-material-overlay-tint-alpha0.74 | Rung 4 (overlay): how opaque that tint is. | surface |
| --opsin-material-overlay-blur28px | Rung 4 (overlay): backdrop blur radius. | surface |
| --opsin-material-overlay-saturation1.8 | Rung 4 (overlay): backdrop saturation multiplier. | surface |
| --opsin-material-overlay-borderoklch(0 0 0 / 0.10) | Rung 4 (overlay): the boundary. | surface |
| --opsin-material-overlay-shadow0 8px 32px oklch(0 0 0 / 0.12) | Rung 4 (overlay): the shadow that separates it from what is behind. | combobox, menu, popover, select, surface, toast, tooltip |
| --opsin-material-overlay-scrim0.74 | Rung 4 (overlay): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | surface |
| --opsin-material-overlay-opaquevar(--opsin-neutral-0) | Rung 4 (overlay): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | surface |
| --opsin-material-scrim-tintvar(--opsin-neutral-950) | Rung 5 (scrim): the tint over what is behind it. The dimming layer behind a modal dialog or a consent sheet. Its job is to remove the page from consideration, not to look like glass. | surface |
| --opsin-material-scrim-tint-alpha0.44 | Rung 5 (scrim): how opaque that tint is. | surface |
| --opsin-material-scrim-blur2px | Rung 5 (scrim): backdrop blur radius. | surface |
| --opsin-material-scrim-saturation1 | Rung 5 (scrim): backdrop saturation multiplier. | surface |
| --opsin-material-scrim-bordernone | Rung 5 (scrim): the boundary. | surface |
| --opsin-material-scrim-shadownone | Rung 5 (scrim): the shadow that separates it from what is behind. | surface |
| --opsin-material-scrim-scrim0.44 | Rung 5 (scrim): the minimum scrim opacity needed for text on this rung to clear the contrast floor. | surface |
| --opsin-material-scrim-opaqueoklch(0.205 0.006 250 / 0.72) | Rung 5 (scrim): the opaque substitute used under prefers-reduced-transparency and where backdrop-filter is unsupported. | surface |
Accessibility impact
The rules on this page are mostly aesthetic hygiene, but three of them are accessibility controls wearing plain clothes:
- N1 exists so that contrast remains computable. A nested translucent surface has no bounded worst case, so no floor can be guaranteed for text on it. This is the reason the rule is absolute rather than a preference.
- N4 exists so that modality is honest. A reader using a screen reader is told the overlay is modal and everything else is inert; a reader using their eyes should be told the same thing, and a bright, crisp nav bar above a scrim says the opposite. The visual and the programmatic state have to agree.
- The conditional case exists so that no health value depends on a backdrop. Bounded is not the same as known, and the number somebody acts on gets the stronger guarantee.
One more, which belongs to the components rather than to the tokens: sheet
surfaces are modal, and modality is a focus contract. Focus moves in, is
trapped, and returns to the trigger on dismissal. The material says nothing
about that, and choosing sheet does not implement it.
Keyboard and focus is the
contract.
Related
- The ladder says what each rung is made of, and the boundary between opaque and translucent.
- Performance budget explains why "at most three composited surfaces" is a real number and how to check you are under it.
- Layering and elevation has the z-index contract that decides what is in front of what.
The ladder
The six named rungs are canvas, card, raised, sheet, overlay and scrim, each with its job, its eight properties and its opaque fallback.
The contrast floor
Every translucent surface gets a minimum backing tint, so text contrast never depends on what is behind it, plus the worst-case composite and the scrim rule.