Files
fhd_scan_web_app/design.md
T
gardentech f34817d5b6 Initial commit: scan_web integrated recording UI
FastAPI backend + vanilla JS frontend for LiDAR/camera startup, camera
settings, and rosbag recording, unifying the previous scan_gui/scan_gui_dual/
scan_gui_triple desktop tools into one web app.
2026-08-07 14:26:00 +09:00

103 lines
4.8 KiB
Markdown

# Design — scan_web
Locked design system for the unified scan/record control panel. Every future
view (the M3 Scanning/LIO view, any later addition) reads this file before
shipping new UI — extend it, don't invent a parallel system.
## Genre
modern-minimal (Stripe / Linear / instrument-panel school — SaaS/dashboard trigger)
## Macrostructure family
This is an **app**, not a marketing site — no macrostructure/nav-archetype/
footer-archetype apparatus applies (those are page-shape concepts for
landing pages). The whole app is one page-type:
- App pages (Setup/Control view, Scanning/Live view, any future view):
hairline-bordered panel grid, left-biased control column + right-biased
primary content, sticky toolbar with a segmented view switch. No
enrichment — function carries the page (never add hero imagery/CSS art).
## Theme
Custom — cool-cobalt, referenced against the "Tally" modern-minimal SaaS
example the user pointed at, adapted toward Cobalt's instrument-panel
discipline (hairlines over shadow, mono status readouts) since the app is
an operational dashboard, not a marketing hero.
- `--color-paper` oklch(97.8% 0.005 255)
- `--color-paper-1` oklch(99.3% 0.003 255)
- `--color-ink` oklch(22.0% 0.020 258)
- `--color-ink-2` oklch(38.0% 0.016 257)
- `--color-rule` oklch(85.0% 0.010 255)
- `--color-accent` oklch(52.0% 0.185 256)
- `--color-focus` oklch(48.0% 0.200 256)
- Functional state colours (not brand accent — status semantics):
`--color-success` (149°), `--color-warning` (68°), `--color-danger` (25°)
Full token set: [`frontend/css/tokens.css`](frontend/css/tokens.css).
## Typography
- Display: Geist, weight 650, normal style
- Body: Geist, weight 400 (single-family discipline — the modern-minimal signature)
- Mono/outlier: Geist Mono, weight 500 — carries exactly one role: status
badges, tab labels, meta/hint text, and numeric-readout fields (camera
params). Do not reach for it a third role.
- Korean UI copy falls back per-glyph to "Noto Sans CJK KR" / "Noto Sans KR"
automatically — Geist covers Latin only.
- Fonts are self-hosted at `frontend/vendor/fonts/` (woff2, variable) —
never a Google Fonts CDN link. The field PC/phone may have no internet.
## Spacing
4-point named scale in `tokens.css` (`--space-3xs``--space-2xl`). Always
reference by name.
## Motion
- Easings: `--ease-out` / `--ease-in` / `--ease-in-out`, no bounce/overshoot.
- Modern-minimal default: reveals are OFF — this is a repeatedly-used tool,
not a first-impression marketing page. The only motion is functional:
button press, badge colour transition, tab crossfade, REC pulse dot.
- `prefers-reduced-motion: reduce` collapses everything to ≤150ms opacity
(see `tokens.css`).
## Microinteractions stance
- Silent success (no toast for visible state changes).
- Focus rings appear instantly, never animated in.
- No `transition: all` anywhere — properties are named explicitly.
## Status/health vocabulary — must stay legible in direct sunlight
Every status signal is **icon + text + colour**, never colour alone
(outdoor/colour-blind requirement, not just a Hallmark default):
- `badge-ok` — ✓, success green
- `badge-stale` — !, warning amber
- `badge-down` — ✕, danger red
- `badge-rec` — pulsing dot, danger red
- The ● / state-line idiom (`● 실행 중` / `● 정지`) is carried over from the
original PyQt5 tools on purpose — operators already know it.
## CTA voice
- Primary actions (시동/녹화 시작): pill radius, solid semantic fill
(`--color-success` / `--color-danger`), 44px min-height (touch target floor).
- Secondary/utility actions: `.btn-gray`, `.btn-blue` — same pill shape,
neutral or accent fill.
- E-STOP: outlined danger pill that fills solid on hover/press — visually
distinct from the recording Stop button, since it's a different severity
of action (kills every subsystem at once).
## What pages MUST share
- The token set in `tokens.css` — never an inline OKLCH/hex value.
- The badge/status vocabulary above.
- 44px minimum touch target on every interactive control (phone access is
a hard requirement, not a nice-to-have).
- Hairline-bordered panels, whisper shadow at most — no drop-shadow cards.
## What pages MAY differ on
- Panel layout within a view (the Scanning/LIO view is full-viewport canvas
+ status strip, not the Setup view's panel grid — that's fine, it's a
different *content* need, not a different *system*).
## Related tool: the calibration GUI (`~/dvlc_gui/dvlc_calib_gui.py`)
Native PyQt5, out of scope for a web port (Phase 2, if ever). Its Qt
stylesheet was updated to reuse this same palette (sRGB conversions of the
tokens above) purely for visual consistency across the two tools the
operator switches between — no functional change. See the `STYLESHEET`
constant and `C_*` colour constants near the top of that file.