One system, nine pages
A light, cool-neutral interface built on Hanken Grotesk and JetBrains Mono. Every colour, size, radius, shadow and curve in the portfolio resolves to a token declared in one file.
How it is put together
Four layers, each of which may only depend on the one above it.
- Tokens —
app/styles/tokens.css. The only file allowed to contain a literal value. - Base —
app/app.css. Element defaults, built only from tokens. - Primitives —
app/styles/primitives.cssandapp/components/ui/. The pieces on the Component pages. - Pages — routes compose primitives. A page may lay things out; it may not restyle them.
Rules that hold everywhere
- Both viewports, every time. Nothing ships without being checked at ~375px and ≥1280px together.
- Hover is an enhancement. Hover styling lives behind
@media (hover: hover), so a touch device never inherits a state it cannot leave. - Focus is never removed. Every interactive primitive styles
:focus-visible. - Reduced motion is respected. Duration tokens collapse to
0ms; GSAP opts in separately viauseReducedMotion. - Contrast is not negotiable. All three ink tones clear WCAG AA on the page surface.
The pages
PsychologyWhy these choices land the way they do on a reader.PrinciplesThe decisions to make when the system does not say.TypographyTwo families, a fluid scale, and how tracking moves with size.ColourA cool neutral ramp, three ink tones, one accent.SpaceA 4px base, fine for interiors and coarse for page rhythm.MaterialSurfaces, hairlines, radius and elevation — the physics.MotionFast by default, with easing carried in tokens.ComponentThe primitives everything else is assembled from.