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)
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.
--inkBase surface
The fixed dark shell every page sits on. Never used as a text color on light surfaces.
--paperPrimary text
Body copy, headlines, and primary UI text on the ink surface.
--pinkPrimary accent
Primary buttons, links, and the loudest accent. Safe for normal-size text on ink.
--violetSecondary accent
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.
--limeInteractive / focus
Hover states, focus rings, and active nav indicators. High contrast, used sparingly so it stays meaningful.
--lilacSoft accent
A rare decorative fill, like a resume hover state. I don't use it for body text.
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
ShippedSkill tag · About section
Interaction DesignProject tag chip · overlaid on an image
AI ConceptComponents
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%
task success after the fix shipped
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
Step one
What happens first, and why it comes first.
Step two
What gets decided or discovered at this stage.
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.
| Task | Success | Time | Ease |
|---|---|---|---|
| Task A | +45% | 2.3x faster | 2.2x more direct |
| Task B | — | 22% faster | Fewer 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.
Before
After
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.
Image caption
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: reducemedia 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. - 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.
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
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.
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.
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."
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.
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 is the 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 the most complete one right now, with final copy, layout, and visuals all done. The Cotality/OneHome capstone is still in progress, and it'll take over this slot once the final report lands.