Layout
How Container, Section, Band, GridShell, Frame and the rails compose into the canonical Cronwerk page anatomy — with a full annotated example page.
The canonical page anatomy
Every Cronwerk page is the same five layers, outside-in:
- Page wrapper — one
position: relative; isolation: isolateelement spanning the page. It exists so the chrome and the rails have something to anchor to;isolation: isolatekeeps GridShell’s negative z-index above ancestor backgrounds. - GridShell (Storybook: Components/GridShell) —
the decorative chrome behind everything: dot-grid gutters outside the
1200px column and four dashed verticals running the full page height.
aria-hidden,pointer-events: none, desktop only. - Sections — the page is a vertical stack of
Sections (transparent, tokens-driven 120px/160px rhythm) andBands (the full-width tinted variant for announce/manifesto strips). Separate sections withSectionRulewhen a boundary needs a visible dashed rule. - Container — inside every Section/Band, one
Containercenters the content at--container-max(1200px) with the responsive gutter. Band paints full-width; Container keeps its copy on the column. - Content spacing — inside the Container,
Stack(vertical) andCluster(horizontal wrap) own every gap on the 8px--space-*scale;Framewraps a block in the hairline + corner-handle look;DotGridRailruns decorative dot columns down a section edge.
The anatomy, composed
The full composed example — the same composition ships as the
Guidelines/Layout → CanonicalPage story in Storybook, where it is captured
by VRT in both themes on every PR, so this anatomy cannot drift silently.
Canonical page anatomy — annotated source
<div style="position: relative; isolation: isolate"> {/* 1 — page wrapper */}
<GridShell /> {/* 2 — chrome behind everything */}
<Section aria-labelledby="hero-h"> {/* 3 — transparent, 120/160px rhythm */}
<Container> {/* 4 — the 1200px column */}
<Stack gap={6} align="start"> {/* 5 — 8px-scale spacing */}
<Cluster gap={2}>
<Chip>Cron-native</Chip>
<Chip>Hairline chrome</Chip>
</Cluster>
<h1 id="hero-h">Work that runs itself.</h1>
<Cluster gap={4}>
<Button>Book a diagnostic</Button>
<Button variant="outline">See the console</Button>
</Cluster>
</Stack>
</Container>
</Section>
<Container><SectionRule /></Container> {/* dashed section boundary */}
<Section aria-labelledby="how-h">
<DotGridRail side="left" /> {/* decorative edge dots */}
<Container>
<Frame label="agents / weekly-digest">
...framed block...
</Frame>
</Container>
</Section>
<Band tone="soft" aria-labelledby="band-h"> {/* full-width tinted strip */}
<Container>
<h2 id="band-h">Announce band</h2>
</Container>
</Band>
</div> Composition rules
- Sections stay transparent. The GridShell chrome must show through —
a Section never paints a background. When a strip needs a tint, that is a
Band(softorcard), never a styled Section. - One Container per Section. Content never touches the viewport edge; Band paints edge-to-edge but its content still sits in a Container.
- All spacing is Stack/Cluster. If you are writing
margin-topbetween siblings inside a section, reach forStack(vertical) orCluster(wrapping row) with a--space-*step instead. - Decor is decor. GridShell, DotGridRail, DashedRule and the Frame
handles are
aria-hiddenand non-interactive — never hang content or handlers on them.
Do
Mount GridShell once, on the page wrapper, and let every Section stay transparent above it.
Don't
Don’t mount a GridShell per section or paint section backgrounds — the gutter dots and verticals must run unbroken down the page.
Do
Use SectionRule (role=“separator”) when a section boundary needs the dashed rule; give landmark Sections an accessible name via aria-labelledby.
Don't
Don’t fake separators with borders on Sections, and don’t use the full-bleed DashedRule where an in-flow, inset rule is wanted.