Layout primitives
Composable regions for desktop SaaS / web-app surfaces — not opinionated
page designs. Low specificity (:where()), token-driven gaps,
and each primitive exposes --l-* instance-config tokens with
framework defaults. Defaults assume comfortable desktop density;
.l-sidebar adapts to its panel width via a container
query, not the viewport.
.l-grid — dashboard widgets
Auto-fitting tracks; set --l-grid-min for a wider floor.
.l-cluster — action bar
A wrapping row of filters and actions; --l-cluster-justify: flex-end to right-align.
.l-stack + .l-sidebar — settings panel
Resize the demo panel's handle (or narrow the window) to cross 48rem:
the sidebar collapses to a single column because the
wrapping panel (.demo-panel) is the container.
.l-scroll-area — data pane
Independent scroll with a stable gutter (no layout shift when the bar appears) and contained overscroll.
| Account | Plan | Status | MRR |
|---|---|---|---|
| Acme Ops | Enterprise | Active | $2,400 |
| Northwind | Pro | Trialing | $980 |
| Globex | Pro | Past due | $980 |
| Initech | Team | Active | $420 |
| Umbrella | Enterprise | Active | $2,400 |
| Stark Industries | Pro | Active | $980 |
| Acme Ops | Enterprise | Active | $2,400 |
| Northwind | Pro | Trialing | $980 |
| Globex | Pro | Past due | $980 |
| Initech | Team | Active | $420 |
| Umbrella | Enterprise | Active | $2,400 |
| Stark Industries | Pro | Active | $980 |
.l-page — readable column
Centered content within a wide surface; tighten --l-page-max for prose.
Long-form guidance, release notes, or policy text — kept readable even when the surrounding app runs edge-to-edge.
.l-container — centered app wrapper
General-purpose max-width container for dashboards, settings, and lists.
Distinct from .l-page (prose-focused, 88rem). Default 80rem;
tune via --l-container-max.
This content is centered and capped at 80rem.
.l-columns — multi-column masonry
CSS multi-column layout for card walls, search results, and image grids.
Set column count via --l-columns-count or let it auto-fit.
Items use break-inside: avoid so cards don't split.
Each card flows into the next column without splitting. Resize to see reflow.
Each card flows into the next column without splitting. Resize to see reflow.
Each card flows into the next column without splitting. Resize to see reflow.
Each card flows into the next column without splitting. Resize to see reflow.
Each card flows into the next column without splitting. Resize to see reflow.
Each card flows into the next column without splitting. Resize to see reflow.
.l-inset — constrained content in full-bleed
Centers and constrains content inside a full-width parent (hero, banner).
Default 48rem; tune via --l-inset-max.
Use for captions, CTAs, or forms within a wide hero section.
Full layout index
| Primitive | Purpose | Tunable token |
|---|---|---|
.l-stack | Vertical flow (forms, sections) | --l-stack-gap |
.l-cluster | Horizontal wrapping group (buttons, filters) | --l-cluster-gap |
.l-grid | Auto-fitting card grid | --l-grid-min |
.l-sidebar | Two-column with container-query collapse | --l-sidebar-size |
.l-page | Centered prose column (88rem) | --l-page-max |
.l-scroll-area | Independent scroll region | — |
.l-app-shell | Full SaaS shell (topbar + sidebar + main) | --app-sidebar-size |
.l-container | Centered app wrapper (80rem) | --l-container-max |
.l-columns | Multi-column masonry | --l-columns-count |
.l-inset | Constrained content in full-bleed | --l-inset-max |