Project Name
40% improvementShort outcome-led description of what was achieved and for whom.
6 min readBuilding it felt like a design project from the start. Every colour, typeface, and spacing value is a named decision stored in a single token file. The CSS follows ITCSS — specificity only increases where it has to. Components have one job and a consistent naming convention. JavaScript is an enhancement, not a dependency. If you've landed here, you're probably the kind of person who checks the source before closing the tab.
85 named custom properties in one file. No hex values, hardcoded sizes, or magic numbers anywhere else in the codebase.
Base, text, accent, and semantic feedback tokens. Contrast ratios shown where the token is used as foreground text.
Base
Text
Accent
Feedback — four semantic statuses, each with bg / border / text roles
Four font families, each with a specific role.
Type scale — core nine sizes, rendered in Fraunces.
8pt scale — every margin, padding, and gap is a multiple of 8px. Bar width equals the exact token value.
Five steps from tight corners to full pill.
Five named easing curves. Each bar fills with its own curve — the acceleration character is the point. Paused if you prefer reduced motion.
Four breakpoints, five window size classes. Band widths are proportional to the pixel ranges. Scroll on narrow screens.
26 component CSS files. Each is self-contained, named by role rather than appearance, and works without JavaScript unless stated.
.btn
Three variants covering the full range of visual emphasis. 44px minimum touch target on mobile, 40px desktop, scale(0.97) on active.
.nav__link
Desktop: underline scaleX(0→1) on active. Mobile: bottom sheet at translateY(100%), backdrop at 50% opacity — z-stack: overlay 300 / sheet 400 / toggle 500.
Desktop inline nav and mobile bottom sheet in one component. Initialised progressively from nav.js — works without JS as plain links.
.card
Short outcome-led description of what was achieved and for whom.
6 min readCase study card with metric chip, outcome subtitle, and read-time CTA. Lifts on hover with teal accent border and shadow.
.cs-annotated-image
The CTA is visually prominent and immediately scannable on first view.
Secondary content competes with the primary action for visual weight.
Full-width image with interactive numbered callout pins. Green pins mark improvements; red mark problems. Desktop shows inline tooltips on hover; mobile uses a centred modal on tap.
.cs-two-col
Default — text left, media right
Short narrative copy alongside a supporting visual. Used for pairing context with evidence, prototypes, or research artefacts.
Flipped — media left, text right
The --flip modifier reverses the layout on desktop. Mobile always stacks text first, then media, regardless of variant.
Editorial two-column layout for pairing narrative copy with a supporting visual. Two-column on desktop, stacked on mobile.
.cs-before-after
Side-by-side comparison for redesigns and iterations. Semantic label badges use feedback error and success tokens. Two columns at 905px, stacked below.
.image-block
Illustrations and SVGs that support narrative without dominating. Never stretched, never annotated, always object-fit contain.
.image-block
Wider landscape variant for panoramic illustrations that need more horizontal breathing room.
.cs-editorial-list
Research, interviews, and problem framing to understand what matters and why.
Synthesise findings into a clear problem statement and design direction.
Iterate through prototypes and validation, shipping what solves the real problem.
Stacked editorial list for findings, process phases, or ranked themes. Content aligns flush to the container and fills the available width.
.cs-metric-grid
35%
Improvement across 5 million annual searches.
30%
More users completing the feedback flow at zero dev cost.
97 hrs
Freed from the support team by better platform design.
Outcome metrics with value, label, and supporting body text. Teal accent underline on each value. Responsive from 1 to 3 columns.
.pull-quote
Good defaults are worth more than a hundred options.
— Sam Blake
High-impact statement that interrupts the reading rhythm. 3px teal left accent bar, Fraunces display type at 30–36px.
Six structural decisions that keep the codebase coherent as it grows.
Styles are layered from abstract to specific. Settings and variables sit at the foundation. Components build on them. Layouts use both. Specificity flows in one direction and never fights itself.
Every visual decision is a named CSS custom property in _variables.css. No hex values, hardcoded sizes, or arbitrary spacing exist anywhere else in the codebase. One file to update; the whole site responds.
No framework overhead. Vite handles multi-page entry points, fingerprinted asset paths, and fast dev server rewrites. Each page is a straightforward HTML file registered in rollupOptions.input.
Content renders and works before JavaScript loads. Navigation functions as pure HTML. The typewriter, annotated image interactions, and scroll reveals are layered on top — remove them and the page degrades gracefully.
Every interactive element is keyboard-navigable with a visible focus ring. Touch targets meet the WCAG 2.2 44px minimum. aria-current, aria-label, and screen-reader text are part of every component from day one.
Mobile-first breakpoints at 600, 905, 1240, and 1440px — chosen for real device densities, not round numbers. Hover states are gated behind (hover: hover) and (pointer: fine) to prevent false activation on touch screens.
Six principles that shaped every component, layout, and interaction on this site.
Layout serves content, never the other way around. Every structural decision starts with what needs to be communicated, not what looks interesting.
Heading levels reflect document hierarchy. Lists are lists. Quotes are blockquotes. Markup describes what things are, not how they look.
The page renders and works before JavaScript arrives. Scripts add behaviour; they do not create it.
Every animation has a reduced-motion counterpart. User preference is respected at the system level, not worked around at the component level.
WCAG 2.2 AA is a constraint, not a milestone. Contrast ratios, focus states, and ARIA labels are part of every component from the first commit.
Reuse earns its keep or it does not happen. Three similar elements share one component when the abstraction is genuinely simpler. Otherwise, write the thing twice.
85
Every colour, spacing value, radius, and easing curve named in one file.
28
Purpose-built CSS components with one responsibility each.
12
From the home page through three case studies to this one.
0
Vanilla HTML, CSS custom properties, and progressive enhancement only.