Skip to content
VVVER

Design System / 07.4

Layout

Primitives for page rhythm, responsive shells, sticky sequences, product framing, and content grids.

Component reference

Stack

The flex layout primitive that keeps spacing on the system scale, from the 3px mosaic gutter to the viewport-relative section rhythm tiers. It spaces children and does nothing else.

Live specimen

Rendered output

Column, gap="sm"

Small gap.

Shared rhythm.

Source order.

Row, baseline, justify="between"

InkPaperRules

API reference

Inputs, types, and behavior

Inputgap
TypeStackGap = "md"
Behaviornone | flush (3px mosaic) | xs | sm | md | lg | rhythm-sm/md/lg (viewport-relative section tiers).
Inputdirection
Type"column" | "row"
BehaviorDefault "column".
Inputalign / justify
Typestring
BehaviorWord values mapped to static utilities (start, center, between, …).
Inputas / wrap
TypeElementType / boolean
BehaviorSwap the element ("section", "ul", …); wrap enables flex-wrap. No padding or margin props by design.

Component reference

Sticky stack

Chaptered sections that pin and stack as you scroll. Each sticky panel slides over the last at an escalating z-index, with a hairline top rule. Pure CSS positioning — no script.

Live specimen

Rendered output

Structure

Sections pin in source order with an escalating stacking level.

Spacing

Each panel keeps its own content rhythm and hairline boundary.

Source order

The same document sequence remains readable without sticky support.

API reference

Inputs, types, and behavior

Inputtop
Typenumber = 0
BehaviorShared sticky offset in px — set it to your fixed-nav height.
Inputchildren
TypeStickyStackSection[]
BehaviorSections receive escalating z-index; per-section top (number = 0) / zIndex (number = index + 1) props win.
InputclassName
Typestring?
BehaviorMerged onto the StickyStack root element.
InputclassName (section)
Typestring?
BehaviorSections default to min-h-[70svh] paper — override height and padding here.

Component reference

Sticky scroll

A pinned panel synced to scrolling steps. The steps column scrolls past while one panel stays fixed, crossfading its media as each step’s band reaches viewport centre. Scroll this section to drive it. Below md the panel unpins and each step’s media renders inline. The active step is derived from scroll position, so it stays correct under reduced motion — a content swap, not an animation.

Live specimen

Rendered output

01

Supplied media

The pinned panel displays consumer-provided media while the step keeps the explanatory copy in source order.
Woman seated on the steps of a pale green garden shed
02

Scroll position

The active step is derived from position rather than an ambient animation or automatic timer.
Woman seated on the steps of a pale green garden shed
03

Text fallback

A step without media remains complete by using its title in the visual panel.

API reference

Inputs, types, and behavior

Inputsteps
Type{ title, content, media? }[]
BehaviorThe pinned panel shows media; a step with no media falls back to its title set large.
InputmediaSide
Type"left" | "right"
BehaviorSide the pinned panel occupies at md+, default "right".
Inputtop
Typenumber = 0
BehaviorSticky top offset in px for the pinned panel — pass your header height.
InputheadingAs
Type"h2" | "h3" | "h4"
BehaviorStep-title heading level. Default "h3".
InputclassName
Typestring?
BehaviorMerged onto the outer grid.

Component reference

Split landing shell

A page-scale split with a persistent lead rail and a longer detail column. The lead pins at lg, then returns to ordinary source order on smaller screens. It owns only the split, seam, and sticky geometry; the host supplies headings, navigation, actions, and supporting content.

Live specimen

Rendered output

Product thesis

Source package contract

The short proposition remains in view while the reader tests it against complete product surfaces.

01

Readme

State the contract in a measured text column.

02

Detail matrix

Pair each claim with a working example.

03

Working surface

Show the actual interface, not a generic illustration.

API reference

Inputs, types, and behavior

Inputlead / children
TypeReactNode / ReactNode
BehaviorLead rail first in source order; children form the independent detail column.
InputstickyTop
Typenumber = 0
BehaviorNon-negative desktop offset in px for a host-owned fixed header.
InputleadClassName / contentClassName
Typestring? / string?
BehaviorMerged last onto the sticky lead wrapper and detail column.
InputclassName
Typestring?
BehaviorMerged last onto the two-column root.

