design.
Premium playful minimalism. Black and white structure, one blue interaction color, and four elemental hues that each own a section.
Every sample here is rendered from src/design/tokens.json. No hardcoded values, so it cannot go stale.
color
surface
backgrounds and edges. what things sit on.
page background base
page gradient midpoint
page gradient end
raised card surface
inset wells, code blocks
hairlines, card borders
content
text. three weights of emphasis, all AA on every surface.
primary text
secondary text, captions
tertiary text, metadata
brand
the one blue. links, CTAs, focus rings.
links, CTAs, focus ring
accent text on light surfaces (AA-safe)
label sitting on an accent fill
section
one hue per bento section. fills and dots, never body text.
fire. Sights section accent. Fills and dots only.
sounds section accent. Fills and dots only.
air. Curiosity section accent. Fills and dots only.
earth. Creations section accent and live status. Fills and dots only.
Rare expressive accent. Never body text or a section default.
destructive fill
sights TEXT on its own tint (AA-safe)
sounds TEXT on its own tint (AA-safe)
curiosity TEXT on its own tint (AA-safe)
creations TEXT on its own tint (AA-safe)
spirit TEXT on its own tint (AA-safe)
destructive TEXT (AA-safe on card)
accent ramp
fixed tints of the blue interaction color. legacy components may still reference the sunrise alias while they migrate to semantic accent utilities.
type
families
--font-displayAdam Pang . 2026
Space Grotesk 700. Headings.
--font-bodyAdam Pang . 2026
Lato 300/400/700. Prose.
--font-monoAdam Pang . 2026
JetBrains Mono. Numerals, labels.
scale
--text-caption12px. Metadata, footnotes.
--text-label13px. Eyebrows, nav.
--text-body16px. Default prose.
--text-lead18px. Intro paragraphs.
--text-h324px. Card titles.
--text-h230px. Section titles.
--text-h136px. Page title. 3rem at sm+.
--text-display-sm28-40px fluid. Timeline beats, feature headings.
--text-display40-76px fluid. The one big statement per page.
tracking
--tracking-tightest-0.02emdisplay headings--tracking-tighter-0.01emsubheads--tracking-label0.12emuppercase eyebrows--tracking-wide0.22emuppercase kickersspace
a 4px base step. mobile gutters and card padding use --space-5, opening to --space-6 at sm+.
--space-1--space-2--space-3--space-4--space-5--space-6--space-8--space-10--space-14radius + shadow
radius
--radius-sm--radius-md--radius-lg--radius-fullshadow
--shadow-card--shadow-card-md--shadow-card-lgcomponents
hover and press the real controls, and tab through them to see :focus-visible, a 2px accent outline at 3px offset set globally. States marked simulated are frozen copies so the styling is visible without a pointer.
button
rounded-full, accent fill. the primary CTA.
button . secondary
card surface, hairline border. the alternative action.
link
1px underline, hairline decoration until hover.
input
these are real inputs. click and tab through them.
badge
status pill. section hues as fills, never as text.
card
--color-card on --color-line at --radius-lg. the standard container.
resting
shadow-card, no transform
hover
lift 2px, accent border, shadow-card-lg
motion
one easing curve everywhere. all of it collapses to 0ms under prefers-reduced-motion: reduce, enforced globally rather than per component.
--motion-easecubic-bezier(0.16, 1, 0.3, 1)the one easing curve--motion-duration-micro200mshover, color change--motion-duration-state400msstate transition--motion-duration-reveal700msscroll reveal--motion-lift-2pxhover translateYinfluences
sites studied while building this one, and exactly what was taken from each. a site earns a place here only if a real line of code traces back to it. a site that only shaped the tone, with no artifact to point to, does not qualify.
measure set in ch, not px. line length that tracks the font instead of a fixed pixel width. his whole site is 2kb of css and one script tag, the standing argument against adding weight here.
landed in src/app/about/page.tsx (max-w-[62ch])
a timeline as a path you travel, not a list you skim. scroll position mapped to a camera, not a wheel handler hijacked. also the color-quarantine move: rainbow hues live only in decoration (a gradient, a rim), never in text.
landed in src/components/JourneyTimeline.tsx . src/lib/journey.ts . src/components/Coin.tsx
not a source, a confirmation. arguably the most acclaimed personal technical site on the internet is built almost entirely on scroll-linked interactive diagrams, the same mechanic already in use here. one useful detail worth naming: a fixed accent used sparingly against an otherwise near-monochrome page.
landed in src/components/JourneyTimeline.tsx (validated, not sourced)
a flat list of named side projects, each with one honest line describing it. also the direct source for this page: he keeps his own /sites crediting influences, so this one does too.
landed in src/data/influences.ts (this file)
building something on-brand?
Fetch the tokens rather than reading hexes off this page. Both files are generated from the same source in the same build, are CORS-open, and carry every value shown above.
curl https://adampang.com/design/tokens.json
curl https://adampang.com/design/tokens.cssthree rules that keep work on-brand
- 01Reference the variable, never the hex. Write
var(--color-fg), not#1a1a1a. Every token is mode-aware, so one reference is correct in both themes. A literal hex is right in one mode and wrong in the other. - 02One accent, one hue per section.
--color-accentowns links, CTAs, and focus. Section hues are decorative fills and dots. Never set body text in one: at ~2:1 on white they fail AA, which is why--color-alert-inkexists separately from--color-alert. - 03Three text weights, no opacity.
--color-fg,--color-muted,--color-faint. Do not dim text with opacity to invent a fourth. That is exactly what failed AA here:text-ink/55measured 3.90:1 against a 4.5:1 requirement.
Identity data lives at /api/profile.json and /llms.txt.