Layout

Composition rule

Start with structure, then choose flow

Layout primitives should make the page shape obvious before content arrives. Use Container and Section for the outer reading frame, Stack for ordered vertical rhythm, and Grid or Columns only when siblings are peers.

  1. 1. FrameContainer sets the readable width; Section names a related group.
  2. 2. FlowStack owns rhythm; Grid and Columns distribute peers without changing reading order.
  3. 3. SeparateDivider is a semantic break; Spacer is a rare explicit gap when composition cannot express it.
  4. 4. ShellSidebar and Splitter belong to app chrome or work surfaces, not ordinary document sections.

aspect-ratio

#existingsource

A box that reserves space at a fixed width:height ratio before its media loads, eliminating layout shift. The ratio is an enum drawn from a known design ladder (square, 4:3, 3:2, 16:9, 21:9) so embeds stay visually consistent and a one-off ratio cannot creep in. The single child is stretched to fill and object-fit cover is applied.

Readiness
complete
Preview
live
Props
1
Examples
3
Code
implementation mapped

When to use

  • Embedding a video or iframe that must not reflow on load
  • A media thumbnail grid where every tile must match
  • A responsive hero image with a locked cinematic ratio
  • Avatar or preview squares that must stay square at any width

When not to use

  • Text or content that should size to its own height — use a plain block
  • A box whose height is driven by its content — use Stack/Container
  • Charts that compute their own dimensions
  • Layouts needing different ratios per breakpoint (compose two instances)

Accessibility

Role: presentation

  • No keyboard shortcuts

Screen reader: Layout-only; AspectRatio adds no semantics. The embedded media is responsible for its own alt text, title, or accessible name.

Notes: Always give the contained image an alt attribute and the iframe a title — the ratio box does not and cannot supply one for them.

Props

NameTypeDefaultDescription
ratioenum: 1:1 | 4:3 | 3:2 | 16:9 | 21:916:9The width:height ratio to enforce on the box.

Examples

16:9 widescreen

Widescreen 16:9 media frame that keeps the video slot stable before content loads.

16:9Video frameStable before the iframe loads
YAML
type: aspect-ratio
props:
  ratio: 16:9
slots:
  default:
  - type: well
    props:
      padding: spacious
      class: layout-demo-media
    slots:
      default:
      - type: stack
        props:
          gap: xs
          direction: column
          align: center
        children:
        - type: badge
          props:
            variant: muted
          slots:
            default: 16:9
        - type: text
          props:
            kind: heading
            value: Video frame
        - type: text
          props:
            kind: muted
            value: Stable before the iframe loads
1:1 square

Square thumbnail or avatar frame constrained by the showcase stage so the shape is obvious.

1:1Avatar
YAML
type: aspect-ratio
props:
  ratio: 1:1
slots:
  default:
  - type: well
    props:
      padding: cozy
      class: layout-demo-media
    slots:
      default:
      - type: stack
        props:
          gap: xs
          direction: column
          align: center
        children:
        - type: badge
          props:
            variant: primary
          slots:
            default: 1:1
        - type: text
          props:
            kind: muted
            value: Avatar
21:9 ultrawide

Ultrawide cinematic banner that demonstrates the shallow 21:9 ratio without collapsing.

21:9Cinematic banner
YAML
type: aspect-ratio
props:
  ratio: 21:9
slots:
  default:
  - type: well
    props:
      padding: spacious
      class: layout-demo-media
    slots:
      default:
      - type: stack
        props:
          gap: xs
          direction: row
          align: center
        children:
        - type: badge
          props:
            variant: muted
          slots:
            default: 21:9
        - type: text
          props:
            kind: heading
            value: Cinematic banner
Edit YAML

center

#existingsource

A flex box that centres its content both horizontally and vertically. The minimum height is an enum (none, small, medium, large, or full viewport) so the common "centre this in the available space" intent is expressed declaratively rather than with a one-off 100vh. Use it for empty states, loading placeholders, and single-focus hero regions.

Readiness
complete
Preview
live
Props
2
Examples
3
Code
implementation mapped

When to use

  • Centring an empty-state illustration and message
  • Centring a loading spinner in the available space
  • A single call-to-action centred in a viewport-tall region
  • Centring a card in a modal or full-page shell

When not to use

  • Multiple peer items in a row/column — use Stack
  • A responsive tile grid — use Grid or Columns
  • Only horizontal centring of block content — a max-width Container
  • Centring text within a line — use text-align via the text component

