Skip to content

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.

TokenValueRoleAA notes
--bg#fef6e9The page ground: one first-class ground, warm cream.The ground every ratio below is measured against.
--surface#f8eeddCards, panels, code blocks.Carries the same ink stack as the ground.
--line#e5dac6Hairline divider: the page rhythm.Decorative; never carries text.
--rule#003f48Heavy section rule, brand teal.Structural breaks only; keep distinct from --line.
--accent#003f48Deep teal, the working accent: structure and interaction, links, focus, strokes on light.10.8:1 on the ground, AAA as text.
--accent-warm#e73d00Vermillion, the conversation color: CTA, avail-dot, wordmark dot.3.9:1, never small text.
--accent-warm-ink#bf3400The only text-safe vermillion.5.3:1 on the ground.
--tint#f3e9d8The wash: active, hover, and selected grounds only.Never a resting card ground.
--wash-cool#e7f0ecCool wash for supporting surfaces.Never the page ground, never inside a band, one per viewport at most, no hairline border on it.
--strong#182528Headline ink, top of the four-step ink stack.Hierarchy comes from ink, not size alone.
--body#2f3d3eDefault body ink.AAA at body sizes.
--body-2#3e4c4eSecondary body ink.AAA at body sizes.
--muted#5f6e73Captions, 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

ClassSizeLine heightWeightTrackingCasingUsage
.t-heroclamp(56px, 16.5vw, 248px)0.82800-0.03emUppercasePoster voice: full-bleed heroes with no competing art, one per page (Work, 404).
.t-h1clamp(48px, 9.2vw, 112px)0.92800-0.03emSentenceSplit heroes that share the viewport with live art (Expertise, About).
.t-h2clamp(32px, 4.6vw, 60px)1.02700-0.02emSentenceSection heading voice.
.t-displayclamp(30px, 3.8vw, 54px)1.04700-0.015emSentenceDisplay voice for sub-page and reference headings.
.t-subclamp(18px, 1.9vw, 24px)1.6 (inherited)500-0.01emSentenceShort editorial subhead (pillar tag lines).
.t-ledeclamp(18px, 1.6vw, 21px)1.55400noneSentenceIntro paragraph under a hero; body-2 ink.
.label12px1.6 (inherited)5000.12emUppercaseEyebrows, 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.

12px
Pill

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

.uline draws in from the left, exits to the right

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.

Case study

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

PILL tag

.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-expo cubic-bezier(0.19, 1, 0.22, 1)
  • --ease-quint cubic-bezier(0.23, 1, 0.32, 1)

Duration

  • --dur-micro 0.3s: color/opacity
  • --dur-mask 0.5s: pills, swaps
  • --dur-struct 0.75s: structural moves, underlines

Systems

  • Reveals: one GSAP context reads data attributes off server markup: data-rv-reveal (fade and rise, data-rv-stagger for 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" in lib/eases.ts: a fast rise that settles without overshoot.
  • Strip entrances, drift, and re-roll: BandStrip elements enter via data-strip-el, and the data-strip-drift group scrubs a gentle 2.5% travel against scroll so strips feel placed, not printed. A click re-rolls the shape numbers in lib/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 --strong is display sizes only at 3.9:1. On panels, --muted mixes 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-visible draws 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) carry inert so they are never tab stops, and the mobile menu contains focus while open.