Kelvin Chen ~/design

design system

One ink. One red.
Four rooms.

This page is the design system behind this site and its sibling apps — the code is the spec. Every token, component and motion pattern lives here, and works in all four rooms: try the room switches in the header. When a new component is needed anywhere, it gets designed on this page first.

restraint does the talking: chrome is monochrome, color lives only in photographs, data, and one hand-drawn red.

the spec opens in motion — when a loop exists, it beats the still

01 · rooms

Only the paper changes.

Every surface keeps the same bones and swaps only its light: paper for the personal site, night for the party screens and dark mode, porcelain for the dog, stone for the dashboards. A component that only uses room variables works in all four automatically — that's the test, and this whole page passes it.

--seal #c3272b

rooms are assignments, not prisons: the party app lets guests flip paper ⇄ night and remembers the choice. the one exception is the party TV — a giant screen in a dark room stays night, because its darkness is stagecraft, not preference.

02 · voices

Three voices, never a fourth.

content · Space Grotesk 700/400

Headlines carry the argument.

metadata · IBM Plex Mono

KICKERS · CAPTIONS · DATA · STATUS — the quiet voice that annotates everything

hanzi display · Songti

English leads. The system's entire public hanzi inventory is seven characters — three names and four card seals: 焕 · 暖暖 · 随想 · 造 · 动 · 暖 · 聚. A fixed set, not a pattern: concept names stay English in public copy, and full bilingual text lives only in /notes prose. Body text stays quiet, medium-weight ink for emphasis, links as thin underlines.

scale is the accent color: a 72px headline against 11px mono does what a bright hue would — without the hue. fonts are self-hosted woff2, zero CDN.

03 · the red

One red, four doors.

Vermilion #c3272b — the red-pen ink teachers grade with — is the only color the interface is allowed. It appears through exactly four doors, always as a hand-drawn stroke, never as a geometric block. Everything else is ink, paper, gray.

door 1

The marks

One gesture only: the brush swash under the name. Signature, not logo.

door 2

Alive signals

Live pulses, form errors, destructive confirms, the newest data point, first place. Things that need attention right now.

running · morning brief
door 3

Selection

Select any text on this page — the red belongs to whatever is being read.

go ahead, drag across this line.

door 4

Her collar

Yumi is the one member of the house the design system doesn't apply to.

the collar