Accessibility

Role: presentation

  • No keyboard shortcuts

Screen reader: Layout-only; Center adds no role or semantics. The centred child is responsible for its own heading, label, and focus behaviour.

Notes: DOM order matches visual order — Center does not reorder content, so screen-reader sequence stays aligned with the visual presentation.

Props

NameTypeDefaultDescription
min_heightenum: none | sm | md | lg | screennoneMinimum block size of the centring region.
inlinebooleanfalseWhen true, render as inline-flex so the box hugs its child instead of filling the available width.

Examples

Empty state

Centred empty-state message in a panel-sized region.

No resultsTry a different search term.
YAML
type: center
props:
  min_height: md
slots:
  default:
  - type: stack
    props:
      gap: sm
      direction: column
      align: center
    children:
    - type: text
      props:
        kind: heading
        value: No results
    - type: text
      props:
        kind: muted
        value: Try a different search term.
Centred spinner

Centred loading spinner.

Loading workspace
YAML
type: center
props:
  min_height: sm
slots:
  default:
  - type: spinner
    props:
      size: medium
      layout: block
    slots:
      label:
      - type: text
        props:
          kind: muted
          value: Loading workspace
Inline center

Inline centring hugging its child.

Centered
YAML
type: center
props:
  inline: true
  min_height: none
slots:
  default:
  - type: badge
    props:
      variant: primary
    slots:
      default: Centered
Edit YAML

columns

#existingsource

Lay out peer content blocks in two, three, or four equal-width columns that collapse to a single column below the small breakpoint so reading order stays obvious on phones. The column count and gap are component props, not one-off utility strings embedded in product screens. Use it for marketing sections, settings groups, and side-by-side prose.

Readiness
complete
Preview
live
Props
2
Examples
3
Code
implementation mapped

When to use

  • Two or three blocks of prose or settings side by side on desktop
  • A feature grid that should stack to one column on mobile
  • Splitting a form into balanced columns at wide widths
  • Equal-weight peer cards where source order is the reading order

When not to use

  • A dense N-up tile dashboard with breakpoint-specific counts — use Grid
  • A linear ordered flow — use Stack so order is unambiguous
  • A fixed nav rail plus main content — use Sidebar
  • User-resizable panes — use Splitter

Accessibility

Role: presentation

  • No keyboard shortcuts

Screen reader: Layout-only; Columns preserves child DOM order. Visual columns must not imply a different reading order — the source order is the order assistive tech announces.

Notes: Children remain responsible for their own headings, roles, and focus states. The single-column collapse keeps narrow-viewport reading order identical to the source order.

Props

NameTypeDefaultDescription
countenum: two | three | fourtwoColumn count at the wide breakpoint; collapses to 1 when narrow.
gapenum: sm | md | lgmdGap between columns, from the shared token scale.

Examples

Three columns

Three equal feature blocks that collapse to one on mobile.

01FastA peer block with equal desktop weight.
02ComposableColumns preserve source order.
03ObservableThe gap is token-driven.
YAML
type: columns
props:
  count: three
  gap: md
children:
- type: well
  props:
    padding: cozy
    class: layout-demo-card
  slots:
    default:
    - type: stack
      props:
        gap: xs
        direction: column
      children:
      - type: badge
        props:
          variant: primary
        slots:
          default: '01'
      - type: text
        props:
          kind: heading
          value: Fast
      - type: text
        props:
          kind: muted
          value: A peer block with equal desktop weight.
- type: well
  props:
    padding: cozy
    class: layout-demo-card
  slots:
    default:
    - type: stack
      props:
        gap: xs
        direction: column
      children:
      - type: badge
        props:
          variant: muted
        slots:
          default: '02'
      - type: text
        props:
          kind: heading
          value: Composable
      - type: text
        props:
          kind: muted
          value: Columns preserve source order.
- type: well
  props:
    padding: cozy
    class: layout-demo-card
  slots:
    default:
    - type: stack
      props:
        gap: xs
        direction: column
      children:
      - type: badge
        props:
          variant: success
        slots:
          default: '03'
      - type: text
        props:
          kind: heading
          value: Observable
      - type: text
        props:
          kind: muted
          value: The gap is token-driven.
Two columns

Two-column settings split.

