Skip to main content
Back to portfolio

Design System

The system behind this portfolio

This page walks through the actual tokens, type scale, and components running the site you're on right now - not a separate mockup. Every color, spacing value, button, and section pattern below is imported straight from the production code (down to the case-study block components), so what you see here is exactly what's shipping. It's meant to be enough on its own to build another page in this system.

Foundations

Layout & spacing

Every page sits in a max-w-shell (1280px) container with px-[6vw] side margins, so gutters scale with the viewport instead of snapping between fixed breakpoints. Vertical rhythm between major sections is py-16 md:py-24. Corners use one radius token, rounded-ds (2px) - sharp enough to read as structural, not soft/decorative. Borders are almost always border-paper/10 to border-paper/18, never solid paper.

Spacing scale · base 4px (Tailwind default)

1
2
3
4
6
8
10
12
16
20
24

Radius · --radius-ds: 2px

One radius everywhere - buttons, cards, images, badges. No larger "soft" radius exists in this system.

Foundations

Accessible color palette

A fixed dark shell, no light mode, built around one paper-on-ink text pairing and three accent colors. I check every accent's contrast ratio against the ink background against WCAG 2.1 before I let it carry text. If a color only passes at large sizes, it's restricted to labels and headlines, never body copy.

#0B0B0F
Ink--ink

Base surface

The fixed dark shell every page sits on. Never used as a text color on light surfaces.

#F7F7F2
Paper--paper

Primary text

18.28:1 on ink · AAA

Body copy, headlines, and primary UI text on the ink surface.

#FF2E83
Pink--pink

Primary accent

5.59:1 on ink · AA

Primary buttons, links, and the loudest accent. Safe for normal-size text on ink.

#7B61FF
Violet--violet

Secondary accent

4.67:1 on ink · AA

Numbered labels and secondary emphasis. It clears AA for normal text on ink, but only just, so I keep it to short labels, not paragraphs.

#C6FF00
Lime--lime

Interactive / focus

16.56:1 on ink · AAA

Hover states, focus rings, and active nav indicators. High contrast, used sparingly so it stays meaningful.

#F3D6FF
Lilac--lilac

Soft accent

14.85:1 on ink · AAA

Image captions in case studies - high contrast that still reads as soft/secondary rather than an interactive accent, and doesn't wash out next to light-background screenshots the way muted gray text did.

Foundations

Typography

Archivo carries every weight of the interface, from body copy to the heaviest display headlines. Caveat, a handwritten accent face, only shows up for a single emphasized word here and there. I never let it carry a full sentence.

H1 · font-display · 900 · clamp(36px, 6.5vw, 64px)

Designing systems people trust

H2 · font-display · 900 · 30 to 48px

Products, not just screens

H3 · font-display · 900 · 20 to 24px

A section heading, like inside a case study

Body · font-sans (Archivo) · 400 · 16px

Body copy sits at a comfortable reading measure, medium contrast against ink at paper/70, and generous line height, so a long case-study passage stays easy to scan.

Label / eyebrow · font-mono · 10px · uppercase · tracked

Section label example

Accent · font-accent (Caveat) · single word only

Designing systems people actually trust.

Components

Buttons

Four variants, two sizes. primary is the only filled button in the system - use it once per view for the one action that matters most.

variant="primary"

variant="outline"

variant="arrow" · text link with a hover-expanding gap

variant="destructive" · reserved for delete/remove actions (admin tool)

disabled state

Components

Badges & tags

Three small pill patterns cover every "label on top of content" need in the system.

Kind badge · case study header

Shipped

Skill tag · About section

Interaction Design

Project tag chip · overlaid on an image

Product Design

Components

Numbered section header

Every top-level homepage section (Work, Approach, About, Contact) opens with this exact pattern: a violet index number, a lime mono eyebrow, then the display headline. Copy this block verbatim and renumber it for a new section.

05New Section

Headline goes here

Components

Cards

One bordered card shape, two ways to fill it: an icon + title + body (Approach, case-study principle cards), or plain content. Both use rounded-ds border border-paper/12 p-6.

Icon card title

Icon in a bordered box, then a medium-weight title, then paper/60 body copy. Used for principles, pillars, and "what shipped" summaries.

Plain card title

Same shell without the icon slot - for content-only cards where an icon would just be decoration.

Case study components

The full case-study block vocabulary

These are the actual components (components/case-study-template.tsx) that every case study on this site is built from - imported here directly, not recreated, so there's zero drift from production. Compose a new case study (or any long-form page) by stringing these together.

type: "text"

