oll.in · design system · build-in-public

One product, three skins

The whole oll.in prototype now carries a live Style: Bold · Clean · Warm switch (× light/dark) in the top bar. Same content, same layout, same behaviour — only the look changes, and it follows you as you click through the flow. So the visual direction can be decided on the real product, not just the landing.

3 styles × 2 themes = 6 looks token-driven content frozen v1 · mockup

01The decision it unlocks

The landing was shipped in the Bold direction, with Clean and Warm explored and set aside (see the design study). But a landing is one page — a product is a whole surface: dense dashboards, cards, forms, tables, a radar. A style that sings on a hero can feel heavy across an app, or a quiet style can read as more trustworthy where real money and real applications are on the line.

You can't judge a product's skin from its landing.

So the three directions are now wired as a live switch on every product screen. Pick a direction once; it persists (via localStorage) as you move Dashboard → Applications → Get found → Radar → Settings. Judge each on the content that actually ships, then decide.

02The three directions

Bold Swiss-red · the current ship
Clean Linear / Vercel register
Warm editorial · candlelit dark
TraitBoldCleanWarm
TypeInter + Fraunces displayInter grotesk onlyFraunces serif + Nunito Sans
BordersThick 2–3px inkHairline 1pxSoft 1.5px
ShadowsHard offset "sticker"Soft low-opacityGentle, warm
RadiusLow (tight)MediumHigh (rounded)
CasingUPPERCASE labelsSentence caseSentence case
FeelConfident, loud, SwissPremium, calm, neutralHuman, editorial, warm
DarkNear-black + vivid redPremium near-black, cool greysCandlelit walnut + amber

03How it works — token-driven, not three codebases

There is one set of markup per screen. Two attributes on <html>data-style (bold|clean|warm) and data-theme (light|dark) — select a block of CSS custom properties. Every component consumes those tokens, so flipping an attribute re-skins the entire screen with zero content change.

The token layers

Colour--red · --ink · --paper · --paper-2 · --line · --muted · --green … set per style and per theme (6 blocks total).

Geometry--bw/--bw2 (border widths), --bc (border colour), --r-xs…--r-lg/--r-pill (radius scale).

Type--font-body, --font-display, --body-lh, --body-ls, --tt (label casing).

Depth--shadow-xs…--shadow-lg, --sh-btn, --sh-accent (hard offset in Bold, soft in Clean/Warm).

A tiny inline script in <head> reads the saved choice before paint (no flash of the wrong style); the switcher writes it back to localStorage under ollin-style/ollin-theme, shared across every screen. A handful of per-style overrides handle the genuinely different treatments (weight, casing, Warm's serif headings) beyond colour and shape.

Why this shape

One source of truth, N looks. No forked templates to drift apart. When the direction is chosen, the losing token blocks + the switcher are deleted and the winner becomes the single house skin — a ~10-minute cleanup, not a rebuild.

04What shipped this pass

Every primary screen was refactored from hard-coded Bold values to the shared token system and given the switcher. Content, copy, CHF prices, the market-neutral persona (Lena Weber · Regional Hospital) and the honesty guarantees are untouched — styling only.

ScreenSwitcherNotes
Ollie's dashboard — the homeliveFlagship. Includes the new "How Ollie works" explainer + "Ollie's on it" service reel, both tokenised.
Applications trackerlivePipeline rows, filter tabs, stage rails.
Get foundlivePost drafts, open-to-work toggle, weekly strip.
Career RadarliveRadar viz, salary benchmark, watch sources.
SettingsliveProfile, plan cards, integrations, automation guarantees.
Role · Apply · OnboardingliveDrill-downs + pre-login, so the style never drops mid-flow.
Style lab (dashboard-styles.html)liveThe isolated playground where the engine was proven (nav held inert).

Verified across the six combinations with live screenshots: same content re-skins coherently, contrast legible in every light/dark pairing, 0 console errors, cross-screen persistence working, mobile collapses.

05Recommendation — decide, then collapse

A proposal, not a verdict — the switch exists precisely so the call is made on the real thing:

Lead with Clean for the app, keep Bold for the landing

The landing's job is to grab — Bold earns its loudness there. The product's job is to be trusted with a job hunt: dense screens, real applications, real CHF. In that context Clean reads as the most premium and the least fatiguing across a long session, and its hairline system scales to tables and forms without shouting. Warm is the strongest differentiator if the brand wants to feel human over corporate. Clean is set as the product default so that's the first impression; flip through all three on the dashboard and settings before locking it.

Once chosen: keep the winning token blocks, delete the other two + the switcher, and the whole prototype settles into one coherent house skin. Nothing else has to change — the markup already speaks tokens.

See it live: the switcher rides on the oll.in prototype — the dashboard and the isolated style lab (in the oll.in repo; use the Style control in the top bar) · and the earlier landing design study (Clean · Warm · Bold, and why Bold won the landing).