Page

A layout primitive that owns the vertical rhythm inside the main column.

Basic

The .page flow is a flex column whose gap spaces a .page__header and a series of .page__section siblings, so children carry no outer margin (the reboot already zeroes headings and paragraphs). The header pairs a .page__headline (title + description) with a trailing .page__action; sections repeat the shape at a tighter scale. Title and section-title carry opinionated cadences so you drop the recurring heading utility. A header with a title, a muted description, and an action, followed by a section with its own header.

The page wrapper

Drop a .page inside the main column. .page__header carries the title and primary actions and sits above .page__body, the flow that holds the sections. The header's bottom margin supplies the rhythm between them.

Page header

.page__header is a flex row with a .page__headline group on the leading edge and a .page__action slot on the trailing edge. The heading group stacks a .page__title with an optional .page__description; the action slot takes any button row.

Without actions

Drop the action slot. The title block sits alone.

Sections

.page__section is a flex-column for each block inside the page. The .page parent supplies the gap between sections; each section supplies its own inner gap via --page-section-inner-gap. Pair .page__section-title with .page__section-description for muted helper copy beneath it, the section-scale counterpart to .page__description.

With section header + actions

For sections that need their own action row, wrap the title in .page__section-header and reuse .page__action as the slot. Same flex recipe at a tighter scale.

Content width

.page is unopinionated about width. Wrap it (or set its own width on a parent) to clamp the content column.

  • Full width (.container-fluid or no container) for dashboards, tables, anything dense.
  • Breakpoint-clamped (.container) for marketing pages, or anything that should match the standard column widths.
  • Custom max-width (40 to 60rem) for settings forms, articles, anything text-heavy where long lines hurt readability.
Container

Nesting a container

Putting .container on the .page element itself, as above, keeps the sections as direct children, so the row gap still spaces them. When the container has to be a separate wrapper, it becomes the only child of .page and the sections inside it fall outside the gap. Move the flow into .page__body. It owns the same section rhythm, so a container can sit between the frame and the content without flattening the spacing.

Nesting Container

.page__body reads the same --page-section-gap as .page, so a gap override set on the page still reaches the nested sections. Reach for it only when a container nests inside the page; the plain .page already flows its own direct children.

Putting it together

Page header, sections, and a section with its own header, all inside one .page that supplies the rhythm.

Customization

These variables retune the flow and the heading cadences. Override on the root or any wrapper.

VariableUse
--page-section-gapGap between the header and each section (cascades to .page__body)
--page-header-gapGap between the headline and the actions
--page-heading-gapGap between a title and its description
--page-action-gapGap between action buttons
--page-section-inner-gapGap between a section header and its content
--page-section-header-gapGap inside a section header
--page-title-font-size / -title-font-weightPage title type
--page-description-font-size / -description-colorPage description type
--page-section-title-font-size / -section-title-font-weightSection title type
--page-section-description-font-size / -section-description-colorSection description type