---
title: "Choosing a layer"
description: "A decision table from surface kind to rung, five nesting rules, and the three questions that resolve almost every case."
url: "https://opsinjs.pensievelabs.org/foundations/materials/choosing-a-layer"
source: "https://opsinjs.pensievelabs.org/foundations/materials/choosing-a-layer.md"
section: "Foundations"
kind: "foundation"
reviewed: "2026-09-02"
reviewer: "design"
aliases: ["which material", "surface decision", "nesting surfaces", "when to use blur", "layer choice"]
---

> Elements written as `<PascalCase … />` below are opsinjs documentation
> components. Their attributes are the content: the values they render are
> generated from `tokens/*.json` and `registry/catalogue.ts` and are
> published separately at https://opsinjs.pensievelabs.org/r/index.json and under the Reference
> section.
> Nothing is missing from this page. The data simply does not live in
> the prose.

<PageTemplate kind="foundation" />

## Overview [#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 [#how-it-works]

### Three questions, in order [#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](../../project/decisions/0014-material-rung-names.mdx).

### The decision table [#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 [#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 [#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](./the-contrast-floor.mdx): 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 [#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](../../health/two-colour-axes.mdx).

<DoDont>
  <DoDont.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.
  </DoDont.Do>

  <DoDont.Dont>
    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.
  </DoDont.Dont>
</DoDont>

**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 [#tokens]

<TokenTable scope="material" />

## Accessibility impact [#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](../../accessibility/keyboard-and-focus.mdx) is the
contract.

## Related [#related]

* [The ladder](./the-ladder.mdx) says what each rung is made of, and the boundary
  between opaque and translucent.
* [Performance budget](./performance-budget.mdx) explains why "at most three
  composited surfaces" is a real number and how to check you are under it.
* [Layering and elevation](../layering-and-elevation.mdx) has the z-index
  contract that decides what is in front of what.
