STYLE GUIDE

Sudoku Fighting — visual design reference & component inventory

Colors

Brand Accent — Purple

Primary action color. Used on primary buttons, shadows on white text, character card shadows, toggle on-state, KO block fill, overlay text. Never used as background behind white text unless a contrasting shadow offsets it.

accent-light#EDE0FFHover bg on secondary btn
accent#8B49FFPrimary color
accent-hover#7235E0Btn hover state

Player 1 — Cyan

All P1 UI: grid border, selected cells, name shadow, HUD score/combo shadow. Never used on P2 elements.

p1-bg-light#E5F7FDCell highlight, selected bg
p1-bg-mid#B8ECFBSame-number cells
p1-color#17B9EFGrid border, cursor outline
p1-mid-dark#0A8FBBText shadow on white
p1-dark#006B8FCorrect cell text

Player 2 — Magenta

Mirrors P1 semantics exactly, shifted to magenta. Grid border, selected cells, name shadow. Never mixed with P1 colors on the same element.

p2-bg-light#FFEBF8Cell highlight, selected bg
p2-bg-mid#FFD0EFSame-number cells
p2-color#F31299Grid border, cursor outline
p2-mid-dark#BC0074Text shadow on white
p2-dark#880055Correct cell text

Danger — Red

Error states, conflicts, and low-HP. The track of the health bar is always the danger red, revealed as health drops. Also used for the timer at <10s, wrong-move cell backgrounds, and the Leave/Surrender button.

danger-bg-light#FFEBECDanger cell bg
danger-bg-mid#FFCED1(reserved)
danger-color#F00013HP track, timer urgent
danger-dark#A8000FDanger cell text, Leave btn hover

Action Colors — Orange & Yellow

Orange is the VS AI / single-player signal color. It also appears on the HP bar at ~50% health, and in the heavy-hit screen flash. Yellow is the HP bar fill at full health, and the overlay subtitle text (always on dark or colored backgrounds).

orange#FF8B16VS AI btn, mid HP bar, heavy flash
yellow#FFCA00Full HP bar, overlay sub-text

Greyscale & Neutrals

Used for backgrounds, surfaces, borders, and body text. All neutral values have equal R/G/B channels — no color cast.

bg#F2F2F2Page background
surface#FFFFFFCards, panels
border#E5E5E5General borders
grid-border#D5D5D5Thin sudoku cell lines
grid-box-border#888888Thick 3×3 box lines
text-tertiary#555555Muted / toggle-off
text-secondary#333333Given cell numbers
text-primary#111111Main text

Typography

Typefaces

Body & UI — Roboto Mono

Aa Bb Cc 0–9

Weights used: 400 (normal), 500 (medium), 700 (bold), 800 (heavy), 900 (black).
Used for all body text, buttons, HUD labels, number input, settings, lobby UI.

Display — Barlow Condensed

FIGHT!

Weight 900 only. Reserved exclusively for impactful game moments: FIGHT!, KO, VICTORY/DEFEAT, VS, lobby titles, section labels, match-end screen, start-screen mode headers.

Type Scale with Text Shadow Offset Scale

Hard pixel text shadows (offset, no blur) are used throughout. Shadow offset grows with type size so it remains proportionally punchy at every scale. Shadows pair with the background context — purple shadows over white UI, colored shadows over dark/arena backgrounds.

TokenSizeShadow offset ruleCommon shadow pairings
--text-xs 10px1px 1px 0
--text-sm 12px1px 1px 0White text → accent; Settings labels
--text-base 14px1px 2px 0White text → accent; Body copy
--text-md 18px2px 2px 0P1 white name → p1-mid-dark; P2 white name → p2-mid-dark; Generic white → accent
--text-lg 22px1px 2px 0Yellow overlay sub-text → accent
--text-xl 26px2px 3px 0White timer → accent; Share code white → accent
--text-2xl 28px2px 3px 0White VS / lobby title → accent; Player label → white
--text-3xl 66px3px 4px 0Match-end title accent → semi-white; danger defeat variant
--text-4xl 96px4px 5px 0Main overlay (accent text) → white

Live Specimens