ProfileName, avatar, and contact defaults.
SecuritySessions, MFA, and tokens.
YAML
type: columns
props:
  count: two
  gap: lg
children:
- type: well
  props:
    padding: cozy
    class: layout-demo-card
  slots:
    default:
    - type: stack
      props:
        gap: xs
        direction: column
      children:
      - type: text
        props:
          kind: heading
          value: Profile
      - type: text
        props:
          kind: muted
          value: Name, avatar, and contact defaults.
- type: well
  props:
    padding: cozy
    class: layout-demo-card
  slots:
    default:
    - type: stack
      props:
        gap: xs
        direction: column
      children:
      - type: text
        props:
          kind: heading
          value: Security
      - type: text
        props:
          kind: muted
          value: Sessions, MFA, and tokens.
Four columns

Four-column dense layout.

1
2
3
4
YAML
type: columns
props:
  count: four
  gap: sm
children:
- type: well
  props:
    padding: compact
    class: layout-demo-card layout-demo-card--compact
  slots:
    default:
    - type: badge
      slots:
        default: '1'
- type: well
  props:
    padding: compact
    class: layout-demo-card layout-demo-card--compact
  slots:
    default:
    - type: badge
      slots:
        default: '2'
- type: well
  props:
    padding: compact
    class: layout-demo-card layout-demo-card--compact
  slots:
    default:
    - type: badge
      slots:
        default: '3'
- type: well
  props:
    padding: compact
    class: layout-demo-card layout-demo-card--compact
  slots:
    default:
    - type: badge
      slots:
        default: '4'
Edit YAML

container

#existingsource

Container constrains page content to the platform reading width while preserving the standard page gutter. It is the outer wrapper for normal document-like surfaces where full-bleed chrome would make the content harder to scan.

Readiness
complete
Preview
live
Props
1
Examples
1
Code
implementation mapped

When to use

  • Pages or panels whose primary content should align to the platform column
  • Documentation, settings, onboarding, and other reading-first surfaces
  • A stable outer wrapper before composing Grid, Section, or Stack

When not to use

  • Full-bleed application chrome such as workspace shells or browser panels
  • Small repeated cards or list rows; use Card, Section, or Stack instead
  • Dialogs and overlays, which own their own width and inset rules

Accessibility

Role: presentation

  • No keyboard shortcuts

Screen reader: Adds no landmark by itself; pair with semantic page regions inside.

Notes: Do not use Container as the only semantic wrapper around a page.

Props

NameTypeDefaultDescription
classstringOptional extra class for rare page-level variants.

Examples

Settings page column

A settings page column with a heading and primary action.

Workspace settingsReview access, defaults, and notification preferences.
YAML
type: container
children:
- type: stack
  props:
    gap: md
  children:
  - type: text
    props:
      kind: heading
      value: Workspace settings
  - type: text
    props:
      kind: muted
      value: Review access, defaults, and notification preferences.
  - type: button
    props:
      variant: primary
    slots:
      default: Save changes
Edit YAML

divider

#existingsource

A thin separator line between content groups. The orientation is an enum (horizontal full-width rule, or a vertical hairline between inline siblings) and the surrounding space is drawn from the shared gap token scale, so a divider can never introduce a magic pixel margin or a one-off border colour. Purely decorative dividers are hidden from assistive tech; set decorative=false when the break is semantically meaningful (e.g. between unrelated record groups).

Readiness
complete
Preview
live
Props
3
Examples
3
Code
implementation mapped

When to use

  • Separating sections inside a panel or settings group
  • A vertical hairline between inline toolbar actions
  • Visually grouping unrelated blocks without a heading
  • Replacing a raw <hr> so the colour tracks the theme

When not to use

  • Spacing between flex siblings — use Stack gap or Spacer
  • A titled, collapsible region — use Section
  • Card or surface edges — those own their own border
  • Decorative flourish — a divider is structural, not ornamental

Accessibility

Role: separator

  • No keyboard shortcuts

Screen reader: A decorative divider (the default) sets role=presentation and aria-hidden so it adds no screen-reader noise. A meaningful divider (decorative=false) is announced as a separator with aria-orientation matching its axis.

Notes: Do not rely on a divider alone to convey grouping for non-visual users — pair semantically distinct groups with headings or labels.

Props

