How I built this site
I designed this site the same way I design products.
Who it's for
Hiring managers and design leaders, who usually scan a portfolio in a few minutes, often on a phone. Published surveys of several hundred hiring managers agree on what they look for: relevant work first, the thinking behind the decisions, and evidence of craft.
What that meant for the design
A handful of case studies chosen for this audience, not everything I've done. Each one opens with the process I followed and the decisions I made, because that's what a reviewer can't see in a finished screen. Every page works on a phone.
How it's built
I designed the pages in Figma (opens in a new tab) and defined a small set of design tokens for color, type and spacing, the same approach I used for Broadcom's design system. I used Claude Code to build the site from my content and direction, and reviewed every change before it went live. It's built to meet WCAG 2.2 AA accessibility standards.
Design to code
The design system has two layers in Figma and in code, with the same names in both. Mode holds the light and dark colors; Background tells each component which of those colors to use on the surface it sits on. Components never use a raw color, so any component works on any background, in either mode, without a separate design. I wrote a design system guide for the code, so new pages and AI-assisted changes follow the same rules for tokens, states, target sizes and contrast.
What I'd test next
Whether reviewers find the case study they need in the first minute, and which ones they open.
This site's design system
Figma variable collections drive every page: Background (five surfaces), Mode (light, dark) and Size (desktop, phone).
Light mode
Colors
The raw color values in code (--color-*), light mode.
- color/bg#F1ECE1
- color/surface#FBF8F1
- color/accent-soft#DDE3CF
- color/ink#1F2A1F
- color/ink-active#1F2A1F
- color/muted#575A4A
- color/accent#355E3B
- color/accent-hover#2A4B2F
- color/accent-pressed#1F3A24
- color/on-accent#FFFFFF
- color/ink-hover#2F3D2F
- color/ink-pressed#141C15
- color/clay#7D5034
- color/line#D8D0BE
- color/disabled-fill#E4DDCF
- color/disabled-text#9C9789
- color/hero-bg#1E2B22
- color/hero-surface#2A3A2D
- color/hero-ink#F2EDE1
- color/hero-ink-active#F2EDE1
- color/hero-muted#AAAD9B
- color/hero-accent#A9AE6A
- color/hero-accent-hover#C4C98A
- color/hero-accent-pressed#A0A566
- color/hero-on-accent#1E2B22
- color/hero-clay#D4A27F
- color/hero-line#3A4A3A
- color/hero-disabled-fill#2E3C31
- color/hero-disabled-text#6E7663
Backgrounds
What each component uses on each background. In Figma, the "Background" collection has one mode per column; each value points to that background's variable in the "Mode" collection, which holds the light and dark values. Cells show the Figma variable; rows show the CSS name.
| Token | Page | Surface | Soft | Hero | Hero raised |
|---|---|---|---|---|---|
--bg-fill | Page/bg/fill | Surface/bg/fill | Soft/bg/fill | Hero/bg/fill | Hero raised/bg/fill |
--bg-raised | Page/bg/raised | Surface/bg/raised | Soft/bg/raised | Hero/bg/raised | Hero raised/bg/raised |
--text-primary | Page/text/primary | Surface/text/primary | Soft/text/primary | Hero/text/primary | Hero raised/text/primary |
--text-secondary | Page/text/secondary | Surface/text/secondary | Soft/text/secondary | Hero/text/secondary | Hero raised/text/secondary |
--text-active | Page/text/active | Surface/text/active | Soft/text/active | Hero/text/active | Hero raised/text/active |
--text-label | Page/text/label | Surface/text/label | Soft/text/label | Hero/text/label | Hero raised/text/label |
--text-label-alt | Page/text/label-alt | Surface/text/label-alt | Soft/text/label-alt | Hero/text/label-alt | Hero raised/text/label-alt |
--link | Page/link/default | Surface/link/default | Soft/link/default | Hero/link/default | Hero raised/link/default |
--link-hover | Page/link/hover | Surface/link/hover | Soft/link/hover | Hero/link/hover | Hero raised/link/hover |
--link-pressed | Page/link/pressed | Surface/link/pressed | Soft/link/pressed | Hero/link/pressed | Hero raised/link/pressed |
--button-fill | Page/button/fill | Surface/button/fill | Soft/button/fill | Hero/button/fill | Hero raised/button/fill |
--button-fill-hover | Page/button/fill-hover | Surface/button/fill-hover | Soft/button/fill-hover | Hero/button/fill-hover | Hero raised/button/fill-hover |
--button-fill-pressed | Page/button/fill-pressed | Surface/button/fill-pressed | Soft/button/fill-pressed | Hero/button/fill-pressed | Hero raised/button/fill-pressed |
--button-text | Page/button/text | Surface/button/text | Soft/button/text | Hero/button/text | Hero raised/button/text |
--line | Page/line | Surface/line | Soft/line | Hero/line | Hero raised/line |
--focus | Page/focus | Surface/focus | Soft/focus | Hero/focus | Hero raised/focus |
--disabled-fill | Page/disabled/fill | Surface/disabled/fill | Soft/disabled/fill | Hero/disabled/fill | Hero raised/disabled/fill |
--disabled-text | Page/disabled/text | Surface/disabled/text | Soft/disabled/text | Hero/disabled/text | Hero raised/disabled/text |
Type
- Display · Barlow CondensedSelected work
- Body · DM SansPlain, warm writing at a comfortable size for reading on any screen.
Dynamic components on every background
Pick a background: the same components re-color from the Background tokens, just as they do across the site. These are pictures of each state; the real controls are live everywhere else.
Page
Dark mode
Colors
The raw color values in code (--color-*), dark mode.
- color/bg#141C16
- color/surface#1C261E
- color/accent-soft#1A251D
- color/ink#ABAA9F
- color/ink-active#ECE7DA
- color/muted#9B9B90
- color/accent#A9AE6A
- color/accent-hover#C4C98A
- color/accent-pressed#A0A566
- color/on-accent#141C16
- color/ink-hover#BDBCB1
- color/ink-pressed#9A998F
- color/clay#D4A27F
- color/line#3A4A3C
- color/disabled-fill#222C24
- color/disabled-text#5F6158
- color/hero-bg#0E1510
- color/hero-surface#1B271E
- color/hero-ink#AEACA2
- color/hero-ink-active#F2EDE1
- color/hero-muted#8E8F86
- color/hero-accent#A9AE6A
- color/hero-accent-hover#C4C98A
- color/hero-accent-pressed#A0A566
- color/hero-on-accent#1E2B22
- color/hero-clay#D4A27F
- color/hero-line#2C3A2E
- color/hero-disabled-fill#18221A
- color/hero-disabled-text#4E544C
Backgrounds
What each component uses on each background. In Figma, the "Background" collection has one mode per column; each value points to that background's variable in the "Mode" collection, which holds the light and dark values. Cells show the Figma variable; rows show the CSS name.
| Token | Page | Surface | Soft | Hero | Hero raised |
|---|---|---|---|---|---|
--bg-fill | Page/bg/fill | Surface/bg/fill | Soft/bg/fill | Hero/bg/fill | Hero raised/bg/fill |
--bg-raised | Page/bg/raised | Surface/bg/raised | Soft/bg/raised | Hero/bg/raised | Hero raised/bg/raised |
--text-primary | Page/text/primary | Surface/text/primary | Soft/text/primary | Hero/text/primary | Hero raised/text/primary |
--text-secondary | Page/text/secondary | Surface/text/secondary | Soft/text/secondary | Hero/text/secondary | Hero raised/text/secondary |
--text-active | Page/text/active | Surface/text/active | Soft/text/active | Hero/text/active | Hero raised/text/active |
--text-label | Page/text/label | Surface/text/label | Soft/text/label | Hero/text/label | Hero raised/text/label |
--text-label-alt | Page/text/label-alt | Surface/text/label-alt | Soft/text/label-alt | Hero/text/label-alt | Hero raised/text/label-alt |
--link | Page/link/default | Surface/link/default | Soft/link/default | Hero/link/default | Hero raised/link/default |
--link-hover | Page/link/hover | Surface/link/hover | Soft/link/hover | Hero/link/hover | Hero raised/link/hover |
--link-pressed | Page/link/pressed | Surface/link/pressed | Soft/link/pressed | Hero/link/pressed | Hero raised/link/pressed |
--button-fill | Page/button/fill | Surface/button/fill | Soft/button/fill | Hero/button/fill | Hero raised/button/fill |
--button-fill-hover | Page/button/fill-hover | Surface/button/fill-hover | Soft/button/fill-hover | Hero/button/fill-hover | Hero raised/button/fill-hover |
--button-fill-pressed | Page/button/fill-pressed | Surface/button/fill-pressed | Soft/button/fill-pressed | Hero/button/fill-pressed | Hero raised/button/fill-pressed |
--button-text | Page/button/text | Surface/button/text | Soft/button/text | Hero/button/text | Hero raised/button/text |
--line | Page/line | Surface/line | Soft/line | Hero/line | Hero raised/line |
--focus | Page/focus | Surface/focus | Soft/focus | Hero/focus | Hero raised/focus |
--disabled-fill | Page/disabled/fill | Surface/disabled/fill | Soft/disabled/fill | Hero/disabled/fill | Hero raised/disabled/fill |
--disabled-text | Page/disabled/text | Surface/disabled/text | Soft/disabled/text | Hero/disabled/text | Hero raised/disabled/text |
Type
- Display · Barlow CondensedSelected work
- Body · DM SansPlain, warm writing at a comfortable size for reading on any screen.
Dynamic components on every background
Pick a background: the same components re-color from the Background tokens, just as they do across the site. These are pictures of each state; the real controls are live everywhere else.
Page
Accessibility
I checked the site against WCAG 2.2 AA in both light and dark mode, on every background a component can sit on. Text, labels and links have at least 4.5:1 contrast with their background, and large display text at least 3:1. Button text keeps 4.5:1 on the button in every state, and buttons keep 3:1 against the background on hover, press and focus. Focus rings have at least 3:1 contrast and follow each component's shape. Buttons, tabs and standalone links are at least 24 by 24 pixels. Every image has alt text, and the site works with a keyboard alone. Turn on "Show accessibility" in the design system above to see the measurements on any background.
Claude Code ran automated checks on every page in both modes, at desktop and phone sizes: alt text on every image, valid roles and ARIA attributes on buttons, tabs and links, keyboard focus on every control, and contrast for every color pair. I reviewed the results and fixed what they found.
| Contrast | Required | Lowest measured | Result |
|---|---|---|---|
| Body, secondary text and labels | 4.5:1 | 4.74:1 | Pass |
| Links: default, hover, pressed | 4.5:1 | 4.63:1 | Pass |
| Large display text | 3:1 | 6.68:1 | Pass |
| Tab text: default, hover, current | 4.5:1 | 4.74:1 | Pass |
| Tab bar, current | 3:1 | 5.14:1 | Pass |
| Button text on the button, every state | 4.5:1 | 5.67:1 | Pass |
| Button against the background: default, hover, pressed, focus | 3:1 | 4.63:1 | Pass |
| Focus ring, every component | 3:1 | 5.14:1 | Pass |
| Theme toggle icon | 3:1 | 6.68:1 | Pass |
| Target size (2.5.8) | Required | Smallest measured | Result |
|---|---|---|---|
| Buttons | 24 × 24 | 196 × 47 | Pass |
| Nav and content tabs | 24 × 24 | 33 × 47 | Pass |
| Theme toggle | 24 × 24 | 40 × 40 | Pass |
| Menu button (phone) | 24 × 24 | 70 × 39 | Pass |
| Standalone links | 24 tall | 24 tall | Pass |
310 color pairs measured: 31 checks × 5 backgrounds × 2 modes. Disabled controls are exempt from contrast under WCAG and are marked as such.