--text-xs / 10pxweight-bold
Used for fine print labels
STYLE GUIDE
--text-sm / 12pxweight-bold
Settings labels, status text, small button text
SOUND EFFECTS
--text-base / 14pxweight-medium
Body text, HUD sub-info, char names on cards
Character name
--text-md / 18pxweight-heavy
Player names in HUD, round label, lobby labels, scores
PLAYER 1   PLAYER 2   ROUND 1
--text-lg / 22pxweight-bold
Overlay sub-text (yellow on dark)
Best 2 of 3
--text-xl / 26pxweight-bold (Roboto Mono) or weight-black (Barlow)
Round timer, lobby title, share code
42   MATCHMAKING
--text-2xl / 28pxBarlow Condensed weight-black
Lobby VS, KO block, player highlight labels
VS   YOU
--text-3xl / 66pxBarlow Condensed weight-black
Match-end screen title
VICTORY   DEFEAT
--text-4xl / 96pxBarlow Condensed weight-black
Main game overlay: FIGHT!, KO, VICTORY
FIGHT!
Mobile overrides
On ≤500px viewports, three sizes scale down
--text-xl 26px → 18px
--text-3xl 66px → 44px
--text-4xl 96px → 56px

Buttons

All buttons use Roboto Mono, 700 weight. Press interaction: translate +2px/+2px with shadow shrink. Release: translate -1px/-2px with shadow grow. Shadow is a hard pixel offset (no blur) — the direction and color are the meaningful signals.

Primary — .btn

Usage
Primary action on each screen: Quick Play, Create Room, Select character, Play Again.

Background: accent #8B49FF
Text: white
Shadow: 4px 4px 0 #ffffff
Hover shadow: 5px 6px 0 #ffffff

Primary Small — .btn.btn-sm

Usage
In-context small actions: Back navigation, Copy Invite. Sits inline with other elements.

Font size: 12px (–text-sm)
Padding: 5px 14px
Radius: 5px (slightly less rounded than full .btn)
Shadow: 2px 2px 0 #ffffff

Secondary — .btn-secondary or .btn.btn-secondary

Usage
Secondary/alternative actions: Share, difficulty selector. Lower visual weight than primary.

Background: white
Text: accent #8B49FF
Shadow: 4px 4px 0 accent (purple)
Note: When combined with .btn.btn-sm, a specificity override ensures purple shadow wins over the default white.

Alternate Primary — .btn.btn-alt

Usage
A second primary-weight CTA that needs to contrast with .btn (purple) on the same page. Currently: VS AI on the start screen, START! in the SP lobby. Not tied to any specific screen — use any time you have two primary CTAs and need visual separation.

Background: --orange #FF8B16
Text: white
Shadow: 4px 4px 0 #ffffff

Destructive — .btn.btn-danger

Usage
Any action that destroys or irreversibly exits. Currently: Leave at game-over overlay. Semantic contract: if it deletes, exits, or can't be undone, use this class.

Background: --danger-color #F00013
Text: white
Shadow: 4px 4px 0 #ffffff

Utility — .btn-utility

Usage
Low-priority functional actions that always appear over colored/arena backgrounds: settings icon, track carousel chevrons, surrender. No shadow — dark semi-transparent pill recedes visually.

Background: rgba(0,0,0,0.35)
Text: white
Shadow: none

Button System — Weight × Intent

.btn-sm composes orthogonally with any intent modifier. e.g. .btn.btn-sm.btn-danger = small destructive button.

Class(es)PreviewBackgroundShadowSemantic role
.btn accent #8B49FF 4px 4px white Primary CTA — default intent.
.btn.btn-alt orange #FF8B16 4px 4px white Alternate primary — contrast with .btn on same page.
.btn.btn-danger danger #F00013 4px 4px white Destructive / irreversible action.
.btn-secondary white #FFFFFF 4px 4px accent Secondary weight — alternative action.
.btn.btn-sm accent #8B49FF 2px 2px white Small primary. radius-xs (5px).
.btn.btn-sm.btn-secondary white #FFFFFF 2px 2px accent Small secondary. opacity 0.55 when inactive.
.btn-utility rgba(0,0,0,0.35) none Minimal weight — icons & low-priority actions.

Sudoku Grid & Cell States

The grid renders over an arena background. Cell backgrounds use white at varying opacity so the arena art shows through on ghosted cells. Own grid uses 88% opacity white for clarity. Opponent empty cells use 25% — visibly ghosted to signal "not yours". Opponent filled cells match own-grid opacity (90%) to show progress legibly.

