Folio

Design System

Atomic component library for the portfolio site — tokens, atoms, molecules, organisms and case-study patterns. Resize the browser to watch breakpoints respond, or toggle the theme to preview the dark scheme.

v2.0 8 sections 40+ tokens light + dark schemes
01

Responsive Breakpoints

Mobile-first: components stack single-column by default, then progressively enhance at each threshold. Tokens --bp-sm: 480px--bp-md: 768px--bp-lg: 1024px--bp-xl: 1200px · containers --container-max: 800px--container-narrow: 720px — the badge in the bottom-right corner tracks the live viewport.

Mobile 0–679px · single column
Tablet 768–1023px · 2-col grids
Desktop 1024–1199px · full layout
Large ≥ 1200px · larger type, 1080px container

Layout behavior per component

ComponentMobile <680Tablet 768+Desktop 1024+Large 1200+
NavHamburger toggleFull links · glass on scroll1080px inner
Hero70vh · h1 = text-hero (fluid)h1 max-width 600px90vh · h1 caps at 4.25rem
About layout1 column1.2fr : 1fr splitLarger gap
Section headStackedBaseline row
Work grid1 column1.5fr / 1frLarger gap
Essay index1 column2 columns · lead spans full
Timeline1 column · rail2 cols: head + highlights
Container16px padding800px max800px max1080px max
02

Tokens

Color palette

Values shown for the light scheme — click any swatch to copy. The dark scheme remaps every token via [data-theme="dark"]; toggle it from the sidebar.

Typography scale

--text-xs
0.75rem · 12px · Inter 400
The quick brown fox jumps over the lazy dog
--text-sm
0.875rem · 14px · Inter 400
The quick brown fox jumps over the lazy dog
--text-base
1rem · 16px · EB Garamond (body)
The quick brown fox jumps over the lazy dog. EB Garamond.
--text-lg
1.125rem · 18px · EB Garamond
The quick brown fox jumps over the lazy dog
--text-xl
1.25rem · 20px · EB Garamond
The quick brown fox jumps over the lazy dog
--text-2xl
1.5rem · 24px · Inter 600
Heading level two
--text-3xl
1.875rem · 30px · Inter 700
Heading level three
--text-4xl
2.25rem · 36px · Inter 700 · −0.025em
Heading level one
--text-display
clamp(1.875rem → 3rem) · Inter 700 · −0.03em
Case-study titles
--text-hero
clamp(2.25rem → 4.25rem) · Inter 700 · −0.03em
I turn user insights into product decisions

The static scale steps up one notch at ≥ 1200px (see layout.css): lg → 20px, xl → 24px, 2xl → 28px, 3xl → 36px, 4xl → 48px. --text-hero and --text-display are fluid clamp() values — no breakpoint needed.

Spacing scale · 4px base

--space-14px
--space-28px
--space-312px
--space-416px
--space-520px
--space-624px
--space-832px
--space-1040px
--space-1248px
--space-1664px
--space-2080px
--space-24120px

Shadows

--shadow-sm --shadow-md --shadow-lg --shadow-card-hover
Low-elevation system — borders do most of the work; shadows reserved for hover and overlays. --shadow-card-hover = shadow-md in light; in dark mode it deepens the drop shadow and adds a subtle primary ring. Dark scheme deepens alpha automatically.

Border radii

--radius-sm · 4px --radius-md · 8px --radius-lg · 12px --radius-full

Motion

--ease-out · cubic-bezier(0.22, 0.61, 0.36, 1) --ease-out-soft · cubic-bezier(0.16, 1, 0.3, 1) --ease-in-out · cubic-bezier(0.65, 0, 0.35, 1) --ease-spring · cubic-bezier(0.34, 1.56, 0.64, 1) --dur-instant · 0.1s --dur-fast · 0.2s --dur-med · 0.3s --dur-slow · 0.5s --dur-slower · 0.9s --dur-very-slow · 1.4s --move-sm · 8px --move-md · 16px
hover to preview --ease-out
Calm editorial motion — an easing family (emphasized --ease-out-soft for entrances and reveals, --ease-out for micro-interactions) and a six-step duration scale from instant feedback to very-slow entrance sweeps. Travel distances are short (--move-sm/--move-md). The complete system lives in §08 Motion.
03

