Layout primitives

Composable regions for desktop SaaS / web-app surfaces — not opinionated page designs. Low specificity (:where()), token-driven gaps, and each primitive exposes --pl-l-* instance-config tokens with framework defaults. Defaults assume comfortable desktop density;.pl-l-sidebar adapts to its panel width via a container query, not the viewport.

.pl-l-grid — dashboard widgets

Auto-fitting tracks; set --pl-l-grid-min for a wider floor.

Active seats1,284+4.2%
MRR$48.2k+1.1%
Churn1.4%-0.3%
Open tickets23

.pl-l-cluster — action bar

A wrapping row of filters and actions; --pl-l-cluster-justify: flex-end to right-align.

.pl-l-stack + .pl-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 thewrapping panel (.demo-panel) is the container.

General

Workspace name, default locale, and session preferences live here.

.pl-l-scroll-area — data pane

Independent scroll with a stable gutter (no layout shift when the bar appears) and contained overscroll.

AccountPlanStatusMRR
Acme OpsEnterpriseActive$2,400
NorthwindProTrialing$980
GlobexProPast due$980
InitechTeamActive$420
UmbrellaEnterpriseActive$2,400
Stark IndustriesProActive$980
Acme OpsEnterpriseActive$2,400
NorthwindProTrialing$980
GlobexProPast due$980
InitechTeamActive$420
UmbrellaEnterpriseActive$2,400
Stark IndustriesProActive$980

.pl-l-page — readable column

Centered content within a wide surface; tighten --pl-l-page-max for prose.

Long-form guidance, release notes, or policy text — kept readable even when the surrounding app runs edge-to-edge.

.pl-l-container — centered app wrapper

General-purpose max-width container for dashboards, settings, and lists. Distinct from .pl-l-page (prose-focused, 88rem). Default 80rem; tune via --pl-l-container-max.

This content is centered and capped at 80rem.

.pl-l-columns — multi-column masonry

CSS multi-column layout for card walls, search results, and image grids. Set column count via --pl-l-columns-count or let it auto-fit. Items use break-inside: avoid so cards don't split.

Card 1

Each card flows into the next column without splitting. Resize to see reflow.

Card 2

Each card flows into the next column without splitting. Resize to see reflow.

Card 3

Each card flows into the next column without splitting. Resize to see reflow.

Card 4

Each card flows into the next column without splitting. Resize to see reflow.

Card 5

Each card flows into the next column without splitting. Resize to see reflow.

Card 6

Each card flows into the next column without splitting. Resize to see reflow.

.pl-l-inset — constrained content in full-bleed

Centers and constrains content inside a full-width parent (hero, banner). Default 48rem; tune via --pl-l-inset-max.

This inset is centered inside a full-bleed surface.

Use for captions, CTAs, or forms within a wide hero section.

Full layout index

PrimitivePurposeTunable token
.pl-l-stackVertical flow (forms, sections)--pl-l-stack-gap
.pl-l-clusterHorizontal wrapping group (buttons, filters)--pl-l-cluster-gap
.pl-l-gridAuto-fitting card grid--pl-l-grid-min
.pl-l-sidebarTwo-column with container-query collapse--pl-l-sidebar-size
.pl-l-pageCentered prose column (88rem)--pl-l-page-max
.pl-l-scroll-areaIndependent scroll region—
.pl-l-app-shellFull SaaS shell (topbar + sidebar + main)--pl-app-sidebar-size
.pl-l-containerCentered app wrapper (80rem)--pl-l-container-max
.pl-l-columnsMulti-column masonry--pl-l-columns-count
.pl-l-insetConstrained content in full-bleed--pl-l-inset-max