Grid Identity & Borders

Default (neutral, spectator)
5
3
4
6
7
8
9
1
2
6
7
2
1
9
5
3
4
8
1
9
8
3
4
2
6
5
7
8
5
3
4
2
6
4
2
6
8
5
7
3
9
1
7
1
9
6
2
4
8
7
5
9
6
1
5
3
7
2
8
4
2
8
7
4
1
9
6
3
5
3
4
5
2
8
6
1
7
9
Default: 2px #888888 — spectator/neutral
P1 (is-me — own grid)
5
3
4
6
7
8
9
1
2
6
7
2
1
9
5
3
4
8
8
6
5
7
8
5
5
3
4
2
6
4
2
6
8
5
7
3
9
1
7
1
9
6
2
4
8
7
5
9
6
1
5
3
7
2
8
4
2
8
7
4
1
9
6
3
5
3
4
5
2
8
6
1
7
9
is-me: 3px p1-color — replaces neutral
P2 (is-me — own grid)
5
3
4
6
7
8
9
1
2
6
7
2
1
9
5
3
4
8
8
6
5
7
8
5
5
3
4
2
6
4
2
6
8
5
7
3
9
1
7
1
9
6
2
4
8
7
5
9
6
1
5
3
7
2
8
4
2
8
7
4
1
9
6
3
5
3
4
5
2
8
6
1
7
9
is-me: 3px p2-color — replaces neutral

Cell States Reference

StateClass(es)BackgroundText color / weightNotes
Empty (own).is-me .cellrgba(255,255,255,0.88)Active puzzle cells
Empty (opponent).cell (not is-me)rgba(255,255,255,0.25)Ghosted — not interactive
Given number.cell.giveninheritstext-secondary #333, w700Pre-filled clue cells. Still clickable.
Correctly filled.cell.correctrgba(255,255,255,0.88)p1-dark/p2-dark, w600Color keyed to grid owner
Highlighted row/col.cell.highlightp1/p2-bg-lightSame row/col as cursor
Same number.cell.same-numberp1/p2-bg-midAll cells matching entered digit
Selected cursor.cell.selectedp1/p2-bg-lightInset box-shadow: 2px solid p1/p2-color
Invalid entry.cell.dangerdanger-bg-light #FFEBECdanger-dark #A8000F, w600Red diagonal X overlay via ::before/::after
Opponent cursor.cell.opponent-cursorp1/p2-bg-light3px inset outline in p1/p2 color
Opponent filled.cell.opponent-filledrgba(255,255,255,0.9)p1/p2-dark, w600Matches own-grid opacity to show legibly

Grid Borders & Box Dividers

Thin cell lines: 1px #D5D5D5. Thick 3×3 box dividers: 2px #888888. The outer border uses CSS custom properties (--_grid-outline-color, --_grid-outline-width) so that .is-me overrides both color and width on the same single border declaration — it fully replaces the neutral border rather than layering on top of it. Default: 2px grid-box-border. Own grid: 3px in the player's identity color. Right and bottom borders of cells adjacent to box lines are suppressed to prevent double-border artifacts.

Mobile Numpad

1
2
3
4
5
6
7
8
9
Usage
Shown only on touch (coarse pointer) devices. One tap = number entry.

Background: rgba(255,255,255,0.85)
Border: 2px rgba(0,0,0,0.15)
Radius: 6px
Font: 19px, w700, Roboto Mono
Active: scale(0.93) + fully opaque white

HUD & Gameplay UI

Health Bar System

The bar track is always danger red — the red is the "low HP" signal already baked in. The fill bar covers it: yellow at full, orange at ~50%, then pulsing yellow at low HP.

Full HP (yellow)
Fill: yellow #FFCA00
Mid HP (orange)
Fill: orange #FF8B16
Low HP (pulsing yellow)
Fill: yellow pulsing (opacity blink)
KO (empty HP)
Track: danger-color fully visible

Full HUD

PLAYER ONE ROUND 2 PLAYER TWO
KO
1240 PTS 42 870 PTS
Timer at <=10s: color switches to danger-color, shadow to orange, blinks. KO block: Barlow Condensed 28px, accent purple fill, white text.

