Design system · v0.1
STYLE GUIDE
Every screen in Coin Cartel is built from the components below. Each section is live — the demos are the real components with real state, so what you see here is what ships. Where a component has a rule that is not obvious from the API, that rule is written underneath it.
Use the motion switch in the header to compare the animated and reduced states. Everything respects prefers-reduced-motion by default.
Colour, surfaces & texture
Three surface tiers, five signal ramps, and a set of pattern treatments for the game's risk motif. Everything below resolves to these tokens — no ad-hoc hex values in components.
Base · Noir
Page, section and overlay surfaces. Darkest values sit at the back.
Primary Brand · Acid
Cash, product, growth, and anything trending up.
Prestige · Gold
The wordmark, banked money, rank, and the ready state.
Legitimate · Ice
Bank, energy, information, and anything the state can see.
Danger · Heat
Raids, damage, busts, and the bounty on your head.
Cost · Rust
Dirty money and anything that spends more than it returns.
Text
Primary white on noir surfaces; slate-400 is the workhorse secondary.
tailwind.config.ts → theme.extendSix ramp families cover the entire product. Adding a colour outside these ramps needs a design review — that's the point of the constraint.Surface tiers
Base
Page
bg-noir-950Raised
Sidebar, table
bg-noir-900Card
Default panel
bg-noir-700Radii
rounded-md6px · Inline chips, tiny tags
rounded-lg10px · Buttons, inputs
rounded-xl14px · Nav rows, list items
rounded-2xl18px · Cards, panels
rounded-full9999px · Pills, dots, avatars
Elevation & glow
shadow-panelDefault resting elevation on a card
shadow-panel-lgModals, popovers, the top bar
shadow-glow-acidPrimary action, cash, growth
shadow-glow-goldBanked money, rank, ready state
shadow-glow-iceBank channel, energy, information
shadow-glow-heatDanger: raids, damage, busts
shadow-glow-rustDirty money and expensive actions
shadow-*Glows are not decoration. They mark the one thing on screen that wants a click, or the one thing that has just become ready.Texture
The pattern layer. Grid and haze carry the noir; hazard tape is the raid motif and is the only one allowed to sit on a card that has content.
bg-cartel-gridBlueprint grid. Page-level, under content.
bg-cartel-hazeTwo corner radials. The ambient brand wash.
bg-cartel-scanlinesCRT scanlines. One per screen, ≤6% opacity.
bg-cartel-hazardGold hazard tape. The raid / bust motif.
bg-cartel-hazard-heatRed variant, for an active bust.
.hazard-edgeHazard tape is a 4px mask on the top edge, never a fill. A filled panel would drown the payout and risk numbers that the player came to read.Type scale
One display face for the wordmark, one for headings, and a mono face reserved for anything that counts. Numerals are always tabular so timers don't jitter as digits change.
wordmark36px / 1.0COIN CARTEL
Logo only. Anton is a display face and has no lowercase — never for UI copy.
h130px / 1.1Weed Farm
Screen titles. Uppercase, one per screen.
h220px / 1.2Grow Plots
Section headers, card group titles.
h316px / 1.4Dirt Weed — Plot 2
Card titles, list item names. Sentence case is fine here.
subtitle14px / 1.5Runner · Level 5 · The Ridgeline
Supporting line beneath a header.
body14px / 1.65Six grow plots behind a cedar fence. The whole thing smells, and everyone knows it.
Prose, job descriptions, flavour text.
label11px / 1.4Raid risk
All-caps eyebrow above values and bars.
numeric18px / 1.204:12
Timers and counters. Always tabular so digits don't jitter.
numeric-lg24px / 1.1$1,250
Hero currency counters and the ledger.
font-mono + tabularAny value that changes over time must use the mono face with tabular figures. Proportional digits shift the layout on every tick, which reads as jitter at 10Hz.font-wordmarkAnton ships without a lowercase and collapses at small sizes. It is loaded for the logo only, and never appears in UI copy.Tier pills, status & risk
Tier says which side of the law a job is on. Status says what a place is doing right now. Risk says what it can cost you. Three axes, three components, never shared.
Tier
Corporate — Launder it, file it, buy the block. Slow, quiet, and it compounds.
Underground — Keep it raw and spend it fast. Every payout raises your heat.
TierPill · TierToggleThe toggle commits the player to a path. The pill only labels one. Never use a pill where a choice is being made — a player who can't tell them apart will tap the wrong thing.Status badges
- Clean
cleanNo active heat here. Standard risk, standard take. - Running
runningSomething is live in this building right now. - Exposed
exposedUnits on the door. Raid chance roughly doubled while it holds. - Guarded
guardedArmed detail inside. Entry costs more and alarms sooner. - Hot
hotActively being raided. Nothing can be claimed until it burns out. - Locked
lockedNeeds a level or a contact you have not earned yet.
StatusBadge status=… size=… glow?glow is meant for exactly one badge in view at a time — the location the player is standing in. Glowing everything is the same as glowing nothing.Risk — the raid ring
A shape, not a bar. Every payout in the game is paired with the probability of losing it, and it has to read as a different kind of number from health, energy and heat.
riskBand(pct)The bands are fixed, not relative to the job on screen. A 20% raid is “moderate” whether or not a 40% one is on the same screen — a scale that shifts under the player is a scale they can't learn.liveThe rotating sweep only runs when the job is actually in flight. Four static rings spinning at once is noise, and the sweep would pull the eye away from the countdown next to it.Composed — how they read together
Tier says the job is underground. Status says units are on the dock. The ring says the run gets rolled a third of the time. All three are readable before the player spends a point of energy.
ComposedTier on the left, the job in the middle, status and risk on the right, actions beneath. Keep this reading order consistent across every panel.Band reference
lowUnder 10%. The default state of most street work.moderate10–24%. Noticeable enough that a bad night costs a plot.highAbove 25%. Only worth it when the payout is disproportionate.
Currencies, vitals & heat
The top bar. Three balances, two meters, one bar that is bad when it is full, and the player identity pill that anchors the layout.
Currencies
cashSpendable now. Earned by jobs. Loses value to nothing — it just leaves.bankProtected and passive. The only balance that earns interest and lowers heat.dirtyUntraceable. Worth less than cash, raises heat, and must be laundered to be spent on anything real.
CurrencyPill currency=… size=… animated?Three balances and never a fourth. animated eases the number to its new value so a payout reads as a tick-up. Keep it on the header counters only — animating a hundred ledger rows is noise.Vitals, heat & combat
A loss in a hostile job costs the full difference.
100% remainingSpent on every job. Restores over time and at the Hospital.
85% remainingVitalityBar · HeatMeter · StatChipHealth, energy and heat are capped meters. Attack, defense and power climb forever, so they get a chip instead of a bar — a bar implies a ceiling the player can't see. Heat is deliberately the inverse of every other meter: full is bad, and it is the only bar that drains on its own.Player profile
Full variant, then the compact one. The compact variant drops the subtitle and the trailing balance — it survives down to a 380px viewport where the full pill would wrap.
ProfilePill size=…The level bar is the same ProgressBar as the vitals at xs height. One component, two jobs — if level progress ever needs a different visual, it should be a different component.Countdowns & cycles
The core verb of the game. Use the transport controls to drive one to completion and watch the state transition to Ready to claim.
Cycle cards
Skunk — Grow
Presses itself out on a timer. Claim before the neighbours notice.
Laundering Cycle
Turns dirty into bank. Completes on its own; there is no skill in waiting.
Getaway Driver
Holds the car at the safehouse. Skip it and the heat follows you home.
Custody Hold
Your person is in holding. Nothing escapes until this one clears.
CountdownCard durationSec=…Use Skip to ready to jump straight to the completed state instead of waiting out a fifteen-minute cycle. The bar fills toward ready — it never refills from zero, which would read as a glitch. On completion the card takes the gold glow, which is the only gold on the screen until you claim it.Cycle row — the same hook, no card
Energy regen
01:45
useCountdown({ durationMs })The hook is the reusable part. It ticks off an absolute end timestamp, so it stays accurate through tab throttling, and it exposes percent for the bar plus isComplete for the transition.Job cards & the risk ring
The workhorse. Every job shows the same four things in the same order — what it is, what it pays, what it can cost you, and the one button that starts it.
Grow plots
Buy a seed, start the cycle, come back when the bar says ready. The risk ring is the whole decision — it is the price of leaving the plot running long enough to be worth harvesting.
Dirt Weed
Plot 1 · The Ridgeline
Dirt Weed
Plot 2 · The Ridgeline
Empty plot
Plot 3 · The Ridgeline
JobCard tier=… value=… riskPct=…An unplanted plot keeps the card mounted and disabled rather than collapsing it, so the grid never reflows when a harvest lands. The action label is the state change — “Harvest" vs “Plant a seed” — not a generic “Go”.Strains — the trade
Skunk
Pungent and profitable. A solid mid-tier strain.
- Seed
- $500
- Grow
- 5m
- Yield
- $900
- Multiple
- 1.8×
- Raid
- 12%
LV 1Northern Lights
Premium strain. High yield, but the smell attracts attention.
- Seed
- $1,500
- Grow
- 10m
- Yield
- $3,000
- Multiple
- 2.0×
- Raid
- 20%
LV 3OG Kush
Top-shelf genetics. Massive yield, serious raid risk.
- Seed
- $4,000
- Grow
- 15m
- Yield
- $9,000
- Multiple
- 2.3×
- Raid
- 30%
LV 5
STRAINS in lib/data.tsThe multiple (yield ÷ seed) is the number players actually optimise, so it gets its own column. A strain locked behind a level shows the level rather than a disabled button — the tease is the feature.A job in flight
Harbor — import run
Containers in at low tide
Import run
Runs on its own. Claim before the manifest clears.
The card on the left is the same job rendered as a running JobCard with a timer prop, fed by the same useCountdown the card on the right owns.
timer={{ percent, remaining }}Passing a timer switches the card to running: the ring gets its live sweep, the payout block is replaced by a progress bar, and the action is suppressed. One card, one state — not two components.Composed — job list
Weed Farm
The Ridgeline · Running
Northern Lights
Plot 2 · 10m cycle
- Energy
- 12
- XP
- 45
- Grow
- 10m
OG Kush
Plot 1 · top shelf
- Energy
- 15
- XP
- 80
- Grow
- 15m
Fast-travel rows
Right-sidebar rows. Select one to see the selected treatment; the active row is the only place a status badge gets its glow.
Weed Farm
The Ridgeline · Product
Six grow plots behind a cedar fence. The whole thing smells, and everyone knows it.
- Travel
- 18m
- Energy
- −8
- Payout
- $900 – $9k
- Status
- Running
Fast travel
LocationList locations=… activeId=… onSelect?The whole row is the hit target, and the active row is the only one that gets the acid glow — so “where am I” is answerable without reading.By district
The Exchange
The Ridgeline
The Flats
Old Town
LOCATIONS in lib/data.tsDistricts are a view concern, not a data field — they are derived at render time so adding a place never means editing a second list. Order the map, not the component.Variants
Without travel time
showTravel={false} — for lists where travel time would be noise.
Status ladder
Severity increases left to right; hue is never the only signal.
StatusBadgeThe status ramp runs acid → rust → gold → heat with slate for locked. Gold sits between exposed and hot on purpose: a guarded building is a cost, not a threat.Notification suite
Six variants on react-hot-toast, one per outcome. Each carries its own channel label, icon and glow so the player can read the result from the corner of their eye.
Fire one
pushToast(variant, payload, options?)Every message routes through one function, so a new notification type is a variant definition plus a named wrapper — never a bespoke toast() call at the call site. The wrappers carry the game's voice; the components carry the chrome.Static reference
Every variant, rendered without dismissing. Use this to compare the borders and glows side by side rather than chasing live toasts.
Cash · Clean
Payout cleared
Counted in the back of the lot. The account is legitimate again.
cash-in · Cash · Clean
Dirty · Underground
Sold on the street
Cash in hand. Untraceable, and worth less than it looks.
dirty-in · Dirty · Underground
Banked · Protected
Laundering cycle complete
The balance is clean. The paperwork is not, but nobody reads it.
bank-in · Banked · Protected
Raid
Raid on the Ridgeline farm
Two plots seized. Heat is up and your contact is not answering.
raid · Raid
Cycle complete
Northern Lights, Plot 1 is ready
Collect it now or it starts losing value while you wait.
ready · Cycle complete
Coin Cartel
The heat cools
Laying low drops your wanted level over the next few cycles.
info · Coin Cartel
Channel labelThe small-caps eyebrow says the channel before the title says the event: Cash · Clean, Dirty · Underground, Banked · Protected. The player learns which column a payout lands in by its colour and its label together.meta chipThe mono chip on the right is the only place a delta appears, and it is always signed. A player glancing at the corner can see whether they gained or lost without reading a word.Voice rules
One outcome per toast
Never stack a win and a loss into one message. The player has to be able to answer “did that cost me anything?” without reading twice.
Lead with the outcome
“Raid on the Ridgeline farm”, not “A raid occurred”. The title is the news; the description is the colour.
No exclamation marks
A game's toast is a terminal line, not a marketing banner. The rarity is the styling, not the punctuation.
Failures always cost something visible
Every negative toast carries a signed meta chip. “Something went wrong” is not a message — “−$11,400” is.
Ready beats done
A completion toast says what is ready and what happens if it is left. Waiting is itself a decision with a cost.
lib/toasts.tsxThe named wrappers live here rather than inside the component so a non-React module can trigger a notification without importing a component. Rules belong with the copy they govern.Buttons & inputs
Buttons are named for what they do to the world. Destructive and dirty-money actions get their own treatment so a misclick is hard.
Variants
Button variant=…primary for the one thing you want done. dirty, bank and danger are the only ones that get a glow — they are the only ones that move money irreversibly.Sizes
Button size=…Minimum 28px hit target on desktop, 36px minimum anywhere tappable. The xs size is for dense rows and demo controls only. A disabled button carries the reason in its label — a greyed-out button with no explanation is a support ticket.Inputs
Burning a plot is the only action with no undo in the game, so it takes two clicks and the second one names the target out loud.
Armed stateAnything irreversible gets a two-step confirm, and the destructive button only exists after intent. A stray click can never cost a player a whole season of growing.The dashboard, assembled
Every block above, in the arrangement the real dashboard uses. This is the reference for assembling the game screens.
The reference arrangement. Every screen in the game drops a subset of these blocks.
← Back to the landing pageVitals
Jobs
The Ridgeline · three plots online
Skunk
Plot 1 · 5m cycle
- Energy
- 8
- XP
- 20
Northern Lights
Plot 2 · 10m cycle
- Energy
- 12
- XP
- 45
Timers
Two cycles running
Northern Lights — Grow
Premium strain. High yield, but the smell attracts attention.
Laundering Cycle
Turns dirty into bank. Completes on its own; there is no skill in waiting.
Fast travel
Ledger
This session
- Harbor import run+$11,400
- Skunk harvest+$900
- Northern Lights seed−$1,500
- Raid on Plot 3−$200
Wanted
Live jobs, wanted rivals and the standings all read from the same risk bands as the cards.
Composed shellThree columns, always in this order: navigation and the player's own state on the left, actions in the middle, the world and the ledger on the right. Money moves right to left — the ledger on the right is the record of what the buttons in the middle just did.