Component reference

Detail showcase

A single-select accordion of titled rows with a synced media panel. Exactly one row is open at a time, and the panel shows that row’s media. It follows the disclosure-set keyboard model: Tab reaches every header, Up/Down/Home/End move focus, and Enter/Space activate. The panel media is illustrative only; below md it renders inline in the open row instead. Disclosure contents settle in 300ms and retreat in 180ms; the pinned media crossfades more slowly.

Live specimen

Rendered output

The active disclosure controls the synchronized media panel while its copy stays available in the open row.
Woman seated on the steps of a pale green garden shed

API reference

Inputs, types, and behavior

Inputitems
Type{ title, content, media? }[]
BehaviorExactly one open at a time; the synced panel shows the active media (title fallback when absent).
InputdefaultIndex
Typenumber = 0
BehaviorRow open on mount, with an out-of-range clamp.
InputmediaSide
Type"left" | "right"
BehaviorSide the media panel occupies at md+, default "right".
InputheadingAs
Type"h2" | "h3" | "h4"
BehaviorItem-title heading level. Default "h3".
InputclassName
Typestring?
BehaviorMerged onto the outer grid.

Component reference

Section

The foundational band: a full-bleed register wrapping a centered measure. Vertical space rides the rhythm tokens; the horizontal gutter follows --gutter-fluid and the display safe area. invert flips the band to the dark register edge to edge — a static swap, not the scroll-driven SectionInvert.

Live specimen

Rendered output

width="prose" · rhythm="sm"

A prose measure caps the line near 68ch so long-form copy stays legible. The band paints paper by default.

invert · dark register

The same band with invert — ink fills bleed-to-bleed so it punctuates the page as a dark register.

API reference

Inputs, types, and behavior

Inputrhythm
Type"sm" | "md" | "lg"
BehaviorVertical padding tier off the viewport-relative --rhythm-* tokens. Default "md".
Inputwidth
Type"prose" | "default" | "wide" | "full"
BehaviorMeasure of the centered container: prose 68ch, default 1200px, wide 1440px, full edge-to-edge.
Inputinvert
Typeboolean
BehaviorFlips the band to the dark register via .dark-section. Static swap.
Inputgutter
Typeboolean
BehaviorDefault true; adds the fluid horizontal gutter with safe-area insets. Set false for a flush container.
Inputas
Type"section" | "div"
BehaviorRendered tag, default "section".
Inputid / className
Typestring?
Behaviorid lands on the band; className merges onto it.

Component reference

Section heading

The house section heading: a mono eyebrow, a fluid title, an optional lede dek, and a trailing action. When start-aligned, the action baselines against the title from md up and stacks below on mobile; when centered, it stacks along the same axis.

Live specimen

Rendered output

Selected work

Projects, 2021—2024

A slot-based heading that pairs a mono eyebrow with a fluid title and an optional supporting line.

Studio

What we make

API reference

Inputs, types, and behavior

Inputtitle
TypeReactNode
BehaviorPlain text renders at the fluid title tier; a display component carries its own scale.
Inputeyebrow
TypeReactNode?
BehaviorMono micro-label above the title.
Inputdek
TypeReactNode?
BehaviorLede paragraph on --fluid-lede, dimmed, capped near 68ch.
Inputaction
TypeReactNode?
BehaviorTrailing slot — baselines against the title (start) or stacks below (center).
Inputalign
Type"start" | "center"
BehaviorDefault "start".
Inputas
Type"h2" | "h3"
BehaviorHeading level and its fluid size tier. Default "h2".
InputclassName
Typestring?
BehaviorMerged onto the wrapper.

Component reference

Feature grid

Editorial feature grid on paper. divided (default) lets cells draw shared 1px top/left rules while an overflow clip removes the perimeter, so ragged rows keep no empty painted tracks. Feed items for the data API or compose your own cells as children; an item with href makes the whole cell a self-dimming link.

Live specimen

Rendered output

Systems

Shared tokens keep spacing, color, and motion values consistent.

Identity

Mark geometry and named type roles stay fixed across contexts.

API reference