Game Overlay

FIGHT!
Round 1
VICTORY
You win! 2–1
DEFEAT
Opponent wins
Main text: Barlow Condensed 96px w900. Normal state: accent purple + white shadow. Defeat: danger-color red. Sub-text: Roboto Mono 22px w700, yellow + purple shadow (never white — legible on any arena bg).

Floating Score Pop

Appears near the cell where a correct number is entered. 13px, w800, accent purple. Floats up 36px and fades out over 0.75s.

Attack Flash

Full-screen inset box-shadow flashes on hit, keyed to attack type:
• Punch: rgba(240,0,19,0.22) — subtle red, 0.25s
• Heavy: rgba(255,139,22,0.32) → red — orange then red, 0.35s
• Self-hit: rgba(240,0,19,0.20) — 0.2s

Lobby & Screen Components

Lobby Text Hierarchy

MATCHMAKING
YOU
Xiao Long
READY
VS
PLAYER 2
---
WAITING...
AB12CD
Lobby title: Barlow Condensed 26px w900, white + purple shadow (2px 2px).
VS: Barlow Condensed 28px w900, white + purple shadow (2px 3px).
Own player label: Barlow Condensed 28px w900, player color + white shadow (3px 3px).
Other player label: Barlow Condensed / Roboto Mono 18px w700, white + purple shadow.
Player name: Roboto Mono 18px w700, white + purple shadow.
Share code: Roboto Mono 26px w900, white + purple shadow, letter-spacing 0.35em.
Status pills: 12px w700, radius 20px — Ready: white text on accent; Waiting: text-secondary on border.

Start Screen Labels

2 PLAYER
SINGLE PLAYER
Mode labels: Barlow Condensed 26px w900, white + purple shadow (2px 2px), letter-spacing 0.12em.
Section labels: Roboto Mono 12px w700, white + purple shadow (1px 1px), letter-spacing 0.08em, uppercase.

Character Card

🥊
Xiao Long
Default
🥊
Xiao Long
Selected
White surface card, radius 0, purple shadow (4px 4px).
Selected: accent purple fill, white shadow (4px 4px), name turns white.
Hover: translate(-1px, -2px) + shadow grows to 5px 6px.

Settings Panel

MUSIC
TRACK
Track Title
SOUND EFFECTS
Panel: rgba(20,20,20,0.96) near-black with subtle white border. Fixed to top-right.
Labels: 12px w700 white, letter-spacing 0.1em, uppercase.
Toggle on: accent purple. Toggle off: text-tertiary grey.
Carousel chevrons: utility buttons (44×44px minimum touch target).

Screen & Form Components

Join Combo — .join-combo

Input and action button fused into a single bordered unit. Currently appears once on the start screen for room code entry. White outer border, purple box-shadow. The inner button acts as a secondary action (white bg, accent text) with no border-radius.

Anatomy
Container: 2px white border, radius-sm, purple box-shadow (4px 4px), overflow:hidden
Input: bg surface, 12px bold uppercase, letter-spacing 0.1em
Placeholder: tertiary color, normal weight, no letter-spacing
Button: white bg, accent text, 15px bold, left-border 1px
Button hover: accent-light bg
Button active: accent bg, white text (inverts)

Standalone Input

Proposed for future forms (player name entry, search, etc.). Not currently used standalone in the game. Focus ring uses accent-light at 3px spread — soft glow rather than hard pixel, since this is a white-bg form context not an arena overlay.

