Design System

Design System

A living reference for the tokens, typography, motion, and components that this portfolio is built from. Everything below is rendered with the same primitives used across every page.

Foundations

Design tokens live as CSS custom properties on :root. A media query seeds them from prefers-color-scheme; an html.light or html.dark class from the theme toggle takes precedence. Tailwind v4’s @theme inline block exposes each variable as a utility (text-fg, bg-bg, border-border).

Three axes: semantic tokens (not hex values) drive every surface, a restrained 8-step spacing grid drives every layout, and five keyframes drive every movement. If something needs a new value, the first question is whether an existing token already covers the intent.

Color

Five tokens, two palettes. The site toggles between light and dark; every component reads from the same names.

TokenLightDarkUsage
--bg#FFFFFF#0B0D14Page and main surface background
--fg#0B0D14#F4F4F0Primary text, headings, links
--muted#4b5563#c7cad8Secondary text, descriptions, meta
--border#0B0D14#F4F4F0Dividers, card edges (always used at low opacity)
--accent#059669#34d399Emerald used sparingly for live signals

Opacity applied to muted and border provides secondary layers without adding new tokens. Typical values: muted/70 for de-emphasised meta, border/15 for quiet rules, fg/10 for subtle hover surfaces.

Typography

One typeface: Google Sans, loaded via Google Fonts. Weights: 400 (regular) and 500 (medium) only. Headings never use 700 or 900.

h1Page and article titleclamp(1.375rem, 1rem + 1vw, 1.625rem) / 500
h2Section heading17px / 500
h3Sub-section heading15px / 500
pBody prose inside articles and case studies.15px / 400
smRow descriptions, hooks, small metadata.14px / 400
xsLabels, dates, tag captions12px / 400

Spacing

A 4-pixel grid. Every margin, padding, and gap snaps to these steps. Tailwind’s default scale matches closely; the site only reaches for arbitrary values when the grid genuinely doesn’t fit.

4px
8px
12px
16px
20px
24px
32px
40px
48px
64px
80px

Motion

Five keyframes for the whole site. Long-running decorative animations were removed. What remains is functional: entrance, response confirmation, and live-signal pulses.

KeyframeDurationUse
fade-up400msPage and section entrance
fade-in250msSimple opacity reveals
chat-msg-in400msMessage bubbles, CodeLens panel transitions
chat-typing-dot1.2sTyping indicators, live-status pulses

All timings ease with cubic-bezier(0.16, 1, 0.3, 1). Reduced-motion preferences turn entrance animations off; live-signal pulses remain because they’re informational, not decorative.

Hello.fade-up
chat-typing-dot

Components

Components here are the primitives that actually ship across the site, rendered with mock data so you can see the exact states.

Section heading

H2 inside a case study or article

When the data is actually live

Primary link

Underlined fg with muted decoration that deepens on hover

Secondary link

Muted text that brightens to fg on hover

Pill tab

Tab row for chart timeframes, code-review findings, filter modes

Context tag

Inline category marker on work rows

ClientPersonalCompanyStartup

Icons

Hand-rolled SVG, 14×14 viewBox, currentColor, 1.2–1.5 stroke width. No icon library; each icon is a single path or small group inlined where it’s used.

Back
Arrow right
External
Close
Check
Copy
Expand
Chevron
Menu
Sun
Moon
Info

Accessibility

WCAG 2.1 AA is the floor, AAA is the target. The site is keyboard-navigable, respects prefers-reduced-motion, and avoids colour as the only channel for meaning.

Contrast

PairDarkLightLevel
fg on bg17.4:119.7:1AAA
muted on bg11.2:17.6:1AAA
accent on bg10.1:15.2:1AA+
border on bg (at 15%)decorative

Principles

  • Never rely on colour alone. Every chart series ships with a line style and marker shape so it reads without colour.
  • Respect motion preference. All entrance animations are disabled under reduced-motion; informational pulses stay.
  • Keyboard first. Every interactive element is reachable and visibly focused.
  • Contrast above AA. Primary text runs above 17:1 in dark and 19:1 in light.