Rejouice — loading0

Foundation

One source, three consumers.

These live in lib/patterns/design-tokens.ts. The stylesheet declares them, lib/gsap.ts derives its durations from the same numbers so GSAP and CSS cannot disagree, and the MCP serves them to agents. pnpm check fails if any of the three drifts.

Motion

Durations and easings tuned for large moving type and full-bleed media. Long, low-acceleration curves read as considered; short bouncy curves read as a toy. `lib/gsap.ts` derives its GSAP durations from these exact values.

--ease-out-expo
cubic-bezier(0.16, 1, 0.3, 1)
The workhorse. Entrances, reveals, and anything that should arrive decisively and settle without overshoot. Mirrors GSAP `expo.out`.
--ease-in-out-quart
cubic-bezier(0.76, 0, 0.24, 1)
Symmetric moves: curtains, pins, page transitions. Mirrors GSAP `power4.inOut`.
--ease-out-quad
cubic-bezier(0.5, 1, 0.89, 1)
Hover and cursor feedback, where anything longer than a breath feels laggy.
--dur-instant
0.12s
Hover state changes, cursor scale, underline draw.
--dur-micro
0.4s
Small element entrances, nav condensation, magnetic release. GSAP `DUR.micro`. (was --dur-quick)
--dur-reveal
0.95s
The default reveal duration for a line of type or a single card. GSAP `DUR.reveal`. (was --dur-base)
--dur-curtain
1.1s
Full-bleed media reveals, curtain transitions, preloader exits. GSAP `DUR.curtain`. (was --dur-slow)
--stagger
0.08s
Delay between consecutive lines of a masked headline. GSAP `DUR.stagger`. (was --stagger-line)
--stagger-grid
0.06s
Delay between consecutive grid cells entering the viewport.
--parallax-depth
0.15
Unitless multiplier applied to scroll delta for background media. Above ~0.3 the effect stops reading as depth and starts reading as lag.

Typography

A fluid scale with very few steps and very big jumps. The direction depends on one headline being genuinely enormous rather than several being moderately large — there is deliberately no mid-sized step.

--fs-display
clamp(3rem, 12vw, 12.5rem)
The single hero statement. One per page. (was --type-display)
--fs-h1
clamp(2.5rem, 6vw, 6rem)
Section openers below the hero. (was --type-h1)
--fs-h2
clamp(2.2rem, 6vw, 5.5rem)
Sub-section headings. (was --type-h2)
--fs-body
clamp(1rem, 1.05vw, 1.125rem)
Running text. Keep measure between 55 and 72 characters. (was --type-body)
--fs-label
clamp(0.68rem, 0.74vw, 0.8rem)
Index numbers, metadata, cursor labels, eyebrow rows. Mono, uppercase. (was --type-caption)
--leading-display
0.9
Display type sits tight. Anything at or above 1.0 loses the block-of-type quality the direction relies on.
--leading-body
1.55
Running text.
--tracking-display
-0.035em
Negative tracking on display sizes only. Never on body or label.
--tracking-caption
0.15em
Positive tracking with uppercase for metadata and eyebrow labels.

Colour

Near-monochrome with one accent, dual-mode. Every colour is theme-relative: flipping `data-theme` on `<html>` repaints the page because Tailwind utilities map to these live variables through `@theme inline`.

--bg
#0a0a0a
Page background. Never pure black — this is the dark-cinematic canvas. (was --paper)
--bg-2
#121212
Raised surfaces: cards, demo frames, code blocks.
--ink
#f3f1ec
Foreground. Warm off-white on dark, near-black on light.
--ink-dim
#8a8781
Body copy, captions, anything secondary.
--ink-faint
#47453f
Disabled and decorative text. Check contrast before using for content.
--accent
#d7ff3e
Exactly one accent, used for focus rings, the cursor, and at most one CTA.
--accent-2
#7087f7
The rare second accent. Reserve for data or state, never decoration.
--line
rgba(243, 241, 236, 0.14)
Hairlines and dividers. (was --rule)
--bg-inverse
#f4f2ed
Background of a section that inverts against the page. Used by scroll-colour inversion. (was --paper-inverse)
--ink-inverse
#0f0e0c
Foreground on an inverted section.
--veil
color-mix(in oklab, #0a0a0a 55%, transparent)
Scrim over media when text sits on top of it. Deliberately dark in both themes — it exists to make type readable over an image, not to match the canvas.

Space

A fluid spacing ramp. Sections breathe far more than components do.

--space-2xs
0.5rem
Inline gaps inside a control.
--space-xs
0.75rem
Label to value.
--space-sm
1.25rem
Stacked text blocks.
--space-md
2rem
Component internal padding.
--space-lg
clamp(3rem, 6vw, 6rem)
Between components.
--space-xl
clamp(6rem, 14vh, 12rem)
Between page sections.
--gutter
clamp(1.25rem, 4vw, 4rem)
Page side gutter. Full-bleed media ignores it; everything else respects it.

Layout

Grid, measure, and the z-index ladder. The grid exists so that breaking it is legible as a decision rather than an accident.

--grid-columns
12
Column count at desktop. 6 at tablet, 4 at mobile.
--grid-gap
clamp(1rem, 1.6vw, 2rem)
Gap between grid columns.
--measure
62ch
Maximum width of a running-text column.
--wrap-max
1440px
Maximum content width before the gutter takes over.
--frame-radius
0px
Square corners by default. The direction reads as print, and print has no border radius.
--z-nav
50
Sticky navigation.
--z-grain
60
Film-grain overlay.
--z-cursor
70
Custom cursor, above the grain.
--z-curtain
100
Route curtain and preloader.