States
Default: 2px border #E5E5E5, radius-sm
Focus: border accent, 3px soft glow accent-light
Error: border danger-color, glow danger-bg-light
Disabled: bg surface (#F2F2F2), tertiary text, not-allowed cursor

Font: Roboto Mono 12px bold. Inherits game aesthetic but softens the focus ring compared to hard-pixel shadows (appropriate for form UI on white background).

Inline Messages — Error & Success

Inline feedback text beneath form elements. Rendered in-place using visibility: hidden / visibility: visible — never display: none — so layout does not shift when they appear.

Room not found. Check the code and try again.

Copied to clipboard!

Usage
Error: danger-color, 1px 1px dark shadow (improves legibility over arena)
Success: accent color, 1px 1px white shadow
Always reserve the line height even when empty. Use visibility not display to toggle.

Glass Card — .lobby-share

Semi-transparent frosted panel used for the private room invite code. Sits over the arena background. Used sparingly — only when content needs grouping against a complex background.

Invite a Friend
AB12CD
Style
Background: rgba(255,255,255,0.12)
Border: 1px rgba(255,255,255,0.25)
Radius: radius-md (12px)

Inner label: 12px bold white, uppercase, letter-spacing 0.08em
Share code: Roboto Mono 26px w900, white, letter-spacing 0.35em, purple shadow

Lobby Promo — visibility-based feedback

Quick Play share row. The feedback text ("Copied to clipboard") always occupies its line height via visibility: hidden/visible — this prevents the button from jumping down when feedback appears.

Copied to clipboard

Copied to clipboard

The no-jump pattern
Reserve line height always. Toggle visibility not display.
Feedback text: 12px bold, rgba(255,255,255,0.85) — slightly dimmer than primary labels.
Left: hidden (button sits at its natural position). Right: visible — button stays in place.

Carousel — .track-carousel

Used for track selection in settings and arena selection in lobbies. Chevron buttons are utility style with 44×44px minimum touch target. Title is center-fixed width with overflow ellipsis.

Usage
Chevrons: .btn-utility style, min 44×44px touch target
Title: Roboto Mono 14px bold, white, 160px fixed width, ellipsis overflow
Gap between chevron and title: 4px
Used in: settings panel (track), both lobby screens (arena picker)

Difficulty Selector — .sp-diff-btn

Row of small secondary buttons where inactive options are dimmed to 55% opacity. The selected button restores to full opacity — no other visual indicator needed.

Difficulty
Pattern
Base: .btn.btn-sm.btn-secondary at opacity 0.55
Selected: opacity 1.0 — full white surface, no extra outline
The opacity differential (0.55 → 1.0) is the only selection signal needed at this size. An outline was previously tried and removed — it conflicted with the button's own box-shadow.

Lobby Hint — pulsing status text

Used for "Waiting for opponent…" and similar transient states. Pulse draws attention without urgency.

Waiting for opponent...

Style
Roboto Mono 14px, white, purple shadow 1px 2px 0
Animation: fade-pulse 1.4s ease-in-out infinite (opacity 1.0 → 0.4)

Combo Counter

Lives in the HUD sub-row alongside the score. Reserves its line height even when empty so the score doesn't shift when a combo appears.

No combo
 
Active combo
×3 COMBO!
P2 combo
×5 COMBO!
Style
18px bold white, player-colored shadow (mid-dark)
min-height: 1em — always reserves space so score doesn't jump
P1: shadow p1-mid-dark — P2: shadow p2-mid-dark

Match-End Screen Elements

VICTORY
DEFEAT
🥊
Xiao Long
Typography
Title: Barlow Condensed 66px w900
Victory: accent color, 3px 4px 0 #ffffff shadow
Defeat: danger-color color, same shadow
Animation: pop-in scale 0.55→1

Winner name: Roboto Mono 26px w800, accent color, 2px 3px 0 #ffffff

Character portrait: 120×120px, radius-md (12px), pixelated rendering

Start Screen Divider

Section above
Section below
Style
240px wide, 1px, rgba(255,255,255,0.25)
Matches the fixed width of the start screen CTAs. Used between 2 PLAYER and SINGLE PLAYER sections.

Text Color & Shadow Pairing Logic

The game renders UI over complex, colorful arena backgrounds. All readable text therefore uses white or high-chroma colors with hard pixel shadows that ensure contrast regardless of what's behind.

ContextText colorShadow colorOffsetReason
Generic white text over arena#ffffffaccent #8B49FF2px 2px 0Purple provides contrast on any bg
P1 player name (HUD)#ffffffp1-mid-dark #0A8FBB2px 2px 0Cyan shadow reinforces P1 identity
P2 player name (HUD)#ffffffp2-mid-dark #BC00742px 2px 0Magenta shadow reinforces P2 identity
P1 player label (lobby, selected)p1-color #17B9EF#ffffff3px 3px 0Player-color on dark bg; white grounds it
P2 player label (lobby, selected)p2-color #F31299#ffffff3px 3px 0Same logic, magenta
Round timer (normal)#ffffffaccent #8B49FF3px 3px 0Large text, offset scales up
Round timer (urgent ≤10s)danger-color #F00013orange #FF8B162px 3px 0Red with warm orange shadow — high alarm
Main overlay (FIGHT! / VICTORY)accent #8B49FF#ffffff4px 5px 0Biggest text; needs widest shadow
Main overlay (DEFEAT)danger-color #F00013#ffffff4px 5px 0Red as danger signal
Overlay sub-textyellow #FFCA00accent #8B49FF1px 2px 0Yellow is legible on both light and dark arenas
Primary btn text#ffffffn/a (box-shadow on element)Button surface carries the shadow
Secondary btn textaccent #8B49FFn/aWhite surface, no text shadow needed

The Mid-Dark Rule

When white text appears with a player-color shadow, use the mid-dark variant (p1-mid-dark, p2-mid-dark), not the primary color. The primary cyan and magenta are too bright for a shadow — they'd feel garish. The mid-dark is dark enough to read as a grounding shadow while still being clearly player-colored.

Proposed Components

These components do not yet exist in the game but are consistent with the design language and likely needed as the game adds features. Specs are proposals — refine before shipping.

Badge / Tag

Small pill labels for surfacing state, novelty, or counts. Uses --radius-pill. Variants keyed to semantic meaning, not visual preference.

NEW BETA 3 P1 P2 OFFLINE
Variants
.badge-new — accent purple: new feature/content
.badge-beta — orange: experimental/unreleased
.badge-count — danger red: notification count
.badge-p1/p2 — player identity colors
.badge-neutral — grey: inactive/offline state

Font: 10px bold uppercase, letter-spacing 0.08em

Toast Notification

Ephemeral feedback that appears over gameplay or between screens. Bottom-anchored, auto-dismisses. Uses a left accent border rather than full background color to keep it readable over any arena. Would need a JS show/hide with a slide-up + fade animation.

Match saved to history
Connection lost — reconnecting
i Opponent is typing...
Structure
White surface card, 4px left border in semantic color
Success: accent border
Error: danger-color border
Info: grid-box-border (#888) border

Shadow: --shadow-md
Font: 12px bold, text-primary
Proposed animation: slide up 8px + fade in over 0.2s, auto-dismiss at 3s

Modal / Confirmation Dialog

Blocks interaction for destructive confirmations (e.g., "Surrender — are you sure?"). Backdrop at 65% black. Panel on white surface. Actions use the existing button system — confirm uses .btn-danger, cancel uses .btn-secondary.

Surrender match?
You'll forfeit the current round. This can't be undone.
Backdrop: rgba(0,0,0,0.65). Panel: white surface, radius-md, heavy box-shadow.
Title: Roboto Mono 18px bold, text-primary. Body: 12px, text-secondary, line-height 1.6.
Actions right-aligned. Cancel left of destructive — follows platform conventions.

Tooltip

Contextual info on hover. Useful for explaining game mechanics (e.g., what "combo" means, difficulty differences). Uses the same dark panel as the settings menu, with a hard pixel shadow in accent to stay on-brand.

COMBO
3+ correct cells in a row
Style
Bubble: #1a1a2e near-black, white text, 10px bold
Shadow: 2px 2px 0 accent — keeps it on-brand
Arrow: CSS border-trick, matches bubble color
Trigger: dashed underline on the term being explained
Show/hide: CSS :hover on parent, or JS for touch

Dropdown / Select

Styled <select> for settings that have more than 2–3 options (e.g., language, region, control scheme). Uses custom chevron SVG via background-image to replace the native arrow.

Style
Same base as standalone input: 2px border, radius-sm, Roboto Mono 12px bold
Custom chevron via background-image SVG
appearance: none removes native chrome
Focus: accent border + accent-light glow (same as input)

Loading States — Skeleton & Spinner

For async waits: character portrait loading, matchmaking, network reconnect. Skeleton uses a shimmer gradient. Spinner uses accent color on top of grey track.

Connecting...
Skeleton
Shimmer: linear-gradient 90° cycling over --border / --bg
800px background-size, 1.4s infinite — smooth left-to-right sweep

Spinner
24×24px (or 16×16px small), 3px border
Track: --border grey. Active arc: --accent purple
0.7s linear rotation