Inputs, types, and behavior

Inputitems
Type{ title, body?, icon?, href? }[]
BehaviorData API. href turns the cell into a self-dimming link. Ignored when children are supplied.
Inputchildren
TypeReactNode?
BehaviorCompose cells directly; takes precedence over items.
Inputcolumns
Type2 | 3 | 4
BehaviorColumn count at md+ (4 steps through a 2-up at md). Default 3.
Inputdivided
Typeboolean
BehaviorDefault true — hairline gutter separators. false gives discrete bordered cards.
InputheadingAs
Type"h2" | "h3" | "h4"
BehaviorHeading level for data-api item titles. Default "h3".
InputclassName
Typestring?
BehaviorMerged onto the grid.

Component reference

Feature proof grid

A numbered feature matrix that pairs each claim with a compact example, diagram, metric, or interface fragment. Linked cells dim the full-ink title and settle their proof by one pixel; body and proof copy keep their specified contrast. Reduced motion keeps the proof still.

Live specimen

Rendered output

01

Explicit source

The public surface stays visible and alphabetized.
02

Measured contrast

Readable text uses at least the sixty-percent ink rung.

API reference

Inputs, types, and behavior

Inputitems
TypeFeatureProofItem[]
BehaviorEach item takes a title, optional body, proof, and safe href. Proof content may remain visible to assistive technology.
Inputcolumns
Type2 | 3 = 3
BehaviorOne column on phones; three-column mode passes through two columns before xl.
InputstartIndex / headingAs
Typenumber = 1 / "h2" | "h3" | "h4"
BehaviorNumbering is presentational; select a title level that follows the document outline.
InputclassName
Typestring?
BehaviorMerged last onto the clipped grid root.

Component reference

Product frame

A squared viewport for showing working product UI at useful scale. The frame provides compact title chrome, an optional navigation rail, a content slot, and a status footer. It does not invent browser dots, shadows, or product behavior; all controls and data remain host-owned.

Live specimen

Rendered output

Release console

Record

Sample

185 exports

TypeScriptRequired01
Production buildRequired02
Browser checksRequired03
Catalog sample · no live release data

API reference

Inputs, types, and behavior

Inputtitle / meta / actions
TypeReactNode / ReactNode? / ReactNode?
BehaviorCompact frame header. The host owns any supplied controls and their behavior.
Inputnavigation / navigationLabel
TypeReactNode? / string
BehaviorOptional responsive rail wrapped in a named nav landmark.
Inputchildren / footer
TypeReactNode / ReactNode?
BehaviorThe product surface and an optional status line.
InputcontentClassName / className
Typestring? / string?
BehaviorMerged last onto the content area and outer frame.

Component reference

Feature row

Alternating split feature row: a copy column beside a media column, two-up at md+ and stacked (media after copy) below. reverse swaps the columns at md+ for zig-zag layouts while source order stays copy-first, so the reading order holds regardless of the flip. Squared media frame, hairline edge, no shadow.

Live specimen

Rendered output

Layout specimen 01

Source-order composition

The copy remains first in the document at every width; the media slot follows it on small screens.
Media slot
Layout specimen 02

Reversed desktop tracks

The reverse prop swaps desktop tracks without changing the accessible reading sequence.
Media slot

API reference

Inputs, types, and behavior

Inputtitle / body
TypeReactNode
BehaviorDisplay-register title beside body copy in the copy column.
Inputmedia
TypeReactNode
BehaviorImage/video/any node for the framed media column.
Inputreverse
Typeboolean
BehaviorDefault false; true puts media left at md+ for alternating rows (source order unchanged).
Inputeyebrow / action
TypeReactNode?
BehaviorMono micro-label above the title; optional link/button below the body.
Inputalign
Type"center" | "start"
BehaviorVertical alignment of copy against media at md+. Default "center".
InputheadingAs
Type"h2" | "h3" | "h4"
BehaviorTitle heading level. Default "h2".
InputclassName
Typestring?
BehaviorMerged onto the row grid.

Component reference

Bento grid

