Cronwerk UI

Layout

How Container, Section, Band, GridShell, Frame and the rails compose into the canonical Cronwerk page anatomy — with a full annotated example page.

stable

The canonical page anatomy

Every Cronwerk page is the same five layers, outside-in:

  1. Page wrapper — one position: relative; isolation: isolate element spanning the page. It exists so the chrome and the rails have something to anchor to; isolation: isolate keeps GridShell’s negative z-index above ancestor backgrounds.
  2. 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.
  3. Sections — the page is a vertical stack of Sections (transparent, tokens-driven 120px/160px rhythm) and Bands (the full-width tinted variant for announce/manifesto strips). Separate sections with SectionRule when a boundary needs a visible dashed rule.
  4. Container — inside every Section/Band, one Container centers the content at --container-max (1200px) with the responsive gutter. Band paints full-width; Container keeps its copy on the column.
  5. Content spacing — inside the Container, Stack (vertical) and Cluster (horizontal wrap) own every gap on the 8px --space-* scale; Frame wraps a block in the hairline + corner-handle look; DotGridRail runs 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.

Cron-nativeHairline chrome

Work that runs itself.

agents / weekly-digest

Framed content

Announce band

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 (soft or card), 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-top between siblings inside a section, reach for Stack (vertical) or Cluster (wrapping row) with a --space-* step instead.
  • Decor is decor. GridShell, DotGridRail, DashedRule and the Frame handles are aria-hidden and 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.