A text block

The default building block - a heading (optional) plus one or more paragraphs. Use it for narrative sections: the challenge, a reframe, a reflection.

type: "list"

A bulleted list

Pink-dot bullets, used for scannable enumerations:

  • First point
  • Second point
  • Third point

type: "quote"

A large pink-bordered pull-quote - only ever real copy pulled verbatim from research, testing, or product text, never invented for effect.

Attribution or context line

type: "stat"

Headline numbers

Big Archivo-900 figures for scannable stats, instead of burying a number in a paragraph.

86%

expected a behavior the product didn't support

+45%

playlist task success in a prototype test

2.3x

faster completion time

type: "tradeoffs" · Negotiations & trade-offs

A real constraint, decision, and resolution

Surfaces the negotiation behind a decision - never a hypothetical.

Constraint

What forced the decision - a deadline, a technical limit, a research finding.

Decision

What was chosen because of that constraint.

Resolution

How it was validated or why it held up.

type: "tiers" · MVP / ideal scoping

Tiered solution options

Single-patty-to-3x3 style scoping, so a reviewer can see the range considered.

Single

MVP

The smallest version that solves the core problem.

Shipped for MVP

Double-double

Next iteration

Adds the highest-value follow-up, still scoped tightly.

3x3

Long-term vision

The full-featured version, for later.

type: "process" · numbered steps

A numbered process

  1. Step one

    What happens first, and why it comes first.

  2. Step two

    What gets decided or discovered at this stage.

  3. Step three

    How the previous step changes what happens next.

type: "cards" · icon grid

Principles or takeaways

First principle

One sentence explaining why it matters.

Second principle

One sentence explaining why it matters.

Third principle

One sentence explaining why it matters.

type: "metrics" · results table

A/B or usability results

Row-level results, with a highlight color for the standout win.

TaskSuccessTimeEase
Task A+45%2.3x faster2.2x more direct
Task BN/A22% fasterFewer misclicks

An optional footnote for context that doesn't fit in the table.

type: "compare" · before/after

Before and after

Two images side by side, each with its own caption - the clearest way to show what changed.

type: "split" · alternating text + image

Text next to its supporting visual

Pairs an insight directly with a screenshot instead of always stacking full-width blocks. Pass flip: true to mirror which side the image sits on.

type: "placeholder" · honest "coming soon"

Visual coming soon

For a visual that doesn't exist yet - never a broken image.

Foundations

Motion & accessibility

  • Every animation (orbs, blink, view transitions) is wrapped in a prefers-reduced-motion: reduce media query in globals.css that forces durations to ~0 - motion is never load-bearing for understanding content.
  • Focus states use focus-visible:outline focus-visible:outline-lime - keyboard focus is always visibly distinct from hover. A November 2026 keyboard check found the shared Button had no visible focus indicator; it now uses the same outline.
  • Text selection is styled (::selection) to lime-on-ink instead of the browser default, so it stays legible against the dark shell.
  • Every accent color is contrast-checked against ink (see the palette above) before it's allowed to carry text, not just used decoratively.

Principles

Usability heuristics

The tokens above decide how things look. Jakob Nielsen's 10 usability heuristics decide how they behave - what the interface tells you, what it lets you undo, and how it fails. Every change to this site, and to the AI-assisted admin tool behind it, is held to all ten. Here's what each one means in practice here, not in the abstract.

  1. 01Visibility of system status

    Anything slower than about a second says it's working and roughly how long it will take - "Re-analyzing... (34s)" in the résumé tool, a typing state in Ask My Portfolio. Counts that need action live as badges in the admin nav and refresh as you move around.

  2. 02Match between system and the real world

    Numbers are labeled with what they actually measure. A fit score reads "Your fit for this role" and sits next to "Role type", so the two never get read as the same thing. The command-menu hint shows ⌘K on a Mac and Ctrl+K everywhere else.

  3. 03User control and freedom

    Destructive actions get a short Undo window instead of a "Are you sure?" dialog. Long AI work can be stopped or cancelled - including Ask My Portfolio mid-answer - and every dead end links somewhere useful.

  4. 04Consistency and standards

    One meaning per color: errors use a single red, warnings a single amber, and every interactive element gets the same lime focus outline. A fit recommendation looks identical everywhere it appears.

  5. 05Error prevention

    Submit buttons stay disabled until the input is valid, and constraints are stated up front ("At least 6 characters") rather than discovered after an error. Even a harmless-looking dropdown that removes something gets an undo.

  6. 06Recognition rather than recall

    Anything you need to act on is reachable from navigation, not buried behind another page. When the résumé tool reuses an earlier answer it shows which one, so you never have to remember it. Icon-only buttons always carry a label.

  7. 07Flexibility and efficiency of use

    The ⌘K / Ctrl+K command menu, suggested questions in Ask My Portfolio, and answers that carry over between job postings so the same question is never asked twice.

  8. 08Aesthetic and minimalist design

    The visual system on this page handles the look. Behaviorally: one line of explanation right where it's needed, not a wall of text, and no chrome a state doesn't need.

  9. 09Help users recognize, diagnose, and recover from errors

    Failures are shown where they happened, in plain words, with a way forward - "Try again" rather than "Request failed with 500". A failed load never masquerades as an empty list or a finished state.

  10. 10Help and documentation

    Short help at the point of decision - "How was this calculated?" under a score, a one-line explainer under a chart - instead of separate help pages. This page is the documentation for everything else.

