THE GUIDE
How this site was designed and built — the concept, the palette, the steam, the neon, and a recipe you can follow yourself.
1Concept & art direction
MIDNIGHT TONKOTSU is a fictional nine-stool ramen counter open 23:00–05:00, for night people. The page has two jobs: show the menu and help you find a door that has no sign — only a red lantern. The art direction is rain-slick asphalt with neon magenta and electric cyan glows, lantern red for the door, broth gold for the food. Every visual is drawn in code: no photographs, no stock, no generated images.
2Palette & type
#07080Dasphalt#FF3DAEneon magenta#28E0FFelectric cyan#FF2B2Blantern red#F0B64Abroth gold#E9E6DCsteam whiteContrast discipline matters more than the neon: the base is near-black, body text is a warm off-white, and each neon color has exactly one meaning — magenta is signage, cyan is wayfinding and machine labels, red belongs only to the lantern (open/closed state), gold only to food and prices.
- BIG SHOULDERS DISPLAY — a tall condensed grotesque with signage bones; it reads like letters cut for a lightbox.
- DotGothic16 — ドットゴシック — a bitmap-style Japanese face; it carries the katakana accents and the ticket-machine LCD flavor.
- IBM Plex Mono — body and labels; monospace keeps prices, codes and steps feeling like printed stubs.
3Techniques
The steam simulation (signature)
The steam over the bowl is a canvas particle system. Each wisp is a
pre-rendered soft radial-gradient sprite composited with lighter
blending. Particles spawn on an ellipse matching the broth surface, rise with
buoyancy, and carry their own sway phase so plumes braid instead of rising in
parallel lines. The pointer applies a radial push plus a share of its own
velocity, so you can stir the steam:
// pointer disturbance: radial push + inherited velocity
const dx = p.x - pointer.x, dy = p.y - pointer.y;
const d2 = dx*dx + dy*dy, R = 120;
if (d2 < R*R) {
const d = Math.sqrt(d2) || 1;
const f = (1 - d/R) * 0.9;
p.vx += (dx/d) * f + pointer.vx * 0.05 * f;
p.vy += (dy/d) * f * 0.5 + pointer.vy * 0.05 * f;
}
The loop is pre-warmed 180 steps so a full plume exists on first paint,
pauses when scrolled offscreen, and under
prefers-reduced-motion it draws one hand-placed static plume
instead of animating.
The neon lighting model
Neon is not one glow — it's a stack: white-hot tube core, tight colored
halo, wide bloom, then faint cast light. The flicker imitates a failing
inverter: uneven timing, partial dips, a double-stutter — driven by
steps(1, end) so it snaps rather than fades:
.neon {
color: #fff0f7;
text-shadow:
0 0 4px #fff, /* tube core */
0 0 11px var(--magenta), /* halo */
0 0 24px var(--magenta), /* bloom */
0 0 55px rgba(255,61,174,.65), /* wide bloom */
0 0 110px rgba(255,61,174,.4); /* cast light */
}
@keyframes inverter-stutter {
7% { opacity: .35; text-shadow: 0 0 3px #fff; }
7.6% { opacity: 1; }
54% { opacity: .25; }
54.3% { opacity: .9; }
54.7% { opacity: .45; } /* the double-stutter */
55.2% { opacity: 1; }
}
The ticket machine
The menu is laid out as a real ticket machine: rows encode kitchen
sequence (bowls → sides → drinks), buttons are actual
<button> elements with aria-pressed state, and
pressing one "prints" a paper stub into the tray — an off-white element whose
perforated edge is cut with a CSS mask of repeating radial
gradients. The stub is the one warm-paper surface on an otherwise wet-asphalt
page, which is why it reads as an object.
4The three passes
- Pass 1 — correctness & composition. The steam was a
blown-out white blob: halved sprite opacity, shrank particles, raised the
emitter off the broth and sped the rise so it forms a plume. Also fixed a real
bug — the paper stub's
display: flexwas overriding itshiddenattribute, so it printed itself before anyone pressed a button. - Pass 2 — elevate. Added a second sway harmonic and a slow ambient wind so the whole plume leans and twists; added the neon hover-buzz micro-interaction (get close and the tube stutters); redrew the bowl toppings — the two eggs plus naruto were reading as a cartoon face, a chashu slice broke it up.
- Pass 3 — taste. Chanel rule: removed the second (cyan) hero glow that competed with the steam's own cool tones; fixed the 390px topbar where the brand wrapped to two lines; rechecked keyboard focus rings and the reduced-motion static plume.
5Do this yourself
- Pick a place, not a topic — somewhere with hours, rules and a smell. Write its one-line job: "menu + find the door."
- Ask Claude for an art direction with a materials list (asphalt, neon, paper, steam), and assign each accent color exactly one meaning before any layout exists.
- Choose three typefaces that answer three different questions: signage, atmosphere, information. Reject any pairing you'd reuse on another project.
- Build the signature element first, alone in a blank page — here, the canvas steam — and iterate until it feels physical. Everything else stays quiet around it.
- Make structure carry meaning: menu rows in kitchen firing order, rule numbers that are real rules, steps that actually navigate somewhere.
- Write the copy like a person who works there. No lorem ipsum, no "delicious food, cozy atmosphere."
- Screenshot, critique, fix — three times, at 1440px and 390px. Each pass must remove something as well as add something.
- Last: reduced motion, keyboard focus, console clean. The invisible pass is the professional one.