NameTypeDefaultDescription
orientationenum: horizontal | verticalhorizontalAxis the rule runs along.
spacingenum: none | sm | md | lgmdMargin reserved on the cross axis of the rule.
decorativebooleantrueWhen true the divider is purely visual and removed from the a11y tree. When false it is announced as a thematic separator.

Examples

Horizontal rule

Default horizontal rule between two text blocks.

AccountManage your profile and security.
YAML
type: well
props:
  padding: cozy
  class: layout-demo-measure
slots:
  default:
  - type: stack
    props:
      gap: none
      direction: column
    children:
    - type: text
      props:
        kind: heading
        value: Account
    - type: divider
    - type: text
      props:
        kind: muted
        value: Manage your profile and security.
Vertical hairline

Vertical hairline separating inline toolbar actions.

YAML
type: well
props:
  padding: cozy
  class: layout-demo-measure
slots:
  default:
  - type: stack
    props:
      gap: sm
      direction: row
      align: center
    children:
    - type: button
      props:
        variant: ghost
      slots:
        default: Save
    - type: divider
      props:
        orientation: vertical
        spacing: sm
    - type: button
      props:
        variant: ghost
      slots:
        default: Discard
Spacing variants

Spacing scale comparison.

spacing: none
spacing: sm
spacing: md
spacing: lg
YAML
type: stack
props:
  gap: sm
  direction: column
children:
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: none
        direction: column
      children:
      - type: text
        props:
          kind: muted
          value: 'spacing: none'
      - type: divider
        props:
          spacing: none
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: none
        direction: column
      children:
      - type: text
        props:
          kind: muted
          value: 'spacing: sm'
      - type: divider
        props:
          spacing: sm
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: none
        direction: column
      children:
      - type: text
        props:
          kind: muted
          value: 'spacing: md'
      - type: divider
        props:
          spacing: md
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: none
        direction: column
      children:
      - type: text
        props:
          kind: muted
          value: 'spacing: lg'
      - type: divider
        props:
          spacing: lg
Edit YAML

grid

#existingsource

Grid lays out peer items in a responsive column system. It keeps column counts and gaps declared as component props instead of embedding one-off utility strings in product screens.

Readiness
complete
Preview
live
Props
6
Examples
1
Code
implementation mapped

When to use

  • A set of peer cards, metric rows, settings groups, or navigation tiles
  • Layouts that need one column on small screens and more columns on wider screens
  • Dashboard summaries where each child should keep equal visual weight

When not to use

  • Linear forms or ordered steps; use Stack so reading order remains obvious
  • Table-like data with headers and cell alignment requirements
  • Freeform canvas or graph layouts that need custom positioning

Accessibility

Role: presentation

  • No keyboard shortcuts

Screen reader: Preserves child order in the DOM; visual columns must not imply a different reading order.

Notes: Children remain responsible for their own headings, roles, and focus states.

Props

NameTypeDefaultDescription
colsinteger1Base column count.
cols_smintegerColumn count at the small breakpoint.
cols_mdintegerColumn count at the medium breakpoint.
cols_lgintegerColumn count at the large breakpoint.
gapstring1remCSS gap value between grid items.
classstringOptional extra class for constrained migration cases.

Examples

Three-column summary

Three equal operational summaries at desktop width.

Agents4 activehealthy
Runs18 todayqueued: 2
AlertsNoneclear
YAML
type: grid
props:
  cols: 1
  cols_md: 3
  gap: 1rem
children:
- type: well
  props:
    padding: cozy
    class: layout-demo-metric
  slots:
    default:
    - type: stack
      props:
        gap: xs
        direction: column
      children:
      - type: text
        props:
          kind: muted
          value: Agents
      - type: text
        props:
          kind: heading
          value: 4 active
      - type: badge
        props:
          variant: success
        slots:
          default: healthy
- type: well
  props:
    padding: cozy
    class: layout-demo-metric
  slots:
    default:
    - type: stack
      props:
        gap: xs
        direction: column
      children:
      - type: text
        props:
          kind: muted
          value: Runs
      - type: text
        props:
          kind: heading
          value: 18 today
      - type: badge
        props:
          variant: muted
        slots:
          default: 'queued: 2'
- type: well
  props:
    padding: cozy
    class: layout-demo-metric
  slots:
    default:
    - type: stack
      props:
        gap: xs
        direction: column
      children:
      - type: text
        props:
          kind: muted
          value: Alerts
      - type: text
        props:
          kind: heading
          value: None
      - type: badge
        props:
          variant: success
        slots:
          default: clear