Asymmetric mosaic on paper, composed with BentoCell. Column count rides a CSS variable so the grid holds at one column below md (spans suppressed) and opens to columns equal tracks at md+. Standalone cells own a hairline frame; flush and hairline grids suppress those frames so each shared seam is drawn once. Add href to make one a self-dimming link.

Live specimen

Rendered output

Case study

Atlas — a wayfinding system

colSpan=2 · rowSpan=2 — a wide feature tile leading the mosaic.

Discipline

Type

Linked

Journal

Practice

Working in monochrome

API reference

Inputs, types, and behavior

InputBentoGrid.columns
Typenumber
BehaviorTrack count at md+; collapses to one column below. Default 3.
InputBentoGrid.gap
Type"flush" | "hairline" | "normal"
BehaviorGutter: flush is an unframed 3px mosaic gap; hairline draws one shared seam; normal separates individually framed cells. Default "normal".
InputBentoCell.colSpan / rowSpan
Typenumber
BehaviorTrack / row span at md+ (default 1); ignored on mobile where every cell is full width.
InputBentoCell.href
Typestring?
BehaviorMakes the tile a self-dimming link.
InputclassName
Typestring?
BehaviorMerged onto the grid or cell.

Component reference

App shell

The product frame: a fixed left rail with a layout-safe, instant expanded ↔ icon-only width swap; its contents recede and settle through opacity and a four-pixel translation. A sticky topbar sits over the scrolling content region. Below md the rail folds into an off-canvas paper dialog with a labelled close control, background inertness, scroll-lock, Escape, focus containment, and trigger restoration. Pair it with SidebarNav, which takes flat items and grouped sections of rows carrying icon, badge and active state — the shell injects collapsed for you.

Live specimen

Rendered output

Overview
Sample total
12
Sample subset
3
Sample amount
$48.2k

A fixed left rail collapses to an icon-only strip on the topbar toggle, and folds to an off-canvas drawer below the md breakpoint. The Overview panel renders here.

Boxed specimen: AppShell is a page-level frame (min-h-dvh), constrained to 520px with overflow-hidden so it stays inside the docs layout.

API reference

Inputs, types, and behavior

Inputsidebar
TypeReactNode
BehaviorA direct SidebarNav element (or any node). The current collapsed flag is cloned onto a direct element automatically.
Inputtopbar
TypeReactNode?
BehaviorStrip across the head of the content region, beside the collapse / MENU controls.
Inputchildren
TypeReactNode
BehaviorThe scrolling content column.
Inputcollapsed / onCollapsedChange
Typeboolean / (c) => void
BehaviorControlled rail collapse. The rail exposes its state as data-rail="collapsed|expanded" on the aside, so a host can swap a lockup or hide a label without measuring the rail's width.
Inputscroll margin
Type
BehaviorThe content column sets scroll-margin-block-start for anchors and focus targets so they clear the sticky topbar. Hosts need not measure the header.
InputdefaultCollapsed
Typeboolean
BehaviorUncontrolled initial collapse. Default false.
InputcontentAs
Type"main" | "div"
BehaviorDefault "main" supplies the page landmark. Use "div" only when embedding the shell inside an existing main, as this specimen does.
InputclassName
Typestring?
BehaviorMerged onto the root frame (min-h-screen + min-h-dvh) — how the specimen above is boxed to a fixed height.

Component reference

Section invert

Scroll-driven inversion with an instant threshold swap. The previous scrubbed color interpolation animated non-compositor properties and passed through low-contrast midpoint colors; both retained mode values now use the same discrete, motion-safe change. Scroll events are captured, so nested overflow containers drive it too. Interior rules should use currentColor so they invert in step.

Live specimen

Rendered output

Inverted section

The hairline above changes register in step because it inherits currentColor.

API reference

Inputs, types, and behavior

Inputmode
Type"scrub" | "threshold"
BehaviorCompatibility selector. Both values now perform the same instant threshold swap; default remains "scrub" so existing callers keep type compatibility.
Inputstart / end
Typenumber = 0.9 / 0.75
BehaviorViewport-height band fractions (useScrollProgress semantics) — tune per section height.
Inputthreshold
Typenumber = 0.5
BehaviorProgress at which the register flips in either mode.
InputclassName
Typestring?
BehaviorMerged onto the root element.