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.
tokens.css + components.css) when building or modifying any Catches web surface.:root variables, invent new button class names, or roll your own loading spinner.Tokens
All variables live in tokens.css. Reference them by name; never hardcode hex.
Color — Surfaces
Color — Accent
Color — Semantic
Each has a -dim companion (12–14% alpha) for badge/background fills.
Color — Text
Typography
.btn-primary and emphasis spans.Spacing scale
Radius
Motion
| Token | Value | Use |
|---|---|---|
| --ease-out | cubic-bezier(0.16, 1, 0.3, 1) | Default for all transitions |
| --dur-fast | 0.2s | Hover, focus, button states |
| --dur-base | 0.3s | Drawer, lightbox, panel toggles |
| --dur-slow | 0.55s | Result reveals, hero animations |
Forms
Inputs
Segmented control
Badges & Status
Badge variants
Status dots
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
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).
Loading
Three buckets. Pick by use case, not by aesthetics.
A · Skeleton (list / data fetch)
B · Full spinner (inference / long task)
C · In-button (submit / save)
Upload
One zone, four states. Drag a file over the demo to see .is-dragover; click the cycle button to step through all states.
idle
Compact variant
Cards & Containers
Card
Page containers
| Class | max-width | Use |
|---|---|---|
| .page | 1080px | Default — fishid root, content-heavy admin |
| .page--wide | 1400px | Dataset list, server dashboard, dense grids |
| .page--narrow | 720px | Legal, support, long-form text |
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.
| Relationship | Mode | Why |
|---|---|---|
| List → primary record | Full-page nav | URL is shareable, refresh works |
| List → light detail (log row) | Drawer | Quick peek without losing context |
| Detail → media zoom | Lightbox | Modal focus, esc closes |
Drawer demo
Lightbox demo
Tables & Activity rows
CSS grid rows with mono columns. Reference: server admin activity log.
Breakpoints
| Breakpoint | Behavior |
|---|---|
| ≥ 861px | Two-column grids, side-nav visible |
| ≤ 860px | Single column, drawer goes full-width, side-nav becomes top scroller |
| ≤ 560px | Reduce 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.
Legacy migration map
Step 2 (after this guide is approved). Don't apply until then.
| Site | Drift | Fix |
|---|---|---|
| catches.world | nav 64px, Fraunces logo, scan/HUD anims | Logo exception keeps; scan kept inside hero only; nav 64→56 next pass |
| fishid root | nav 60px, .fi-btn-* | nav 56; rename to .btn-* |
| fishid/server | (reference impl) | — |
| dataset/login | --surface #111113, hardcoded button | Token alignment + .btn-primary.btn--block |
| dataset/index | drawer + 760px breakpoint | drawer→full page; breakpoint→860px |
| dataset/species | swiper + 760px breakpoint | breakpoint→860px; swiper kept |
| dataset (all) | body weight 300 | → 400 |