Edit YAML

section

#existingsource

Section groups related content under a stable heading rhythm. It is the normal boundary inside a page when content needs a label, optional helper text, and a consistent content inset.

Readiness
complete
Preview
live
Props
5
Examples
1
Code
implementation mapped

When to use

  • Settings groups, documentation blocks, or dashboard subsections
  • A titled region inside a page-level Container
  • Advanced areas that can collapse without leaving the page

When not to use

  • Clickable repeated items; use Card or ItemButton
  • Page titles; use PageHeader
  • Expandable FAQ rows; use Disclosure or Accordion

Accessibility

Role: region

  • Enter — Toggles when the section is collapsible and the header has focus
  • Space — Toggles when the section is collapsible and the header has focus

Screen reader: Heading text identifies the region; collapsible sections expose expanded state.

Notes: Use concise titles so the region list remains useful.

Props

NameTypeDefaultDescription
titlestringOptional section heading.
subtitlestringSecondary text under the heading.
collapsiblebooleanfalseWhether the header toggles the content.
collapsedbooleanfalseInitial collapsed state when collapsible is true.
classstringOptional extra class for migration-only variants.

Examples

Settings section

A settings section with explanatory copy and a save action.

Notifications

Choose which platform events create alerts.

Operational alerts stay enabled for all admins.
YAML
type: section
props:
  title: Notifications
  subtitle: Choose which platform events create alerts.
children:
- type: text
  props:
    kind: muted
    value: Operational alerts stay enabled for all admins.
- type: button
  props:
    variant: secondary
    size: small
  slots:
    default: Edit rules
Edit YAML

spacer

#existingsource

A zero-flex box that reserves a token-sized amount of space on one axis. Reach for it only when Stack's gap cannot express the spacing — for example a single push between two blocks that are not Stack siblings, or reserving height inside a fixed container. The size is an enum from the shared gap scale, never a raw pixel value, so spacing rhythm stays consistent as the scale evolves.

Readiness
complete
Preview
live
Props
2
Examples
3
Code
implementation mapped

When to use

  • A one-off push between blocks that are not Stack siblings
  • Reserving a fixed gap inside a non-flex container
  • Separating two unrelated regions where a Divider would be too loud
  • Vertical breathing room before a footer in a fixed-height column

When not to use

  • Spacing between Stack/flex siblings — use the parent's gap
  • A visible separator line — use Divider
  • Pushing items apart in a flex row — use flex-grow on a real element
  • Page-level layout regions — use Grid or Sidebar

Accessibility

Role: presentation

  • No keyboard shortcuts

Screen reader: The spacer sets role=presentation and aria-hidden so it is entirely invisible to assistive technology — it carries no content.

Notes: Spacing must never be the only thing conveying a grouping boundary for non-visual users; use headings or labels for meaningful breaks.

Props

NameTypeDefaultDescription
sizeenum: xs | sm | md | lg | xlmdGap size, drawn from the shared token scale.
axisenum: vertical | horizontalverticalAxis the gap is reserved on.

Examples

Vertical gap

Vertical gap between two unrelated blocks.

BillingNext invoice on the 1st.
YAML
type: well
props:
  padding: cozy
  class: layout-demo-measure
slots:
  default:
  - type: stack
    props:
      gap: none
      direction: column
    children:
    - type: text
      props:
        kind: heading
        value: Billing
    - type: spacer
      props:
        size: lg
    - type: text
      props:
        kind: muted
        value: Next invoice on the 1st.
Horizontal gap

Horizontal gap separating inline badges.

Livev1.0.0
YAML
type: well
props:
  padding: cozy
  class: layout-demo-measure
slots:
  default:
  - type: stack
    props:
      gap: none
      direction: row
      align: center
    children:
    - type: badge
      props:
        variant: success
      slots:
        default: Live
    - type: spacer
      props:
        size: md
        axis: horizontal
    - type: badge
      props:
        variant: muted
      slots:
        default: v1.0.0
Size variants

Size scale comparison.

xs4px
sm8px
md16px
xl32px
YAML
type: stack
props:
  gap: sm
  direction: column