two content exemptions, because color belongs to content: photographs keep theirs (hushed — see 05), and data keeps its semantics (gains in muted sage #4e8a6e, losses in vermilion — numbers only, never chrome).

04 · marks

Signature, not logo.

No invented pictorial logo, ever. The identity is typographic and photographic: the name with a red brush-stroke under it, a red dot, or simply a real photo — the avatar is the icon.

the swash — footer signature,
og corner, headline keywords
the dot means alive — a signal,
never a signature (door 2 owns it)
Yumi, whisper-graded, blooming on hover
the photo — avatars, QR centers.
a face beats any icon
← at rest: ~14px, 40% opacity
the quiet rule — marks whisper.
never let one grow back into a logo

retired, on purpose: a square seal stamp (too official) · a neat circle around the name (too formal) · a casual double-loop scribble (tried it, didn't love it) · any cartoon or geometric logo (someone else's pen) · a red dot as the header mark (to the whole internet a red dot says "notification" — and here it already says "live"). The graveyard stays public — rejected ideas are how the system learned what it is.

05 · photography

Color rests at a whisper.

Photos carry all of the site's warmth, so they follow one law: at rest they wear a hushed grade — 60% saturation with a faint warm cast — that pins every picture into the same album. Hover (or scroll past center, on touch) and the color blooms back to full. Two tokens, --photo-rest / --photo-alive, and no component may hand-write its own filter.

Coachella sunset at rest grade
at rest — the whisper
A demo night, hover to bloom
hover me — the bloom
Jumping on an empty road, full color
alive — full color, earned
hover to play — a loop rests as a poster, moves when you ask

motion-first law: when a slot has both a still and a loop, the loop wins — poster and reduced-motion fallbacks always in place.

frames are rounded rectangles (20px outer / 12px inner) — the old organic blobs are retired. video loops: muted, playsinline, ~0.5MB, poster fallback, lazy off-screen. an earlier version rested photos at pure black & white; it fought the warm paper and read cold, so the whisper replaced it — chosen from a five-grade darkroom lineup.

06 · no emoji

Emoji are someone else's pen.

Interface chrome uses zero color emoji: they're cartoons drawn by a platform vendor, they carry their own hues, and they render differently on every device. Three substitutes, one per pen:

Type does it

Most emoji can simply be deleted — the mono voice was already enough.

🎉 THAT'S A WRAPTHAT'S A WRAP

⚠️ error⚠︎ error (text-form dingbat, U+FE0E)

✅ done ×3✓ done ×3

The gray pen draws it

When a picture is genuinely needed — empty states, theme badges, reaction controls — a 2px line doodle in the current ink.

the doodle set grows as needed — shuttlecock, paw, megaphone, cake, crown so far.

The red pen celebrates

Ceremony comes from the system, not a party-popper glyph: first place gets a vermilion rank, warnings get the red hairline, big moments get big type and motion.

  • 1team otter
  • 2noodle

the one exemption: emoji typed by guests and users render as-is — that's their voice, not our chrome.

07 · the stage

Backgrounds can live — behind glass.

A stage is a layer of living media between the paper and the chrome — a video loop, a photo wall, a slow generative drift. Hosts own what plays on stage; the system owns the proscenium: every stage wears the whisper grade, a room-colored veil, and the grain — non-negotiable, so anyone's media automatically belongs.

that's a wrap

Thanks for coming!

1team otter · 420 pts

video stage — live in the party app: the host uploads the loop, the TV wears it. also the post-emoji ceremony: big type and one red rank, no party-popper glyph

photo stage — two frames, 26s crossfade drift

breathing paper — the zero-asset fallback

  • video already wired into the party app: the host uploads a loop in the control room (≤4MB, straight to media storage); the TV wears it on the lobby and farewell only — the moment a question opens, the stage clears. Guest phones never render it.
  • photo Ken Burns crossfade; the party farewell can play the night's own guest photos — zero new assets, maximum personal
  • scroll hero-only: film at the top, the page pulls the curtain as you read; never a whole-page video
  • ownership hosts own the stage, the system owns the frame — the veil, the grade and the grain aren't host-configurable, so anyone's media lands on-brand
  • tempo stages move at ≥26s — they're weather, not actors; micro-interactions stay ≤300ms

08 · motion

The page moves because you do.

Everything you touch answers within 300ms; everything that moves on its own is scroll-driven or slower than 26 seconds. A ~40-line JS engine feeds scroll progress to CSS custom properties, because pure CSS scroll timelines silently fail in half the browsers out there. No JS, or reduced motion? The page is already in its final, fully readable state. Zero dependencies, no WebGL.

magnetic — leans toward the pointer, breathes on hover
drawn underlinelinks draw themselves
pointer tilt ±6°
3D for photos and toys, never text
3,500+ orders
count-up on first view
badminton host
pills float

one stage, five reflexes — bring a pointer and play. everything answers within 300ms.

scroll-reveal

Cards stand up

rotateX(22°) → 0 as they enter; older browsers get a one-shot rise instead of a scrub.

the headline above

Ink fills as you scroll

Scroll up and down — the section title floods from gray to ink, left to right.

the big one

Stacking photo cards

The homepage opener: four real photos stack one over the last, each developing as it arrives. See it live →

card wording reveals together with its photo — text never sits static while the image performs. big scroll stories are always vertical and always carried by photography: a giant-logo zoom and an ink-bottle pour were both built, both rejected.

09 · components

The working parts.

Forms, data, feedback, navigation, party hardware, prose — every interactive part of every surface, in one place. All of it passes the room test; flip the header switches while you play.

Forms

● that email needs a full address — red only shows up while it matters

focus = the border turns ink, no colored halos. tab through — every stop is visible.

Data

holdingweightday
NVDA31.2%▲ 2.4%
TSM18.7%▼ 1.1%

ink line, faint grid — the newest point is red because it's alive (door 2).

Feedback

no photos yet — go enjoy the party, upload later

one toast at a time, ink on paper inverted. dialogs are native; destructive buttons wear the red hairline. empty states teach, they don't say "empty".

Navigation & party

Mono labels, an ink underline that moves — the current page is always the ink one.
solid
stripe
dots
outline

teams are told apart by texture, not color — a black-and-white projector and a color-blind guest see the same game.

Prose

notes · №2 · a specimen paragraph

Body text stays quiet at 15–16px with a tall line. Medium ink carries emphasis, inline code sits on a panel, and the divider refuses to be a full-width line:

···

Quotes get a 2px ink bar and a little more voice. 双语是两种声部,不是翻译。

10 · the voice

Write like a person.

plainone humane sentence beats three clever ones. empty states teach ("no photos yet — go enjoy the party") instead of announcing emptiness.

specificdecorative copy mines real details, never values-speak. "vibe coding past midnight" made the marquee; the six core values did not — true isn't the same as usable.

english leadsheadlines must stand alone in English; hanzi arrive as single-character seals (造 · 动 · 暖 · 聚) or names (暖暖) — footnotes, not the melody.

no thirdsthe same word or joke doesn't appear a third time on one page. one supervisor joke per page is plenty.

anchoredfloating captions must belong to their image — no joke gets parked on an unrelated photo just because it's funny.

dry humorthe mono voice keeps its spec-sheet deadpan: power draw: one espresso · latency: replies < 24h

11 · the rules

Eight rules derive everything.

rule 1chrome is monochrome; content keeps color (photos, data semantics)

rule 2red passes through four doors only, always as a hand-drawn stroke

rule 3three type voices, never a fourth

rule 4separation is a hairline; shadow means lift, never decoration

rule 5radius: 12–20px outside, 6–10px inside, pills fully round

rule 6everything touchable answers ≤300ms; every animation respects reduced-motion

rule 7components use room variables only — pass that, and all four rooms come free

rule 8new components are born on this page first. if it isn't here, it doesn't exist yet — design it here, prove it in four rooms, then ship it

12 · decisions

The system remembers its arguments.

A design system is a pile of decisions, and half of its value is the rejects. Kept public on purpose:

  • 2026-06The warm orange era ends. Cream paper and #f47d31 had run the site since the start — proven, friendly, and increasingly everyone's default.
  • 2026-07-03Five palette rounds. Crimson, magenta, marigold, brass, midnight, oxblood, electric azure — all built, all rejected. The lesson: the answer wasn't a hue, it was restraint.
  • 2026-07-03Ink and the red pen, ratified. Monochrome ink, one hand-drawn red, four rooms. A square seal stamp and two circle marks were tried and retired the same day — the red pen won.
  • 2026-07-03Two openers die. A giant-glyph zoom (logo as protagonist) and an ink-bottle pour (drawn props, diagram energy). The stacking photo cards ship instead — photography carries the big moments.
  • 2026-07-03The whisper replaces black & white. Pure grayscale fought the warm paper; a five-grade darkroom lineup settled it at 60% saturation with a warm cast. Same day: the emoji ban.
  • 2026-07-04The stage layer. Living backgrounds, host-owned content behind a system-owned proscenium. Marks learn to whisper — signature, not logo. Party rooms become switchable; the TV alone stays night.
  • 2026-07-05This page goes live. The lab becomes public — the spec, the components, and the graveyard, all in view-source.
  • 2026-07-05The hanzi diet, and motion first. The three-pens metaphor retires to the internal docs; the public hanzi inventory is fixed at seven characters — three names, four seals. And when a slot has both a still and a loop, the loop wins: video is a first-class element now.
  • 2026-07-05The dot retires as a mark. One shape, one meaning: a red dot already says "live" here — and to everyone else it says "notification". The swash is the only signature now; even the favicon is a brush stroke.
  • 2026-07-06Filter, never on the video. Three loops on this page (and three more on /nuannuan) sat frozen — WebKit doesn't repaint a `<video>` that carries a CSS filter directly, even while it's technically still playing. Fix: filter the wrapper, never the tag. Now a checklist line, so it doesn't happen a second time.
  • 2026-07-06Then a second, quieter bug in the same spot. This section's own hero loop still wouldn't play — in Chrome only, Safari was fine. The tell: the stage demo below it (scroll-revealed) played, the hero (a fixed opacity-0-to-1 entrance) didn't. Chrome appears to decide autoplay eligibility the instant a video attaches — one that's still invisible mid-fade seems to get skipped for good. Fix: autoplay video never gets an opacity-fade entrance again, plus a couple of delayed retries as a backstop.
saved

Remove this guest?

Unassign keeps their points with the team; kick deletes them. House rules.