Atoms

Badge .badge

Portfolio 2026
Hero eyebrow. Inline-flex, uppercase, primary color with 6px dot.

Tag .tag

Hero Case Study
Cards & case-study headers. 11px / 600 / uppercase; letter-spacing opens to 0.12em when its parent card is hovered.

Stat .stat.stat-label

0%
Reduction in onboarding drop-off
Used in card-hero and impact banners. Large primary numeral + muted uppercase label.

Section label .section-label

Uppercase eyebrow — retained as an atom, but no longer used on the index: section heads now use .section-head. The hero badge remains the page's single eyebrow.

Hero stat .hero-stat

6+ Years of experience
12+ Studies led
2 Papers published
Editorial anchor under the hero tagline — bordered top rule, 3-up grid (2+1 span on ≤480px). Numbers are Inter 700, labels muted uppercase. The retired .hero-footer (scroll-hint + DS link) no longer ships in the hero; the DS link lives in the site footer.

Timeline dot .timeline-dot

24px circle, 2px primary border, 8px inner fill. Sits in the 48px gutter above the timeline rail (rail at left 12px, centered under the dot).

Contact link .contact-link.contact-link-fill

DefaultOutline
:hoverOutline
:activeOutline
:focus-visibleOutline
Default · fillSolid
:hover · fillSolid
:active · fillSolid
:focus · fillSolid
Hover fills primary and lifts 1px with shadow-sm; active settles back down. Focus ring is global (:focus-visible in reset.css). Static states rendered via .is-hover.is-active.is-focus.

Toast .toast

Message copied to clipboard
Fixed bottom-center pill; .show fades in with an 8px slide-up. Also fires when you copy a swatch hex value.
04

Molecules

Card .card

Hover: border → primary, shadow-md, translateY(−3px); tag tracking opens; arrow slides 3px. Active: translateY(−1px).

Card — states

Card row .card-row

Borderless light-case variant for the work grid — hairline top rule instead of a box, mono index marker. Hover: no lift/shadow; tag tracking opens, title fades to primary with a 1px underline sweeping in from the left (nav-link pattern), arrow slides 3px.

Card hero .card-hero

Spans 2 rows in work-grid (grid-row: span 2), flex column centered. Stat is serif italic (500) to distinguish from impact banners. Mobile: grid-row auto.

About skill .about-skill

Research

Mixed-methods study design, usability testing, field research.

Strategy

Roadmap planning, stakeholder alignment, research ops.

