Manual Style Layout

Layout

elements man style/layout Read as markdown

Layout primitives are composition containers. This is layout.css in @layer components.

The composition contract

One rule governs spacing in the system:

Layout containers own spacing. Components never set outer margins.

No component in the design system puts a margin on its outer element. If you need space between two things, wrap them in a .stack or .cluster and let the container's gap do it. This keeps components portable. The same card works inside any layout because it never assumes a margin.

Flow primitives

.stack: vertical flow with a consistent gap between children. Gap variants is-xs / is-sm / is-md / is-lg / is-xl / is-2xl step the gap up.

<div class="stack is-lg">
  <h2>Account</h2>
  <p>Manage your profile and billing.</p>
  <button class="is-primary">Save</button>
</div>

.cluster: horizontal flow that wraps, vertically centered. For rows of tags, buttons, or metadata that should wrap on narrow screens. Gap variants is-xs, is-sm, is-md, is-lg, is-xl.

.row: horizontal flex that does not wrap. Alignment variants is-baseline / is-start / is-end / is-stretch (default is centered).

Page structure

.page-shell: a width-constrained, centered page container with a gutter. Width variants:

Every width is spelled out. Pick by the number, not by the word: is-narrow is a phone-width column, and it is the wrong choice for most pages.

Variant Max width Use for
is-narrow --container-sm (24rem / 384px) A sidebar or a single narrow card. Not a page.
is-form --container-2xl (42rem / 672px) A form, a list, a settings page. The usual choice for an app page.
is-prose --container-prose (65ch) Running text.
is-marketing --container-5xl (64rem / 1024px) A landing page.
(default) --container-shell (72rem / 1152px) A general app page.
is-wide --container-wide (80rem / 1280px) Tables and dashboards.
is-full none Edge to edge.

.page-hero: a hero block at the top of a page with generous top/bottom padding; is-sm / is-lg adjust it. It also sets rhythm for a direct .eyebrow, h1/.display, and .lede inside it, and constrains the lede to a readable measure.

.section: a vertical-rhythm block between content areas; is-sm / is-lg adjust the padding. Adjacent sections (.section + .section) get a soft top rule automatically, so stacked sections divide themselves.

<main class="page-shell is-prose">
  <div class="page-hero">
    <p class="eyebrow">Changelog</p>
    <h1 class="display">What's new</h1>
    <p class="lede">Recent releases and fixes.</p>
  </div>

  <section class="section"></section>
  <section class="section"></section>
</main>

App shell

.app: a full-height grid shell for an application layout (as opposed to a content page).

.app.has-sidebar: adds a sidebar column. The width comes from --sidebar-width (default 240px); set that token to change it. The layout collapses to a single column at 48rem. .app-main is the main content column (a flex column that won't overflow its track).

<div class="app has-sidebar" style="--sidebar-width: 280px">
  <aside></aside>
  <div class="app-main"></div>
</div>

.app.is-fixed: the shell is exactly the viewport and never grows, so a row inside it scrolls on its own. Reach for it when a region scrolls while the chrome around it stays put: a message log under a header with a composer pinned below, a table under a toolbar. Give the shell its rows, put overflow-y: auto on the element that scrolls, and the rest follows.

<div class="app is-fixed" style="grid-template-rows: auto 1fr auto">
  <header></header>
  <div class="app-main">
    <ul class="feed" style="overflow-y: auto"></ul>
  </div>
  <footer></footer>
</div>

Without it the shell is min-height, so it grows with its content: the log runs past the bottom of the window, the composer goes with it, and the feed never scrolls because it was never bounded. is-fixed also clears the min-height a grid item takes from its content, which is the part that is easy to miss when writing this by hand.

.app.has-sidebar is a layout grid hook, not a sidebar component. It gives you the two-column grid and the responsive collapse. What goes in the sidebar (navigation, its styling, any collapse toggle) is yours to build; v1 ships no sidebar component.

Helpers

  • .center: flex-center on both axes; drop something in the middle of its container.
  • .divider: a visible horizontal rule (a filled 1px bar, not a border). Variants: is-soft / is-strong change the weight, and is-vertical turns it into a vertical divider that stretches inside a .row or .cluster.
<div class="row">
  <span>Draft</span>
  <span class="divider is-vertical"></span>
  <span>Edited 2m ago</span>
</div>