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.
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user