Static pillar — not interactive, no hover state (the retired border-left tint + background wash implied an affordance that didn't exist). 36px primary-subtle icon chip, text column right; pillars are separated by gap only — no rules, so the About section carries a single horizontal line (the section-head underline).

Approach card .approach-card

The tension between academic rigor and commercial speed is not a problem to be solved — it is a design constraint that produces better work.
— Alex Morgan
Primary-subtle bg + primary border. Decorative quote mark (absolute, 30% opacity); italic blockquote with 2px primary left border.

Timeline item .timeline-item

Senior UX Researcher

Vercel
2023–Present
  • Led 12+ mixed-methods studies across developer tools and AI features
  • Established quarterly research cycle and cross-functional stakeholder alignment

Product Manager

Notion
2021–2023
  • Owned analytics and data visualization product area
Rail via .timeline::before (1px, left: 12px) — it draws down once when the section scrolls into view (see §08 Motion). The dot sits in the 48px gutter. ≥768px the item becomes a 2-col grid — head (role / org / mono period) left, highlights right. Bullets are 4px primary dots at 45% opacity.
05

Organisms

Nav .nav

Fixed on real pages; .scrolled swaps the solid background for --color-bg-glass + 12px backdrop blur, plus shadow-sm. Link hover = primary color + underline scaleX (shown on "Work"). A scrollspy adds .is-active to the link matching the section in view (same underline language). ≤680px collapses to a hamburger toggle.

Hero .hero

Portfolio 2026

I turn user insights into product decisions

UX Researcher and Product Manager bridging academic rigor and commercial speed — designing studies that don't just produce reports, they produce roadmaps.
6+ Years of experience
12+ Studies led
2 Papers published
Full height on real pages: 90vh (70vh mobile). Entrance (see §08 Motion): dot pop → badge → h1 line-mask reveal → tagline → hero-stats. The hero also parallax-drifts on scroll (fine pointers only). h1 uses fluid --text-hero (2.25–4.25rem, −0.03em); em inside the tagline is serif italic for a display accent.

Section head .section-head

Selected work

03 case studies
Replaces the retired uppercase .section-label eyebrows on the index. Serif roman title + mono meta on a baseline row; stacks on ≤480px. Meta content varies per section (counts, date ranges, venue tags). Revealed with a fade + rise (see §08 Motion).

About layout .about-layout

I'm a UX Researcher with 6+ years of Product Management experience. I design studies that don't just describe behavior, they explain it — always anchoring product decisions in rigorous research that lands.

Research

Mixed-methods study design, usability testing, field research.

Strategy

Roadmap planning, stakeholder alignment, research ops.

Execution

Product discovery, feature definition, A/B testing.

Editorial split: 1.2fr : 1fr — serif bio left, skills right as icon-chip pillars separated by gap (static, non-interactive). The section carries a single rule (the section-head underline); the quote treatments (.about-quote, .about-philosophy) were retired. Single column ≤680px.

Work grid .work-grid

Essay index .essay-index

Journal index replacing the retired boxed .card-essay grid. The lead essay renders as a .card-language box (surface, border, radius; hover lifts + shadow-card-hover + primary border); the remaining rows keep the journal treatment — hairline rules, title fades to primary with a 1px underline sweeping in from the left (nav-link pattern), arrow slides 3px and shifts muted → primary. ≥1024px the non-lead rows form a 2-column grid.

Contact card .contact-card

Let's work together

Available for full-time roles and consulting engagements.

Centered, surface background. Fill variant = primary CTA. Padding scales space-6 → space-8 → space-12. The index currently closes without a contact section — the footer carries the email CTA if needed later.

Publication .publication

Cognitive Models of Information Scent in Developer Tools
Alex Morgan, Sarah Chen, James Rivera Conference on Human Factors in Computing Systems, 2018CHI
Evaluating Interface Complexity Through Eye-Tracking and Cognitive Load Analysis
Alex Morgan, David Park User Interface Software and Technology, 2019UIST
Borderless citation row — no background, hairline top rule (first row bare), no row-level hover. Hover acts on the title only: color → primary with the ↗ arrow revealed. Elevated cards now use the neutral --color-surface-2 (gray, no warm cast) on case-study pages only.
06

Case Study Components

These live in css/case-study.css and are shared across hero.htmllight-1.htmllight-2.html.

Impact banner .impact-banner

60%
Drop-off reduction
40%
Shorter sales cycle
3
Sprints influenced
3-column grid on primary-subtle + primary border. Collapses to 1 column ≤680px.

Finding card .finding

1

Users expected "deploy" to work immediately with default settings

Finding: 70% of test participants clicked "Deploy" without configuring environment variables, hitting an error state.
Decision: Added pre-deploy validation that detects missing configuration and suggests sensible defaults.
Numbered circle (28px, primary) + heading + finding + decision block (italic, 2px primary left border). Hover: shadow-md + primary border.

Methods grid .methods-grid.method-card

Unmoderated usability testing

24 participants across US, UK, Germany, and Japan.

In-depth interviews

12 follow-up interviews with users who abandoned activation.

Funnel analytics audit

Quantitative analysis of 10,000 sign-up sessions.

Competitive teardown

Compared onboarding flows of 5 competitor products.

2-column grid → 1 column ≤680px. Surface bg, border, radius-md per card.

Outcome list .outcome-list

  • Activation rate34% → 54%
  • Sales cycle length18 days → 11 days
  • Time-to-deploy (median)11 min → 2.5 min
Label/value pairs, flex space-between, hairline separators. Values primary, weight 700.

Reflection list .reflection-list

  • We should have started with a competitive teardown. The template wizard was inspired by a competitor's pattern we discovered mid-study.
  • Cross-market differences were larger than expected. We found meaningful differences in how Japanese users approached template selection.
Bordered list items, strong lead-in, serif body. Last child drops its border.

Case footer .case-footer

Hairline top separator, space-between. Hover widens the arrow gap and deepens to primary-hover.
07

Image Components

For user-uploaded images in case studies and portfolio pages. Supports single images and series (carousel, grid, filmstrip). No captions by default — replace placeholders with <img> tags.

Single image .img-media

[ User-uploaded image — 1200×800 recommended ]
Border + radius + overflow hidden. Placeholder renders when no src is set. Add .contain for diagrams/logos.

Contained .img-media.contain

[ Diagram or logo — fits within container ]
Image scales to fit without cropping — process diagrams, architecture charts, logos.

Carousel .img-carousel

Narrative style — no border, dots or counter. Ghost arrows + primary progress bar appear on hover. Scroll-snap driven, fully draggable.

Grid gallery .img-grid

Image 1
Image 2
Image 3
Image 4
Image 5
Auto-fill, min 280px per item. Hover: scale(1.02) + shadow-md. Fixed-column modifiers: .img-grid-2.img-grid-3.img-grid-4. Single column on mobile.

Filmstrip .img-filmstrip

Step 1 — Research setup
1
Step 2 — Data collection
2
Step 3 — Analysis & findings
3
Vertical stack with numbered badges (top-left) for process sequences and before/after series. Drop .img-filmstrip-num if numbering isn't needed.
08

Motion

Calm editorial motion: long durations, short travel, one easing family. Entrances and reveals are JS-driven on production pages (anime.js + IntersectionObserver), and every animation collapses to instant under prefers-reduced-motion: reduce.

Easing family

--ease-out
--ease-out-soft
--ease-in-out
--ease-spring
--ease-out-soft is the house curve — entrances, reveals, transitions. --ease-out covers hovers and micro-interactions; spring is reserved for the hero dot pop; in-out for symmetric motion. Hover each track to compare.

Hero entrance — h1 line mask

Portfolio 2026

I turn user insights into product decisions

hover the headline to replay the entrance
On the index, JS splits the headline into visual lines after document.fonts.ready (1.5s cap), wrapping each in .line (overflow mask, 0.2em descender compensation) > .line-inner, which rises from a fully-masked offset (125% > mask height) to 0 (1050ms, 110ms stagger, --ease-out-soft). If splitting fails, .anim-mask-up falls back to a clip-path wipe. Full cascade: dot pop → badge → lines → tagline → hero stats.

Scroll reveals

clip wipe · .is-revealed
media · clipReveal

Section head

fade + rise
pull rule · ruleDrawX
timeline rail · ruleDrawY
Sections reveal children with fade + 16px rise (850ms, 170ms stagger). Media (.mockup-img / .img-media) clip-wipes in on scroll (1.1s); the timeline rail draws down once (1.1s + 0.2s delay); pull-quote rules draw out with .anim-rule-draw; hero and impact stats count up (1.2–1.3s). All triggers are one-shot IntersectionObservers.

Scroll-linked — hero parallax

drift · scrollY × 0.22 (capped 140px) fade · begins past 70% viewport gate · pointer: fine + reduced-motion
The hero container translates slower than the page (rAF-throttled, transform-only) and fades to 40% opacity as the viewport scrolls past it. Disabled on touch devices and under reduced motion.

Page transitions — directional

out · translateY(-12px) + fade · 0.3s in · from translateY(12px) + fade · 0.3s ease · cubic-bezier(0.16, 1, 0.3, 1) reduced motion · navigate instantly
Navigation between index, case studies, essays and this design system rises the outgoing page out and slides the incoming page in — a sessionStorage protocol shared by all four page types. Both directions rise upward; under reduced motion the choreography is skipped entirely.

Micro-interactions

underline sweep button lift
image zoom
tag · letter-spacing
Consistent hover language: underline sweeps, card lift + shadow, arrow slides, tag letter-spacing drift, image zoom (1.04, 0.5s). Theme-toggle icons rotate in via iconIn. Durations: --dur-fast 0.2s / --dur-med 0.3s.

Accessibility — prefers-reduced-motion

A global CSS guard collapses animation-duration, animation-delay, animation-iteration-count and transition-duration to 0.01ms (animations.css), and every JS block — entrances, reveals, counters, parallax, page transitions — is gated by matchMedia('(prefers-reduced-motion: reduce)'). Reduced-motion users get static content and instant navigation, never a blank page.

Verify with DevTools → Rendering → Emulate prefers-reduced-motion: reduce.
Copied