Catches Web UI

Living style guide for catches.world, fishid.catches.world, fishid.catches.world/server, and dataset.catches.world. Every component below is the canonical version. If an existing site differs, the site is the one to update — not this guide.

UseReference this page (or copy tokens.css + components.css) when building or modifying any Catches web surface.
Don'tDefine new :root variables, invent new button class names, or roll your own loading spinner.
01 / Foundations

Tokens

All variables live in tokens.css. Reference them by name; never hardcode hex.

Color — Surfaces

--bg
#08080A
--surface
#0F1318
--card
#141920
--card-hover
#1A2232

Color — Accent

--accent
#00C9A7
--accent-hover
#00E8C2
--accent-dim
rgba(0,201,167,.12)
--accent-glow
rgba(0,201,167,.35)
--border-hi
rgba(0,201,167,.28)

Color — Semantic

--blue
#4FC3F7
--warn
#F6AD55
--danger
#F05252
--gold
#F7C04F

Each has a -dim companion (12–14% alpha) for badge/background fills.

Color — Text

--text
#F0F4F8
--muted
#7A8A9A
--faint
#2E3D4E
--border
rgba(255,255,255,.06)

Typography

Fraunces — Display · 600 · 2.4rem
Pomacanthus imperator
Fraunces — Display · 400 · 1.6rem
Living style guide for the Catches family
DM Sans — Body · 400 · 1rem
Default body weight is 400. Don't drop to 300 — the dark theme already runs thin.
DM Sans — Body · 500 · 0.88rem
Used inside .btn-primary and emphasis spans.
JetBrains Mono — UI · 500 · 0.68rem · uppercase
SECONDARY ACTIONS · LABELS · BADGES
JetBrains Mono — UI · 400 · 0.78rem (data)
12.4ms · 87.2% · 2026-05-03 14:22

Spacing scale

--space-14
--space-28
--space-312
--space-416
--space-524
--space-632
--space-748
--space-864

Radius

--radius-sm · 6px
--radius-md · 8px
--radius-lg · 12px
--radius-xl · 18px

Motion

TokenValueUse
--ease-outcubic-bezier(0.16, 1, 0.3, 1)Default for all transitions
--dur-fast0.2sHover, focus, button states
--dur-base0.3sDrawer, lightbox, panel toggles
--dur-slow0.55sResult reveals, hero animations
03 / Components

Buttons

Three classes. No .fi-btn-*, no .btn-cta, no per-page custom styles.

Variants

States

Default
Loading

Click to see in-button spinner.

Disabled

Size modifiers

<button class="btn-primary">Identify</button>
<button class="btn-ghost">Cancel</button>
<button class="btn-danger">Delete</button>
04 / Components

Forms

Inputs

Must be a number between 1 and 3

Segmented control

05 / Components

Badges & Status

Badge variants

Identified Busy Failed Tier 2 Draft

Status dots

Running
Busy (pulsing)
Error
Stopped
05b / Components

Toast & Banner

Use a banner when the message is tied to specific page content (form-level success / error). Use a toast when it's a transient global notice (saved, sync failed). Toasts auto-dismiss after 4–5s; banners stay until acknowledged.

Banner — inline, contextual

Toast — transient, global

Auto-dismisses after 4s. Stack top-right under nav.
05c / Components

Empty State

Every list view must have one. Show an icon, one-line title, one-sentence reason, and a primary action (or a link to docs).

No catches yet
Identify your first fish in the iOS app, or upload a photo here to get started.
06 / Components

Loading

Three buckets. Pick by use case, not by aesthetics.

ForbiddenScan-line, HUD, and pulse animations are only for marketing hero sections. Don't use them as product loading states.

A · Skeleton (list / data fetch)

B · Full spinner (inference / long task)

Identifying species
UPLOAD DETECT CLASSIFY VERIFY

C · In-button (submit / save)

Disabled while loading; copy unchanged.
07 / Components

Upload

One zone, four states. Drag a file over the demo to see .is-dragover; click the cycle button to step through all states.

Drop a file or browse
JPG · PNG · HEIC · max 12MB
Current: idle

Compact variant

Add photo
08 / Patterns

Cards & Containers

Card

Default card
Pomacanthus imperator
Emperor angelfish
Highlighted card
Primary machine · GPU-01
Status
Running · 12 jobs/min

Page containers

Classmax-widthUse
.page1080pxDefault — fishid root, content-heavy admin
.page--wide1400pxDataset list, server dashboard, dense grids
.page--narrow720pxLegal, support, long-form text
09 / Patterns

Detail Patterns

Pick by record importance, not by which page you're on. Today's dataset list-→species drawer is wrong; it should be a full-page navigation.

RelationshipModeWhy
List → primary recordFull-page navURL is shareable, refresh works
List → light detail (log row)DrawerQuick peek without losing context
Detail → media zoomLightboxModal focus, esc closes

Drawer demo

Lightbox demo

10 / Patterns

Tables & Activity rows

CSS grid rows with mono columns. Reference: server admin activity log.

14:22:08
CPU
Pomacanthus imperator
128ms
primary
14:22:15
GPU
Lutjanus campechanus
42ms
primary
14:22:31
GPU
Detection failed
backup
11 / Patterns

Breakpoints

BreakpointBehavior
≥ 861pxTwo-column grids, side-nav visible
≤ 860pxSingle column, drawer goes full-width, side-nav becomes top scroller
≤ 560pxReduce hero font sizes, single-column feature grids, upload zone padding shrinks to 44×20

Resize this window to see the side-nav collapse at 860px and upload zones tighten at 560px.

Reference

Legacy migration map

Step 2 (after this guide is approved). Don't apply until then.

SiteDriftFix
catches.worldnav 64px, Fraunces logo, scan/HUD animsLogo exception keeps; scan kept inside hero only; nav 64→56 next pass
fishid rootnav 60px, .fi-btn-*nav 56; rename to .btn-*
fishid/server(reference impl)
dataset/login--surface #111113, hardcoded buttonToken alignment + .btn-primary.btn--block
dataset/indexdrawer + 760px breakpointdrawer→full page; breakpoint→860px
dataset/speciesswiper + 760px breakpointbreakpoint→860px; swiper kept
dataset (all)body weight 300→ 400