Screen
A full-height route scaffold that paints the surface, applies safe-area insets, and hosts sticky header and footer chrome around a scrolling body. Chrome padding is fixed by the system; only the body's padding is authorable.
Implementation previewEvery example. In one place.
Explore every named example, including states, compositions, and theme variants.
Example source
packages/design-system/src/components/layout/Screen/Screen.stories.tsxExport PlaygroundImplementation preview Local Storybook build 0.2.0-preview.1. Source specification status does not approve this runtime.
Stories + source JSON ↗Local specification source status: preview. Implementation preview pending sign-off.
A full-height route scaffold that paints the surface, applies safe-area insets, and hosts sticky header and footer chrome around a scrolling body. Chrome padding is fixed by the system; only the body's padding is authorable.
Implementation API 13 entry points · source-derived preview
Read the actual callable API and preserve its composition constraints. Each union branch is a complete alternative; props marked forbidden cannot be supplied.
import { Screen } from '@cloakedinc/design-system';ScreenScreenProps
packages/design-system/src/components/layout/Screen/ScreenRoot/ScreenRoot.tsx:17
| Prop | Type | Presence | Notes |
|---|---|---|---|
safeArea | ScreenSafeArea | undefined | Optional | Adds safe-area insets to padding edges. Defaults to `'both'`.Source default: 'both' |
scroll | boolean | undefined | Optional | Wrap legacy children or `Screen.Body` in a scrollable container. Defaults to `true`.Source default: true |
surface | "page" | "surface" | undefined | Optional | Background surface token. Defaults to `'page'`.Source default: 'page' |
overscrollTop | string | undefined | Optional | Color continued into the top over-pull edge (pass a `var(--cds-*)` token). Defaults to the surface color. |
overscrollBottom | string | undefined | Optional | Color continued into the bottom over-pull edge (pass a `var(--cds-*)` token). Defaults to the surface color. |
fill | boolean | undefined | Optional | Fill the available height. Defaults to `true`.Source default: true |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
children | React.ReactNode | Optional | |
testId | string | undefined | Optional | Selector for the screen root element; the slots carry their own. |
padding | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Padding on all four sides. |
paddingX | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Horizontal padding (left + right). Overrides `padding` on the X axis. |
paddingY | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Vertical padding (top + bottom). Overrides `padding` on the Y axis. |
paddingTop | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Top padding. Overrides `paddingY` and `padding`. |
paddingRight | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Right padding. Overrides `paddingX` and `padding`. |
paddingBottom | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Bottom padding. Overrides `paddingY` and `padding`. |
paddingLeft | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Left padding. Overrides `paddingX` and `padding`. |
Screen.HeaderScreenHeaderProps
packages/design-system/src/components/layout/Screen/ScreenSlots/ScreenSlots.tsx:31
| Prop | Type | Presence | Notes |
|---|---|---|---|
safeArea | boolean | undefined | Optional | Adds top safe-area inset. Defaults to the parent `Screen.safeArea`. |
inset | "activity" | undefined | Optional | Presentation inset for a header that stands in for a navigation bar. |
sticky | boolean | undefined | Optional | Keeps the header visible above the body scroller. Defaults to `true`.Source default: true |
children | React.ReactNode | Optional | |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
testId | string | undefined | Optional | Selector for the `<header>` element; chrome inside it takes its own. |
Screen.Header.BarScreenHeaderBarProps
packages/design-system/src/components/layout/Screen/ScreenHeaderChrome/ScreenHeaderChrome.tsx:24
| Prop | Type | Presence | Notes |
|---|---|---|---|
justify | ScreenHeaderJustify | undefined | Optional | Horizontal distribution for header chrome.Source default: 'between' |
children | React.ReactNode | Optional | |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
testId | string | undefined | Optional | Selector for the row element; chrome inside it takes its own. |
Screen.Header.BrandScreenHeaderBrandProps
packages/design-system/src/components/layout/Screen/ScreenHeaderChrome/ScreenHeaderChrome.tsx:193
| Prop | Type | Presence | Notes |
|---|---|---|---|
children | React.ReactNode | Required | |
testId | string | undefined | Optional | Selector for the brand mark wrapper, not the node passed as `children`. |
Screen.Header.TitleScreenHeaderTitleProps
packages/design-system/src/components/layout/Screen/ScreenHeaderChrome/ScreenHeaderChrome.tsx:36
| Prop | Type | Presence | Notes |
|---|---|---|---|
children | React.ReactNode | Required | |
as | "h1" | "h2" | "h3" | undefined | Optional | Optional heading level. Defaults to `h1`.Source default: 'h1' |
variant | "h4" | "h5" | undefined | Optional | Type ramp entry. Defaults to `'h4'`; `'h5'` matches the native iOS bar's `.font(.headline)`.Source default: 'h4' |
testId | string | undefined | Optional | Selector for the heading element; nothing derives from it. |
Screen.Header.ActionScreenHeaderActionProps
packages/design-system/src/components/layout/Screen/ScreenHeaderChrome/ScreenHeaderChrome.tsx:47
| Prop | Type | Presence | Notes |
|---|---|---|---|
children | React.ReactNode | Required | |
onPress | (() => void | boolean) | undefined | Optional | Called when the action is pressed. Return `false` to suppress the `safeBack` fallback. |
safeBack | boolean | undefined | Optional | Treat this action as a back affordance: if the header is still mounted after the transition,
run `fallback`. Use on a text action ("Cancel", "Done") that is a screen's only way out.Source default: false |
fallback | (() => void) | undefined | Optional | Last-resort fallback when `safeBack` is set and the header remains mounted.Source default: reloadToRoot |
fallbackDelayMs | number | undefined | Optional | Delay before the `safeBack` check. Defaults to the Stackflow transition plus buffer.Source default: SAFE_BACK_FALLBACK_DELAY |
haptic | HapticFeedback | undefined | Optional | Haptic feedback for successful presses. Defaults to `true` (`light`).Source default: true |
testIdRoot | string | undefined | Optional | Header test ID root used to derive `${testIdRoot}-${slot}`. |
slot | "right-action" | undefined | Optional | Header action slot used for derived test IDs. |
testId | string | undefined | Optional | Selector for the action element. Overrides the `${testIdRoot}-${slot}` derivation. |
Screen.Header.BackButtonScreenHeaderBackButtonProps
packages/design-system/src/components/layout/Screen/ScreenHeaderChrome/ScreenHeaderChrome.tsx:133
| Prop | Type | Presence | Notes |
|---|---|---|---|
onBack | () => void | boolean | Required | Called when the back button is pressed. Return `false` to suppress the fallback. |
fallback | (() => void) | undefined | Optional | Last-resort fallback when the header remains mounted after the back transition.Source default: reloadToRoot |
fallbackDelayMs | number | undefined | Optional | Delay before the fallback check. Defaults to the Stackflow transition plus buffer.Source default: SAFE_BACK_FALLBACK_DELAY |
label | string | undefined | Optional | Accessible action label. Defaults to `Back`.Source default: 'Back' |
glyph | "arrow-left" | "close" | undefined | Optional | Mark the back affordance draws. Defaults to `arrow-left`.Source default: 'arrow-left' |
haptic | HapticFeedback | undefined | Optional | Haptic feedback for successful presses. Defaults to `true` (`light`). |
testIdRoot | string | undefined | Optional | Header test ID root used to derive `${testIdRoot}-back`. |
testId | string | undefined | Optional | Selector for the back button. Overrides the `${testIdRoot}-back` derivation. |
Screen.Header.IconButtonScreenHeaderIconButtonProps
packages/design-system/src/components/layout/Screen/ScreenHeaderChrome/ScreenHeaderChrome.tsx:91
| Prop | Type | Presence | Notes |
|---|---|---|---|
icon | React.ReactElement<any, string | React.JSXElementConstructor<any>> | Required | Icon element to render inside the header action. |
label | string | Required | Accessible action label. |
onPress | (() => void) | undefined | Optional | Called when pressed. Without it the icon renders as a static mark, not a button. |
haptic | HapticFeedback | undefined | Optional | Haptic feedback for successful presses. Defaults to `true` (`light`).Source default: true |
testIdRoot | string | undefined | Optional | Header test ID root used to derive `${testIdRoot}-${slot}`. |
slot | ScreenHeaderIconButtonSlot | undefined | Optional | Header icon slot used for derived test IDs. |
testId | string | undefined | Optional | Selector for the icon element. Overrides the `${testIdRoot}-${slot}` derivation. |
Screen.Header.ProgressScreenHeaderProgressProps
packages/design-system/src/components/layout/Screen/ScreenHeaderChrome/ScreenHeaderChrome.tsx:161
| Prop | Type | Presence | Notes |
|---|---|---|---|
activeSegment | number | undefined | Optional | 1-based; every segment up to and including it renders filled. Defaults to `1`.Source default: 1 |
totalSegments | number | Required | How many segments to render; values below `1` are treated as `1`. |
testId | string | undefined | Optional | Selector for the segment row. The segments themselves are hidden from assistive tech. |
Screen.Header.TaskScreenHeaderTaskProps
packages/design-system/src/components/layout/Screen/ScreenHeaderChrome/ScreenHeaderChrome.tsx:178
| Prop | Type | Presence | Notes |
|---|---|---|---|
brand | React.ReactNode | Optional | Brand/avatar mark. |
children | React.ReactNode | Required | |
testId | string | undefined | Optional | Selector for the task row, covering both the mark and the label. |
Screen.BodyScreenBodyProps
packages/design-system/src/components/layout/Screen/ScreenSlots/ScreenSlots.tsx:55
| Prop | Type | Presence | Notes |
|---|---|---|---|
scroll | boolean | undefined | Optional | Wrap body children in a scrollable region. Defaults to the parent `Screen.scroll`. |
layout | ScreenBodyLayout | undefined | Optional | `'grouped'` renders `Screen.Section` slabs edge to edge. Defaults to `'solid'`.Source default: 'solid' |
safeArea | ScreenSafeArea | undefined | Optional | Adds safe-area insets to body padding. Defaults to `'none'`.Source default: 'none' |
bleedUnderTopChrome | boolean | undefined | Optional | Runs content under chrome the host draws over the web view, instead of clearing it. For a body
whose top edge is already spoken for: a hero that bleeds to it, a scroller the body does not
own, or an in-flow `Screen.Header` above it. Everything else wants the default, which insets by
the host's chrome and supersedes the top edge of `safeArea`.Source default: false |
children | React.ReactNode | Optional | |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | Inline style override. When `scroll` is true this lands on the scroll container, not the padded inner scroll-content — so a `padding*` here has no effect; use the padding props. |
testId | string | undefined | Optional | Selector for the `<main>` element, which is the scroll container when `scroll` is true. |
padding | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Padding on all four sides. |
paddingX | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Horizontal padding (left + right). Overrides `padding` on the X axis. |
paddingY | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Vertical padding (top + bottom). Overrides `padding` on the Y axis. |
paddingTop | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Top padding. Overrides `paddingY` and `padding`. |
paddingRight | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Right padding. Overrides `paddingX` and `padding`. |
paddingBottom | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Bottom padding. Overrides `paddingY` and `padding`. |
paddingLeft | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Left padding. Overrides `paddingX` and `padding`. |
Screen.SectionScreenSectionProps
packages/design-system/src/components/layout/Screen/ScreenSlots/ScreenSlots.tsx:109
| Prop | Type | Presence | Notes |
|---|---|---|---|
children | React.ReactNode | Optional | |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
testId | string | undefined | Optional | Selector for the `<section>` element; nothing derives from it. |
padding | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Padding on all four sides. |
paddingX | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Horizontal padding (left + right). Overrides `padding` on the X axis. |
paddingY | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Vertical padding (top + bottom). Overrides `padding` on the Y axis. |
paddingTop | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Top padding. Overrides `paddingY` and `padding`. |
paddingRight | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Right padding. Overrides `paddingX` and `padding`. |
paddingBottom | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Bottom padding. Overrides `paddingY` and `padding`. |
paddingLeft | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 96 | 120 | 240 | undefined | Optional | Left padding. Overrides `paddingX` and `padding`. |
Screen.FooterScreenFooterProps
packages/design-system/src/components/layout/Screen/ScreenSlots/ScreenSlots.tsx:123
| Prop | Type | Presence | Notes |
|---|---|---|---|
safeArea | boolean | undefined | Optional | Adds bottom safe-area inset. Defaults to the parent `Screen.safeArea`. |
sticky | boolean | undefined | Optional | Keeps the footer visible below the body scroller. Defaults to `true`.Source default: true |
children | React.ReactNode | Optional | |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
testId | string | undefined | Optional | Selector for the `<footer>` element; controls inside it take their own. |
Only explicit destructured parameter initializers are extracted. Other runtime defaults remain in implementation source.
Overview
Full-height route scaffold. Use it as the outermost element of a routed screen: it paints the
page surface, applies safe-area insets, and hosts sticky Screen.Header / Screen.Footer
chrome while Screen.Body owns the scroll.
When to use
Reach for Screen as the top-level wrapper of a routed view — anything that needs a header,
a scrolling body, a pinned footer action, or device safe-area handling. Compose the header from
the Screen.Header.* sub-parts (Bar, Title, Brand, Task, BackButton, IconButton,
Action, Progress).
- A detail, modal, or standalone screen that just needs a centered surface without route
chrome? Use
Background. - Laying out content inside a Screen? Use
VStack/HStack/Grid.
Chrome padding is fixed
Screen.Header and Screen.Footer take no padding props. The header is 20 / 12 / 8 and the
footer is 20 / 16 / 16, on every route, and a padding on the Screen root reaches only the
body. Only Screen.Body is authorable, because body content genuinely varies — sectioned lists,
full-bleed heroes — while chrome does not.
The props used to exist and every value in them drifted: one app settled on 12 / 8, another
copy-pasted 16 / 12 into thirteen files, two chat headers inset their icons to 12 while their own
body sat at 20, and seven headers shipped with no paddingX at all, flush to the screen edge.
Screen.Header inset="activity" is the one sanctioned variation: it swaps the 12 top for 32, for a
Stackflow activity that renders a header with no Screen root above it and no navigation bar to
sit under.
<Screen.Header inset='activity' testId='pack-details-header'>
<Screen.Header.Bar justify='start'>
<Screen.Header.BackButton onBack={pop} />
<Screen.Header.Title>Pack details</Screen.Header.Title>
</Screen.Header.Bar>
</Screen.Header>
For a bespoke host alignment — a header that must line up with a floating control the app draws
outside React — the documented last resort is the slot's style prop, which lands after the
constant. Reach for it only when a real host constraint outranks the system box, and say so in a
comment.
Overscroll edges
The over-pull (rubber-band) region continues the color of whatever sits at that edge, so a
full-bleed footer or colored hero doesn't break into a flat gray bounce. Set the edge color with
overscrollTop / overscrollBottom (pass var(--cds-*) tokens) to match the content there — e.g.
a colored hero at the top, or a FaqCarousel fullBleed band's var(--cds-feature-<tone>-backdrop)
at the bottom. The effect is only visible while bouncing on a touch device.
Solid and grouped bodies
Every screen is one of two shapes, and layout is the only thing that picks between them:
solid(default) — children flow down one continuous surface inside the page gutter.grouped— the grouped-list rhythm. The body drops its gutter, eachScreen.Sectionpaints the surface, and the fixed 16px gap between two sections takes the page color. The gap is not a prop — a tunable one re-opens the drift this closes.
Sections paint and position only. They add no inline padding, because content already self-pads
to the page gutter (SectionHeader and List.Item both inset 20px). A section that insets its own
children makes them double-inset.
The page color is a separator, never a ground. Only the gap between two sections takes it;
everything else the body holds keeps the screen surface — the inset the body reserves for a native
bar, bottom tab-bar clearance, the slack a short screen leaves under its last section, and
both over-pull edges. A grouped body gets that by painting a box that holds only the sections and
nests inside the padded one, so no page color ever reaches the boundary of the scrolling content.
There is nothing to pair on the Screen root, and no overscrollBottom to set.
Body background
Screen.Body's scroll content is opaque so the overscroll backdrop shows only in the over-pull
edges. To give a scrolling body a custom background (e.g. a hero→page gradient), set
--screen-scroll-bg on the body — a raw background on the body className is painted over
by the scroll content and won't show. See PackDetailsView for the pattern.
Component specification
$schema
../../../specs/component-spec.schema.jsonParts
- Id
- root
- Label
- Root
- Description
- Full-height scaffold painting the surface. Structural container.
- Required
- true
- Id
- header
- Label
- Screen.Header
- Description
- Sticky top chrome slot.
- Id
- body
- Label
- Screen.Body
- Description
- Scrolling content slot that owns route padding.
- Id
- section
- Label
- Screen.Section
- Description
- Full-bleed content slab inside a grouped body. Paints and positions only; it adds no inline padding.
- Id
- footer
- Label
- Screen.Footer
- Description
- Sticky bottom chrome slot.
- Id
- bar
- Label
- Screen.Header.Bar
- Description
- Compact header row with justified chrome.
- Id
- brand
- Label
- Screen.Header.Brand
- Description
- App wordmark or brand mark.
- Id
- title
- Label
- Screen.Header.Title
- Description
- Route title; truncates to one line with an ellipsis.
- Id
- action
- Label
- Screen.Header.Action
- Description
- Text action in the header.
- Id
- back-button
- Label
- Screen.Header.BackButton
- Description
- Safe back button.
- Id
- icon-button
- Label
- Screen.Header.IconButton
- Description
- Icon-only header action.
- Id
- task
- Label
- Screen.Header.Task
- Description
- Partner mark plus active task label.
- Id
- progress
- Label
- Screen.Header.Progress
- Description
- Segmented header progress.
Allowed Design Choices
- Variants
- Route
- Label
- Route
- Description
- The single route-scaffold treatment.
- Sizes
- Surfaces
- Page
- Label
- Page
- Description
- Paints the page background.
- Surface
- Label
- Surface
- Description
- Paints the raised surface background.
- Safe Areas
- None
- Label
- None
- Top
- Label
- Top
- Bottom
- Label
- Bottom
- Both
- Label
- Both
- Insets
- None
- Label
- None
- Description
- Standard route header: 12 top, 8 bottom.
- Activity
- Label
- Activity
- Description
- Deeper 32 top inset for a Stackflow activity that renders a header with no Screen root above it and no navigation bar to sit under.
Variants
- Id
- route
- Label
- Route
- Description
- Full-height route scaffold.
- Platforms
- ios
- android
- web
Sizes
None specifiedMeasurements
- Id
- chrome-padding
- Label
- Header and footer padding
- Kind
- exact
- Token
- --cds-space-20
- Value
- 20
- Unit
- px
- Applies To
- header
- footer
- Notes
- Fixed, not authorable: the horizontal gutter is 20 on both slots, the header is 12 top and 8 bottom (32 top under inset="activity"), and the footer is 16 top and bottom. Callers cannot set it, and a Screen padding does not reach it. Contrast with content-padding, which is example-only because the body genuinely varies.
- Id
- content-padding
- Label
- Body content padding
- Kind
- example-only
- Token
- --cds-space-20
- Value
- 20
- Unit
- px
- Applies To
- body
- Notes
- Representative value: body padding is author-configurable and accepts any step on the spacing scale. 20 is the value rendered and verified by the slot-geometry story.
Layout
- Alignment
- Header and footer are sticky and full-width; the body fills the remaining height and scrolls.
- Flow
- A column: sticky header chrome, a scrolling body, and sticky footer chrome. A body set to layout="grouped" stacks Screen.Section slabs split by page-colored separators instead of flowing children inside the page gutter.
- Notes
- Chrome padding is a system constant, so the header and footer boxes are identical on every route; only Screen.Body accepts padding props. A bespoke host alignment goes through the slot's style prop.
- Use Background when a detail or modal screen just needs a centered surface without route chrome.
- layout picks between the two screen shapes. "solid" flows children down one continuous surface inside the page gutter. "grouped" is the grouped-list rhythm: the body drops its gutter, each Screen.Section paints the surface, and the 16px gap between two sections takes the page color as a separator. Sections paint and position only — content self-pads to the page gutter, as SectionHeader and List.Item already do. A section that insets its own children makes them double-inset.
- The page color is a separator, never a ground. Everything else a grouped body holds keeps the screen surface: the inset reserved for a native bar, bottom tab-bar clearance, the slack a short screen leaves under its last section, and both over-pull edges. Nothing needs pairing on the Screen root.
Typography And Color
- Typography
- Label
- Title
- Description
- Screen.Header.Title uses the h1 ramp by default.
- Token
- --cds-type-h1-size
- Colors
- Label
- Page surface
- Description
- The page surface paints the route background.
- Token
- --cds-bg-page
- Label
- Raised surface
- Description
- The surface option paints the raised background.
- Token
- --cds-bg-surface
- Label
- Text
- Description
- Route content uses primary text.
- Token
- --cds-text-primary
States
- Id
- scroll
- Label
- Scroll
- Description
- The body scrolls under sticky chrome (the default).
- Id
- no-scroll
- Label
- No scroll
- Description
- scroll=false for short fixed routes.
- Id
- surface
- Label
- Surface
- Description
- Uses the raised surface background.
Behavior
- Label
- Sticky chrome
- Description
- Screen.Header and Screen.Footer stay pinned while Screen.Body scrolls.
- Label
- Safe area
- Description
- safeArea adds device insets on top of the configured padding, propagated to slots.
- Label
- Fixed chrome box
- Description
- Screen.Header and Screen.Footer own their padding. The parent Screen padding reaches only the body.
- Label
- Under host chrome
- Description
- Screen.Body always clears chrome the host draws over the web view, reading --cds-safe-area-top-chrome. It takes the larger of that and the device inset rather than stacking them, and collapses to zero where the host draws no chrome. bleedUnderTopChrome drops it for a body whose top edge is already spoken for — a bleeding hero, a scroller the body does not own, or an in-flow Screen.Header above it.
- Label
- Host chrome frost
- Description
- The Screen root draws a progressive blur the height of that same inset, so content reads as passing under the host's bar. The blur is strongest at the top and fully clear by the band's bottom edge, so content at rest is untouched. The host publishes --cds-top-chrome-frost to turn it on, and leaves it off for a bar drawing no background of its own, since a screen meaning to run edge to edge wants no blur across it. Nothing on Screen configures it.
Accessibility
- Label
- Route structure
- Description
- Header, body, and footer keep a predictable landmark order for assistive tech.
- Label
- Safe insets
- Description
- Safe-area insets keep chrome clear of the notch and home indicator.
Content Adaptation
- Label
- Header composition
- Description
- Compose the header from Bar, Title, Brand, Task, BackButton, IconButton, Action, and Progress sub-parts.
- Label
- Scroll control
- Description
- Disable scroll for short routes that never overflow.
- Label
- Title truncation
- Description
- Screen.Header.Title truncates long titles to a single line with an ellipsis instead of wrapping the header chrome.
- Label
- Activity inset
- Description
- inset="activity" on Screen.Header deepens the top inset for a Stackflow activity that stands in for a navigation bar.
Motion Haptics
- Motion
- Label
- None
- Description
- Screen does not animate; slot chrome owns any motion.
- Haptics
- Label
- Chrome haptics
- Description
- Header actions and the back button own their own haptics.
Platform Differences
- Platforms
- ios
- Description
- The scroll content is forced one pixel past the viewport so iOS WKWebView keeps momentum and rubber-band scrolling available on short screens.
- Rationale
- WKWebView only engages bounce when content exceeds the container, so short routes would otherwise feel dead at rest.