Justin Lancaster Product Design
Home Colophon

The system behind this site

Three of the case studies on this site are design systems. This is a fourth — and the only one you can inspect while you read about it. Every token, scale, and rule below is the one rendering this page.

Color

One gradient carries the whole brand. Everything else is a neutral doing structural work. Deliberately narrow — a system with four accent colors is a system nobody can apply consistently.

Primary gradient

--grad
135° · 3B6EF8 → 7C3AED
--signal
#3B6EF8
--violet
#7C3AED
--grad-deep
Contact section

Surfaces

--white
#FFFFFF
--tint
#F2F5FF · hero
--alt
#F7F8FC · alternating
--navy
#0B1120 · footer only

Ink & lines

--ink
#0B1120 · 16.9:1
--ink-2
#334155 · 10.4:1
--ink-3
#627288 · 4.5:1 AA
--ink-4
#66707F · 4.6:1 AA
--line
#E4E9F2
--line-soft
#F1F5F9

Type

Three families, each with one job. Montserrat 800/900 for display only — it has the width to hold a 5rem headline without looking thin. DM Sans for everything a person actually reads. DM Mono for labels, indices, and data, where tabular numerals matter more than warmth.

Complex systems Montserrat 900 · clamp(2.6–5.1rem) · -.035em
Section heading Montserrat 800 · clamp(2–3.1rem) · -.02em
Card title Montserrat 800 · 1.28rem · -.025em
Body copy at 17px, 1.65 line height DM Sans 400 · 17px / 1.65
Wordmark role, light weight DM Sans 300 · 15px
Eyebrow label · 001 DM Mono 500 · 12px · .14em · tabular

Headings use text-wrap: balance and body copy uses text-wrap: pretty. Costs nothing, removes every orphan and widow on the site. Numerals in mono contexts are locked to font-variant-numeric: tabular-nums so the case study indices stay optically aligned in a column.

Space & layout

Fluid rather than stepped. Every major rhythm is a clamp(), so the layout breathes between breakpoints instead of jumping at them.

TokenValueApplied to
--max1200pxContent container
--gutclamp(1.25rem, 4vw, 2.5rem)Horizontal padding
Sectionclamp(4.5rem, 9vw, 8rem)Vertical rhythm
Case study measure880pxLong-form reading column
Radius10 / 14 / 18pxButtons / panels / cards
Breakpoints560 · 700 · 820 · 960 · 1000 · 1200Set by content, not device

Breakpoints were chosen by measuring text against available width, not by picking device sizes. The About heading holds one line above 1000px and the grid stacks below it, because that is the width where 511px of heading stops fitting in a 530px column.

Components

Small inventory on purpose. Buttons, cards, panels, marquee, icon tile, section rail. Everything else is composition.

Primary Ghost 007 Eyebrow

44px gradient tile, 20px stroke icon, one animation each. The motion is the affordance — a static icon in this position would just be decoration.

Motion

One easing curve for everything, one duration band, and a hard rule: motion must either communicate state or connect two views. Nothing moves to be impressive.

BehaviorSpecWhy it exists
--easecubic-bezier(.22,.61,.36,1)Single curve, whole site
Hover lifttranslateY(-1 to -4px) · .2–.3sConfirms the target is interactive
Scroll reveal20px rise · .65s · 80ms staggerDirects reading order on first pass
View transition.45s · thumbnail → hero morphPreserves object identity across pages. The name is assigned at click time, since the hero carousel and the work grid show the same five studies and a view-transition-name must be unique per document
Marquee42s linear · pauses on hoverLegibility beats continuous motion
Reading progress2px gradient · rAF-throttledDepth cue on long case studies

Every one of these is wrapped in prefers-reduced-motion. With it enabled the site is fully static — reveals resolve to visible, the marquee stops, view transitions turn off, the progress bar hides. Content never depends on motion to be reachable.

Accessibility

AreaDecision
ContrastBody ink 10.4:1, secondary 4.8:1 — AA at every size in use
Gradient text@supports fallback to solid #3B6EF8; print falls back to solid ink
FocusVisible 2px ring with 3px offset on every interactive element
KeyboardSkip link; arrow keys page between case studies; rail is tab-reachable
MotionFull prefers-reduced-motion coverage
SemanticsOne h1 per page, ordered headings, labelled landmarks, alt text on all images
No-JSReveals, rail, and progress bar are enhancements — content reads without them

Stack

Vanilla HTML, CSS, and a few hundred lines of JavaScript. No framework, no bundler, no dependencies, no build step. The ten case study pages are generated from a Python content dictionary so they cannot drift apart, but the output is plain static HTML with no runtime dependency on the generator.

LayerChoice
MarkupStatic HTML, one file per page
StylesOne stylesheet, CSS custom properties throughout
BehaviorVanilla JS — IntersectionObserver, rAF, pointer events
TransitionsNative cross-document View Transitions API
GenerationPython, dev-time only
HostingVercel static, clean URLs, 301s from prior paths
Resume PDFPrint stylesheet — one source of truth, never a stale file

Why publish this

Muse, GE Core, and the chart library underneath Digital Command Center are all described here in the past tense, in someone else's product, behind someone else's NDA. It seemed worth having one you can open the inspector on.

The interesting part of a design system was never the swatch grid. It is the set of decisions about what to leave out: one gradient instead of a palette, six breakpoints chosen by measuring text, an icon inventory small enough to hold in your head. Those constraints are the reason a system survives contact with a second designer.