One board UI.
Every game.
The keys, cards, overlays, bursts and layouts we dialled in on DOM Battlefield, made portable. A new board reuses all of it exactly. The only thing a game changes is its palette.
Ten rules. No exceptions.
Everything else on this page follows from these. If a board needs something new, it goes in the spec first.
- 01
3D keys
Everything you can press is a face over a hard base straight below it. Never a side shadow on the board.
- 02
One travel
Face and base move together on one curve: 120ms ease-out, 50ms on press. Nothing snaps.
- 03
Colour is the job
One colour per job, the same on desktop and phone, dark and light.
- 04
Locked reads down
On rests pressed in the lock colour and breathes. Tap again to release.
- 05
Call reads up
The next key to press stays up and sends a ring from the bottom of its base.
- 06
The ink rim
Keys, stickers and cards share one ink rim in both themes: neon caps on ink.
- 07
Short, loud words
Mono caps. FIRST WORD bold · detail. Explanations go to the comms toast.
- 08
Overlay ritual
Grey dim, a sticker that slams and presses in, two keys at the map’s bottom.
- 09
The field first
Cards move away from your team, shrink to a key and never cover each other.
- 10
Embed-safe, small
One root class, one AbortSignal, reduced motion respected, a budget in CI.
The only thing a game changes.
One colour per job. Pass any subset as hex; the rest keep their defaults, and every base, glow and lock colour is derived. Try the switcher above: every demo on this page repaints.
- sys
--k-sysNavigation, camera, pager, tips, focus - cmd
--k-cmdCommand, planning, every locked order - fire
--k-fireThe main action - aim
--k-aimAim modes, dim when off - attack
--k-attackOffensive order - advance
--k-advancePush to objective - fallin
--k-fallinForm up on the player - flag
--k-flagWhite flag: pause, surrender - go
--k-goStart and confirm - teamA
--team-aSide A (retreat = your team) - teamB
--team-bSide B
import { applyPalette } from "board-ui";
applyPalette(boardRoot, { sys: "#06c4ff", cmd: "#8573ee", teamA: "#4bce82", teamB: "#f4457a" });
// or in an embed config: palette: { cmd: "#8573ee" } (hex only; anything else is ignored)A face over its own base.
Families only set variables (--c, --py, --lock); the state rules do all the motion. Press them: they are the real thing.
| Variable | Base | Arcade | Sticker | Meaning |
|---|---|---|---|---|
--py | 3u | 4u | 5u | base height |
--hy | 1u | 2u | 2u | hover lift |
--edge | --line | --c 45% into rim | #f6f3ff | base colour |
--lock | role or --k-cmd | colour when locked |
One family, many jobs.
Orders lock in the command colour. Aim keys rest dim and light up when on. The shortcut always sits on the right edge.
FIRST WORD bold · detail.
Mono caps, spaced, muted, with the first word in ink. Each layout keeps the same grammar at its own length.
| Group | Desktop bar | Compact desktop | Full-screen card | Phone |
|---|---|---|---|---|
| per unit | RADIO · EACH TANK | RADIO | title RADIO · EACH TANK | rail key |
| squad | RADIO · WHOLE SQUAD | WHOLE SQUAD | WHOLE SQUAD | ORDERS · WHOLE SQUAD |
| roster | PLATOON · RADIOS | PLATOON | PLATOON | PLATOON |
| aim | AIM · AUTO / LOCK | AIM | title AIM | rail keys |
Float, fold, step back.
Cards float over the field on the side away from your team. The grip drags them, the icon key shrinks them, the back tab goes one step back. A unit card's status pill sits on the top edge opposite the back tab, flat, breathing with the selected key's glow, in radio talk. Two cards never cover each other (10px buffer).
TANK 2 · MEDIUM
Pick an order beside the tank: Attack HQ, Follow, or Command.
Dim. Slam. Two keys.
Grey dim that blocks the board, a sticker that slams down and presses in a little above centre, at most two keys centred at the map’s bottom. The explanation goes to the toast. Try Surrender: the white flag whites out, waves, and blows up.
| Overlay | Kicker / word | Keys |
|---|---|---|
| Pause | BATTLE PAUSED / GIVE UP? | KEEP FIGHTING · SURRENDER |
| Lost | INSERT COIN / GAME OVER | REMATCH · NEW CAMPAIGN |
| Won | STAGE CLEAR / MISSION COMPLETE | REMATCH · NEW CAMPAIGN |
| Stale page | SOMETHING WENT WRONG / REFRESH | REFRESH BROWSER |
Press, invert, shatter.
Every key that starts or ends a game presses into its base, flips to its inverse, then breaks into spinning chunks with a flash, a shock ring and falling square debris. The action runs in the click; the burst lives on the page body.
- 0–90ms an inert copy presses into its base and inverts (light for dark, same hues)
- 90ms six chunks fly out, spin and fall
- 90–410ms flash, core in the key’s ink, round shock ring
- 90–730ms twelve square bits radiate and drop
- 760ms the layer is gone; the key stays hidden if the action removed it
Say it once, where it reads.
One comms line carries every explanation. It never sits under the header or slides under a card. Button tips dim the page and ring each group's keys.
Tank 2: tap a square to send it, or Attack HQ.
Four shapes, one set of keys.
The same keys rearrange for each screen. The thresholds are measured, not guessed: the roster needs 52u per unit and never wraps.
Snappy keys, theatrical moments.
Keys never spring. Springs are for pop-ins only. Reduced motion keeps every end state and drops the motion.
| Motion | Timing | Notes |
|---|---|---|
| Key travel | 120ms ease-out · press 50ms | transform + box-shadow together |
| Locked breath | 1.8s ease-in-out ∞ | glow ring in the lock colour |
| Call ring | 1.4s ease-out ∞ | from the bottom of the base |
| Sticker slam | 1100ms | drop · hit 36% · settle · hold · press in |
| Launch burst | 760ms | press + invert 90ms · 6 chunks · flash · ring · 12 bits |
| Surrender | 16 × 18ms + wave 120ms × 4 | white-out, then the burst at 480ms |
| Boot doors | 620ms after 120ms | rotateY ±100°; the boot runs from t0 = 0.65s |
| Pop-ins | 380ms spring | cubic-bezier(.34,1.7,.5,1), never on key travel |
| Sound cue | Character |
|---|---|
start | two rising notes |
fire | short zap |
hit | square thud |
hurt | saw drop (surrender press) |
won | three notes up |
gameover | the end cue |
A new game in an hour.
Swap the game logic and the board; keep everything above. The spec has the full checklist, every exact value and the lessons we already paid for.
On someone else's site, mount the board in a shadow root: mount(host, css, { palette }). This box sits under a page rule that turns every button red with a dashed border. The board inside doesn't see it.
- Wrap the board in
<div class="bui" data-theme data-side>and include the kit. - Pass the palette; check every face reads with ink text on it.
- Map each action to a role colour; write labels as WORD · detail.
- Bar groups: per-unit radio · squad orders · roster · aim / mode · flag far right.
- Overlays: pause, result, stale page, with the sticker copy above.
- Launch keys use
launch(); a reset never cancels the burst. - Cards: grip, fold key, back tab, the placement rules.
- Comms toast placement per stage; tips mode.
- Boot screen and door hand-off if it ships as a full-game cartridge.
- Test 844×390, 1100, 1400, 1750, 1920 and full screen, both themes, both sides, 4 and 12 units.
- Reduced motion, keyboard and screen reader names; a size budget in CI.