children:
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: none
        direction: row
        align: center
      children:
      - type: badge
        slots:
          default: xs
      - type: spacer
        props:
          size: xs
          axis: horizontal
      - type: text
        props:
          kind: muted
          value: 4px
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: none
        direction: row
        align: center
      children:
      - type: badge
        slots:
          default: sm
      - type: spacer
        props:
          size: sm
          axis: horizontal
      - type: text
        props:
          kind: muted
          value: 8px
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: none
        direction: row
        align: center
      children:
      - type: badge
        slots:
          default: md
      - type: spacer
        props:
          size: md
          axis: horizontal
      - type: text
        props:
          kind: muted
          value: 16px
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: none
        direction: row
        align: center
      children:
      - type: badge
        slots:
          default: xl
      - type: spacer
        props:
          size: xl
          axis: horizontal
      - type: text
        props:
          kind: muted
          value: 32px
Edit YAML

splitter

#existingsource

Two panes separated by a draggable handle. The user drags (pointer) or uses the keyboard (arrows to nudge, Home/End to collapse) to change the split. The handle is a true ARIA separator carrying live value semantics, the split fraction is clamped to a sane range, and the drag/keyboard state uses Arc reactive primitives so the component is safe inside disposing <Show>/<For> scopes.

Readiness
complete
Preview
live
Props
3
Examples
3
Code
implementation mapped

When to use

  • A side-by-side editor and preview that the user can resize
  • A list/detail layout where the user controls the split
  • Two panels that should share space on a ratio the user owns
  • A diff view with adjustable before/after panes

When not to use

  • A fixed-width nav rail plus content — use Sidebar
  • Equal-weight, non-resizable columns — use Columns
  • A free tile grid — use Grid
  • Simple stacked content — use Stack

Accessibility

Role: separator

  • ArrowLeft — Shrink leading pane (horizontal split)
  • ArrowRight — Grow leading pane (horizontal split)
  • ArrowUp — Shrink leading pane (vertical split)
  • ArrowDown — Grow leading pane (vertical split)
  • Home — Collapse the leading pane toward minimum
  • End — Grow the leading pane toward maximum

Screen reader: The handle is role="separator" with tabindex 0, an aria-label, aria-orientation matching the separator axis (perpendicular to the split direction), and aria-valuemin / aria-valuemax / aria-valuenow reporting the current split percentage.

Notes: aria-orientation is the orientation of the separator itself, which is perpendicular to the split: a side-by-side (horizontal) split has a vertical separator. The handle is fully keyboard operable so a pointer is never required.

Props

NameTypeDefaultDescription
orientationenum: horizontal | verticalhorizontalSplit axis.
initialnumber50Initial size of the leading pane, in percent (clamped 8–92).
aria_labelstringResize panesAccessible label for the resize handle.

Examples

Horizontal split

Side-by-side editor and preview the user can resize.

EditorPrompt and configuration pane
PreviewLive output stays beside the editor
YAML
type: splitter
props:
  orientation: horizontal
  initial: 50
  aria_label: Resize editor and preview
slots:
  leading:
  - type: well
    props:
      padding: cozy
      class: layout-demo-pane
    slots:
      default:
      - type: stack
        props:
          gap: sm
          direction: column
        children:
        - type: badge
          props:
            variant: primary
          slots:
            default: Editor
        - type: text
          props:
            kind: muted
            value: Prompt and configuration pane
  trailing:
  - type: well
    props:
      padding: cozy
      class: layout-demo-pane
    slots:
      default:
      - type: stack
        props:
          gap: sm
          direction: column
        children:
        - type: badge
          props:
            variant: muted
          slots:
            default: Preview
        - type: text
          props:
            kind: muted
            value: Live output stays beside the editor
Vertical split

Stacked split with a smaller leading pane.

ConsoleBuild started
Output streamCompilation logs and traces
YAML
type: splitter
props:
  orientation: vertical
  initial: 35
  aria_label: Resize output panel
slots:
  leading:
  - type: well
    props:
      padding: cozy
      class: layout-demo-pane
    slots:
      default:
      - type: stack
        props:
          gap: xs
          direction: column
        children:
        - type: text
          props:
            kind: heading
            value: Console
        - type: text
          props:
            kind: muted
            value: Build started
  trailing:
  - type: well
    props:
      padding: cozy
      class: layout-demo-pane
    slots:
      default:
      - type: stack
        props:
          gap: xs
          direction: column
        children:
        - type: text
          props:
            kind: muted
            value: Output stream
        - type: text
          props:
            kind: muted
            value: Compilation logs and traces
Wide leading pane

