The reference
One color, one job
Every color here has one job, and every sample on this page is rendered by the site’s real classes and tokens. What you see is what ships.
Part 01
Foundations
Color, typography, spacing, and art. Everything downstream composes from these values; nothing below invents a new one.
Color
The law: four hues, each with exactly one job. Nothing is decorative twice.
Cream
#fef6e9
The only page ground.
Teal
#003f48
Structure and interaction: the only band, every link, hover, focus ring, and stroke on light.
Vermillion
#e73d00
"Talk to me" only: header CTA pill, pre-footer CTA band, contact hero. The CTA is a bright #e73d00 fill with a cream label locked to 19px bold or larger (WCAG large text, 3.86:1), hovering to teal.
Peach
#ffb8a4
Accent-on-dark, the band's accent, and a panel ground. Never an accent on a light ground.
Semantic tokens
The law is implemented as semantic CSS variables in :root, mapped to Tailwind utilities via @theme. Write text-strong, bg-surface, border-line; never raw hex. Each swatch below renders from its own variable.
| Token | Value | Role | AA notes |
|---|---|---|---|
--bg | #fef6e9 | The page ground: one first-class ground, warm cream. | The ground every ratio below is measured against. |
--surface | #f8eedd | Cards, panels, code blocks. | Carries the same ink stack as the ground. |
--line | #e5dac6 | Hairline divider: the page rhythm. | Decorative; never carries text. |
--rule | #003f48 | Heavy section rule, brand teal. | Structural breaks only; keep distinct from --line. |
--accent | #003f48 | Deep teal, the working accent: structure and interaction, links, focus, strokes on light. | 10.8:1 on the ground, AAA as text. |
--accent-warm | #e73d00 | Vermillion, the conversation color: CTA, avail-dot, wordmark dot. | 3.9:1, never small text. |
--accent-warm-ink | #bf3400 | The only text-safe vermillion. | 5.3:1 on the ground. |
--tint | #f3e9d8 | The wash: active, hover, and selected grounds only. | Never a resting card ground. |
--wash-cool | #e7f0ec | Cool wash for supporting surfaces. | Never the page ground, never inside a band, one per viewport at most, no hairline border on it. |
--strong | #182528 | Headline ink, top of the four-step ink stack. | Hierarchy comes from ink, not size alone. |
--body | #2f3d3e | Default body ink. | AAA at body sizes. |
--body-2 | #3e4c4e | Secondary body ink. | AAA at body sizes. |
--muted | #5f6e73 | Captions, labels, metadata. | 4.9:1, AA for small text. |
The accent carries three support values: --accent-ink for links, focus, and small text, --accent-contrast for text sitting on the accent, and --accent-hoverfor the filled button’s hover. On cream all three resolve within teal.
Panel palette
Amendment: chrome is unchanged under One Color, One Job; all new color enters through content panels inside the illustration exemption, drawn only from the five sanctioned panel grounds, and every case study and essay owns exactly one panel hue used everywhere its art appears.
Peach
#ffb8a4
teal 7.01 · ink 9.48
Orange
#ffb567
teal 6.68 · ink 9.04
Steel
#b2c0cc
teal 6.26 · ink 8.47
Mint
#cfe5e0
teal 8.83 · ink 11.95
Cream
#fef6e9
chips and filled accents only
Ratios above are teal ink and #182528 ink on each ground. On a panel, strokes are teal or ink only. Cream appears as filled accents and chips, never as a stroke or text on a hue (1.2 to 1.7:1). Vermillion is banned on panels.
Typography
One typeface, Figtree, self-hosted via next/font, carrying every register from the tracked uppercase label to the oversized hero. The three logical slots (display, body, mono) all resolve to Figtree; the mono slot is the label voice, and true monospace exists only inside code blocks.
.t-hero
Order
.t-h1
Complexity, clarified
.t-h2
The numbers behind the claim
.t-display
Structure plus interaction
.t-sub
A short editorial subhead
.t-lede
For ten years I’ve led experience strategy for some of the most complex digital ecosystems in healthcare and higher education.
.label
Director of Experience · Greater Boston
| Class | Size | Line height | Weight | Tracking | Casing | Usage |
|---|---|---|---|---|---|---|
.t-hero | clamp(56px, 16.5vw, 248px) | 0.82 | 800 | -0.03em | Uppercase | Poster voice: full-bleed heroes with no competing art, one per page (Work, 404). |
.t-h1 | clamp(48px, 9.2vw, 112px) | 0.92 | 800 | -0.03em | Sentence | Split heroes that share the viewport with live art (Expertise, About). |
.t-h2 | clamp(32px, 4.6vw, 60px) | 1.02 | 700 | -0.02em | Sentence | Section heading voice. |
.t-display | clamp(30px, 3.8vw, 54px) | 1.04 | 700 | -0.015em | Sentence | Display voice for sub-page and reference headings. |
.t-sub | clamp(18px, 1.9vw, 24px) | 1.6 (inherited) | 500 | -0.01em | Sentence | Short editorial subhead (pillar tag lines). |
.t-lede | clamp(18px, 1.6vw, 21px) | 1.55 | 400 | none | Sentence | Intro paragraph under a hero; body-2 ink. |
.label | 12px | 1.6 (inherited) | 500 | 0.12em | Uppercase | Eyebrows, dates, nav, breadcrumbs, captions, chips. Never below 12px. |
Display voices set text-wrap: balance and negative tracking; reuse these stops rather than inventing new clamp values.
Hero role is fixed, not a size preference. Full-bleed heroes take .t-hero (Work, 404). Split heroes that share the viewport with live art take .t-h1(Expertise, About). Home’s claim is the one sentence-case hero by design; signature pages (Insights, Lab, Contact) carry their own art moments instead.
Spacing and radius
Sections keep a consistent vertical beat, hairlines carry the rhythm, and exactly two radii exist.
Section beat
Sections run py-12 md:py-16 at rest, opening up to py-16 md:py-24 for heavier moments. Content sits in <Container>, measure capped at var(--maxw) (1400px). Grids run gap-4 for dense swatch rows and gap-6 for card grids.
.hair-t: 1px, var(--line), the page rhythm
.rule-t: 1px, var(--rule), heavy structural breaks only
The two radii
--r-card (12px) rounds every resting object; --r-chip (999px pill) rounds every chip, button, and badge. Nothing else.
Iconography and art
Diagrams and figures are monoline ink-on-paper: 1px to 2px strokes on the paper ground, abstract, no gradients, no stock, no screenshots. Illustration exemption: CaseArt, ArticleGlyph, CaseStudyViz, insight SVGs, and lab canvases keep the full palette; they are art, not UI. CoverArt (case covers recomposed from tokens on a panel) and BandStrip (three strips in the CaseArt monoline dialect, re-rollable on click) are the sanctioned art systems.
BandStrip, reduced height
One variant per panel ground, six placements on the site: the home stats band and the Expertise proof strip take dots, the home principles band and the Expertise FAQ take arcs, and the About and Lab pages take contours. Click a strip: the geometry re-rolls from where the cursor landed and tweens into a new arrangement.
arcs
dots
contours
Part 02
Components
The kit is small on purpose: one card recipe, four buttons, two link idioms, two chips. Extend by reusing these pieces, not by inventing new ones.
Buttons
Pill buttons composed as .btn plus a variant. Solid navigates to proof, CTA asks for contact.
Primary
.btn-solid- Hover
- Fill deepens to --accent-hover (#00303a); label stays cream.
- Focus
- Global :focus-visible ring: 2px --accent-ink outline, 2px offset.
- Use
- The filled primary. Solid navigates to proof: case studies, the work index.
Secondary
.btn-primary- Hover
- Outline fills with teal; label flips to cream.
- Focus
- Global :focus-visible ring: 2px --accent-ink outline, 2px offset.
- Use
- The teal outline. Second action beside a primary, never competing with it.
Tertiary
.btn-ghost- Hover
- Hairline border darkens to strong ink; no fill.
- Focus
- Global :focus-visible ring: 2px --accent-ink outline, 2px offset.
- Use
- The quiet option: utility actions, view toggles, third choices.
Conversation
.btn-cta- Hover
- Fill and border flip to teal; the cream label holds.
- Focus
- Global :focus-visible ring: 2px --accent-ink outline, 2px offset.
- Use
- The one vermillion button, hardcoded by role so no ground remap can restyle it. CTA asks for contact; nothing else may.
On the vermillion field
The CTA renders from .btn-cta alone, so the samples here track globals: bright #e73d00 fill with a cream label locked to 19px bold or larger, passing WCAG as large text at 3.86:1. Inside .field-vermillion it inverts to a cream fill with an #e73d00 label at the same large size. Both hover to teal.
Links
Chase underline
Inline text link
Running prose links render in accent ink with a 1.5px underline offset 3px, the markdown idiom. Teal is every link; nothing else underlines.
Cards
One recipe for every resting object on the page: surface ground, a 1px hairline stroke, a 12px radius. On hover the stroke warms toward teal (a 45% accent mix into the hairline). No lift, no scale, no glow. Case cards open with a panel cover in the case’s hue.
Ten-thousand pages, one path
Anatomy: the cover is a .case-panel in the case’s hue with the art recomposed from tokens, a cream .metric-chip sits bottom-left, and the body stays the plain .card surface: pill, title, blurb, link.
Chips and tags
.proj-chip is a real toggle: hover darkens the label and border to strong ink, and aria-pressed="true" fills it with the accent, so the visual state and the accessibility tree can never disagree. The PILL tag is the passive counterpart: label voice inside a hairline pill, no interaction.
Form elements
.search-trigger: a 41px circle in the ghost-button idiom (1.5px hairline, hover to full ink), sized to pair with the header’s CTA pill.
Forms are limited to search and the contact copy action by design; there is no field kit. The search input (.search-input) drops its own focus ring because the open panel is the focus indicator, and the contact page’s copy-email button composes from the kit (hairline stroke, label voice, global focus ring) rather than a dedicated class.
Part 03
Patterns
Section-scoped token remaps do the heavy lifting: any component dropped inside a band, field, or panel reads correctly with zero component edits.
The band remap
.band
Full-bleed teal section. Same semantic tokens, locally re-pointed: peach becomes the accent here, vermillion never enters a band.
50+
The remap re-declares the Tailwind --color-* aliases so utilities like text-strongrecompute against the band’s tokens. Never hand-roll dark styles; wrap the section instead.
Color fields
.field-vermillion
A poster surface
The one full-strength hue ground, same remap contract as the band. Display text and labels only, no paragraph copy; small text routes through --field-ink. Its three sanctioned homes: CtaBand, ContactHero, and the home marquee wrapper.
Content panels
.case-panel is the same remap contract for art grounds: set --panel to a hex from lib/data/panels.ts and every token-driven child, CoverArt included, recomposes with zero component edits. Strokes stay teal, text stays ink, cream is chips and filled accents only. The five grounds and their ratios are documented under Foundations.
Wash rules
Tint is a wash: --tint is for active, hover, and selected grounds only, never a resting card ground.
Cool wash: --wash-cool grounds supporting surfaces only. Never the page ground, never inside a band, at most one per viewport, and no hairline border on it: the tint change itself is the edge.
Route rhythm
Pages rest on cream and articulate in full-bleed teal bands, cream to band and back, hairlines carrying the beat between them. CtaBand slot rule: one vermillion ask per page, always pre-footer. Nothing else may ask.
Part 04
Motion
Two eases, three durations, and a handful of systems that read data attributes off server markup. Composited properties only.
Easing
--ease-expocubic-bezier(0.19, 1, 0.22, 1)--ease-quintcubic-bezier(0.23, 1, 0.32, 1)
Duration
--dur-micro0.3s: color/opacity--dur-mask0.5s: pills, swaps--dur-struct0.75s: structural moves, underlines
Systems
- Reveals: one GSAP context reads data attributes off server markup:
data-rv-reveal(fade and rise,data-rv-staggerfor lists),data-rv-split(masked line-by-line titles),data-rv-num(ghost numeral parallax),data-rv-rule(hairline draws in). - One custom ease: GSAP entrances share a single CustomEase registered as
"snappy"inlib/eases.ts: a fast rise that settles without overshoot. - Strip entrances, drift, and re-roll: BandStrip elements enter via
data-strip-el, and thedata-strip-driftgroup scrubs a gentle 2.5% travel against scroll so strips feel placed, not printed. A click re-rolls the shape numbers inlib/strip-art.tsand tweens every element to the new arrangement, seeded by the cursor's position across the strip. - CoverArt draw-in: cover strokes (
.cv-edge) draw in via pathLength, fills and dash echoes fade up, staggered per element by--d, once when the cover scrolls in. - View transitions: the outgoing page drops out in 150ms and the incoming page rises 240ms after a 120ms clear; the sequence completes by 360ms. The header never animates: it is the spatial anchor.
- Entry beat: once per tab session the wordmark dot pops (360ms) and the nav row rises (480ms, delayed 80ms). Transform only, finished by 560ms.
Reduced-motion contract: every animation ships its final state; motion is additive. Server markup arrives finished, FX components early-return on the media query, and a global reset zeroes every duration.
Part 05
Accessibility
The contrast math is part of the system, not an audit afterthought. These are the numbers the tokens are built on.
- AA floors per surface: on cream, teal text is 10.8:1 (AAA) and
--mutedholds 4.9:1. In a band, peach is 7.0:1 on teal and the band’s muted 5.5:1. On the vermillion field, small text routes through--field-ink, the only 4.5:1+ ink there, and--strongis display sizes only at 3.9:1. On panels,--mutedmixes at 78% ink to clear 4.5:1 on steel, the palest ground. - The large-text CTA contract: bright vermillion under a cream label is 3.86:1 against the 3:1 large-text floor, so the CTA label is locked to 19px bold or larger. Never shrink it.
- Focus policy:
:focus-visibledraws a 2px--accent-inkoutline at 2px offset on every control. The rare exceptions carry their own indicator (the search input’s open panel, the graph toggle’s inset outline); nothing loses focus visibility. - No-JS contract: hidden pre-animation states apply only after an inline script stamps
html.js; without JavaScript the served markup is the finished page. - Keyboard and inert: interactive state is driven by ARIA (
aria-expanded,aria-pressed) so visuals and the accessibility tree cannot disagree. Hidden or off-screen layers (the mobile table of contents, the case progress rail) carryinertso they are never tab stops, and the mobile menu contains focus while open.