InlineError · components/admin/feedback.tsx

The one error pattern: plain words where the failure happened, plus a “Try again” when there's something to retry. Never a raw status code, never a browser alert.

Undo window · useUndoableAction

Experience entry deletedUndo

Replaces “Are you sure?” The item disappears immediately and is only really removed after six seconds - leaving the page commits it, so nothing is silently lost.

Principles

Gestalt principles

Gestalt principles describe how people group what they see before they read a word of it. These four came out of auditing this site screen by screen - each one fixed a real layout that was grouping the wrong things together.

Similarity · same look, same meaning

Pink is reserved for the primary action, so navigation links stay off-white and lime marks where you are. Admin count badges encode what a number asks of you, not whether it's good news:

5act on this2something broke83just information
Strong ApplyApplyStretchLow Priority

Only things you can click get hover states - static tags don't, so they never read as buttons.

Proximity · space follows meaning

Things that belong together sit closer than things that don't. A form's main action sits at the end of that form, not in a corner of the page. Education sits under the career timeline it continues. An alternative path - like the ⌘K hint under Ask My Portfolio - gets more space than the module's own internal rhythm, so it reads as separate.

Continuity · one edge, one line

Content hangs from the same 6vw edge as the heading above it. Lists that wrap use a hanging indent so the eye follows the text, not the bullet:

Before

  • A wrapped line restarts under the bullet and breaks the column

After

  • A wrapped line lines up with the text above it

Common region · group the decision

When a row holds a decision and some utilities, the decision gets its own labeled group, set apart by a rule - so approving an item never looks like the same kind of action as deleting it.

ApproveReject
edit · delete

Process

How this gets built

This site isn't hand-coded line by line, and it isn't a no-code builder either. Every page here, including this one, is built through direct AI pairing - I decide what should change and why, an agent edits the actual files in this repo, and nothing ships without a check.

The loop

  1. Direct it in conversation

    I describe what should change and why - a content fix, a new feature, a bug - the same way I'd brief a teammate, not a ticket queue.

  2. The agent edits the real files

    Claude Code / GitHub Copilot edits this repo directly - the components, tokens, and case-study data you're looking at right now, not a separate mockup.

  3. Verify before it ships

    Every change gets a TypeScript check and a real browser pass - visually and functionally - before it's trusted, not just "looks right in chat."

  4. Ship

    Committed to GitHub with a real message, auto-deployed by Vercel - usually live within minutes of the conversation that started it.

Swappable

VS Code + GitHub Copilot Chat

Claude Sonnet coding sessions

Vercel hosting

Durable

Design tokens in globals.css

The case-study data model (lib/case-studies.ts)

This page - documents the system so any future session can pick it back up

What that actually produces

One real design system

Every token and component on this page is the same code running the live site, not redrawn for show.

Checked against real audiences

Résumé and case-study copy get reviewed against ATS parsing, recruiter scan time, and hiring-manager depth before anything ships.

Version-controlled, not vibes

Every change is a real git commit with a message - reversible and deployed through the same pipeline as a production app.

Foundations

Logo (extra credit)

A simple wordmark, built entirely from the type system above. No separate illustration tool, so it scales and recolors exactly like the rest of the interface.

SF
Shawn Farnum.
SF

In production

High-fidelity design

I didn't build a separate high-fidelity mockup for this one. The system is implemented directly on the live, shipped site, so the homepage link below isthe high-fidelity design. It's already built and deployed with the exact tokens and type scale documented on this page.

For the showcased project, I'm linking the SoundCloud UX Research case study. It's fully public, with final copy, layout, and visuals all done, which makes it the cleanest example of the system running against real content.