Leading pane starting near the maximum.

80%
20%
YAML
type: splitter
props:
  orientation: horizontal
  initial: 80
slots:
  leading:
  - type: well
    props:
      padding: cozy
      class: layout-demo-pane
    slots:
      default:
      - type: badge
        props:
          variant: primary
        slots:
          default: 80%
  trailing:
  - type: well
    props:
      padding: cozy
      class: layout-demo-pane
    slots:
      default:
      - type: badge
        props:
          variant: muted
        slots:
          default: 20%
Edit YAML

stack

#existingsource

Vertical or horizontal flex container with gap, direction, and alignment drawn from token-driven enums. The single canonical way to express spacing rhythm in a panel.

Readiness
complete
Preview
live
Props
3
Examples
3
Code
implementation mapped

When to use

  • Vertical column with consistent spacing — `direction: column`
  • Horizontal row with consistent spacing — `direction: row` (no separate Inline component)
  • Replacing inline-style margins/padding between siblings
  • Form field stacking (use with `field` children)

When not to use

  • 2D grid layouts — use Grid
  • Centering a single child — use Center primitive (when it lands)
  • Inline text wrapping — Stack is block-flex, not inline
  • Splittable resizable panes — use Splitter (when it lands)

Accessibility

Role: presentation

  • No keyboard shortcuts

Screen reader: No semantic role; layout-only. DOM order matches visual order — do not use flex-order to reorder, as it diverges screen-reader from visual sequence.

Notes: For grouped form controls, wrap with `<fieldset>` (use Fieldset primitive) for proper form-association semantics.

Props

NameTypeDefaultDescription
gapenum: none | xs | sm | md | lg | xlmdGap between children. Token-driven; the px values live in CSS and change globally if the spacing scale changes.
directionenum: column | rowcolumn
alignenum: start | center | end | stretchstretchCross-axis alignment of children.

Examples

Login form stack

Login form fields and submit button (replaces inline-style spacing in PasswordLoginForm).

YAML
type: stack
props:
  gap: md
  direction: column
children:
- type: field
  props:
    label: Username
    field_id: login-username
  slots:
    default:
    - type: input
      props:
        value: ${state.username}
        on_input: ${actions.set_username}
- type: field
  props:
    label: Password
    field_id: login-password
  slots:
    default:
    - type: input
      props:
        value: ${state.password}
        on_input: ${actions.set_password}
        input_type: password
- type: button
  props:
    variant: primary
    type: submit
    loading: ${state.is_signing_in}
  slots:
    default: Sign in
Symmetric CTA row

Symmetric landing-page CTAs (replaces the asymmetric Login-glows / Register-slides bug).

YAML
type: stack
props:
  gap: sm
  direction: row
  align: center
children:
- type: button
  props:
    variant: primary
    on_click: ${actions.open_login_form}
  slots:
    default: Sign in
- type: button
  props:
    variant: primary
    on_click: ${actions.open_register}
  slots:
    default: Create account
Gap scale

All gap variants vertically for visual comparison.

noneAB
xsAB
smAB
mdAB
lgAB
xlAB
YAML
type: stack
props:
  gap: sm
  direction: column
children:
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: none
        direction: row
        align: center
      children:
      - type: badge
        slots:
          default: none
      - type: badge
        slots:
          default: A
      - type: badge
        slots:
          default: B
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: xs
        direction: row
        align: center
      children:
      - type: badge
        slots:
          default: xs
      - type: badge
        slots:
          default: A
      - type: badge
        slots:
          default: B
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: sm
        direction: row
        align: center
      children:
      - type: badge
        slots:
          default: sm
      - type: badge
        slots:
          default: A
      - type: badge
        slots:
          default: B
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: md
        direction: row
        align: center
      children:
      - type: badge
        slots:
          default: md
      - type: badge
        slots:
          default: A
      - type: badge
        slots:
          default: B
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: lg
        direction: row
        align: center
      children:
      - type: badge
        slots:
          default: lg
      - type: badge
        slots:
          default: A
      - type: badge
        slots:
          default: B
- type: well
  props:
    padding: compact
    class: layout-demo-measure
  slots:
    default:
    - type: stack
      props:
        gap: xl
        direction: row
        align: center
      children:
      - type: badge
        slots:
          default: xl
      - type: badge
        slots:
          default: A
      - type: badge
        slots:
          default: B
Edit YAML