Files
imap-copier/DESIGN.md
T
2026-07-03 11:18:40 +07:00

5.8 KiB
Raw Blame History

Design

Terminal / ops-console visual language for an IMAP migration tool. Near-black, green-tinted dark surface; a single amber functional accent; monospaced body with a condensed display face for headings. Dense, instrument-panel layouts where the system state is always legible. Captured from web/src/index.css and web/src/app.css — these tokens are canonical; new work extends them, it does not replace them.

Theme

Dark, committed. Not "dark to look cool" — the scene is an operator running a long-lived migration and watching thousands of counters tick; a dark console keeps the amber accent and the ok/fail/pending signal colors doing the reading work with minimal eye strain. Color strategy: restrained — tinted neutrals carry the surface, one amber accent stays under ~10% of the pixels, and the green/red/yellow/blue roles appear only as status semantics.

Signature textures (keep, don't multiply):

  • A faint amber radial glow at the top of the page (radial-gradient at 50% -10%).
  • A 3px repeating-linear scanline overlay at ~1.2% white — the CRT tell.
  • Panels wear a floating uppercase tab label (.panel-label) punched through the border; the log panel mirrors it on the right (.log-clear).

Color

OKLCH is the target space for any new color; existing tokens are hex and stay hex.

Surfaces (green-tinted neutrals, darkest → raised)

  • --bg #0a0d0b — page base
  • --bg-inset #070a08 — insets: inputs, progress track, log pane
  • --bg-panel #0f1512 — panels, cards, login card
  • --bg-panel-raised #141b17 — topbar, table headers, modal dialog

Borders

  • --border #23342b — default hairline (1px)
  • --border-bright #3a5443 — raised/interactive edges, dashed underlines

Ink

  • --fg #dbe8de — primary text
  • --fg-dim #6f8478 — labels, secondary text, nav idle
  • --fg-faint #4a5c50 — hints, empty states, faint indices

Accent (amber — attention & primary action)

  • --accent #ffb238 — primary buttons, active nav, panel labels, progress fill
  • --accent-strong #ffd27a — hover/emphasis
  • --accent-dim #7a5a26 — active borders, tinted glows
  • Primary-button ink is near-black #1a1200 on amber, never white.

Status semantics (color always carries meaning)

  • --ok #52e6a0 / --ok-dim #234a37 — success (glowing dot)
  • --fail #ff5d5d / --fail-dim #4a2323 — error
  • --pending #f0c419 — waiting/queued
  • --info #57c2ff — in-progress / scan / new-folder marker (pulsing dot)

Contrast watch: --fg-dim on --bg-panel is the thinnest pairing — for real body copy (not just uppercase micro-labels) prefer --fg. Placeholder and small print must still clear AA; don't push text below --fg-faint.

Typography

Two families on a real contrast axis (mono vs. condensed display) — never two similar sans.

  • Body / UI / data: --font-mono = JetBrains Mono (400/500/700), 14px base, line-height 1.5. Numbers use font-variant-numeric: tabular-nums (.mono-num, .num-cell, stat values) so columns align.
  • Display / headings / brand: --font-display = Big Shoulders Display (600/800), uppercase, letter-spacing ~0.30.5px. Page titles 34px, brand 22px, login brand 30px.
  • Micro-labels: uppercase, 1012px, letter-spacing 0.080.14em, --fg-dim. This is the workhorse label style (nav, field labels, badges, table headers, panel tabs). Used as a deliberate system, not a per-section eyebrow.

Components

  • Panels (.panel): bordered surface, 3px radius, floating uppercase tab label. The primary content container — used instead of stacked cards.
  • Buttons: .btn (ghost outline, uppercase, 2px radius) · .btn-primary (solid amber, dark ink, amber glow on hover) · .btn-danger · .btn-ghost · .link-btn (dashed-underline text button, .danger variant).
  • Status badges (.badge + .badge-ok/-fail/-pending/-info): dot + uppercase label, tinted 6%-alpha background, colored dot (glowing/pulsing per state).
  • Tables (table.tbl in .tbl-wrap): dense, hairline row borders, raised uppercase header, right-aligned .num-cell, subtle row-hover, .empty-row for empty states. Horizontal scroll lives on .tbl-wrap.
  • Forms (.field, .field-row): uppercase label above inset input; focus = amber border + 3px amber-12% ring.
  • Modals (.modal-overlay + .modal-dialog, .modal-md/-lg): fixed overlay, backdrop-blur(2px), fade + 6px rise entrance. Folder-mapping grid (.map-row: src → select). z-index scale: topbar 10 → overlay 100.
  • Live progress (.acct-progress, .pbar/.pbar-fill, .pmeta, .pscan, .acct-error): thin amber bar + mono meta; scan phase in --info; persisted last-error in --fail, ellipsis-truncated.
  • Log pane (.log-pane): reversed-column inset console, tagged lines (.tag amber + .payload).
  • Chrome: sticky .topbar with bracketed .brand, uppercase .topnav (active = amber), .session-indicator with pulsing .pulse-dot.

Layout

  • App shell: sticky topbar (56px) over a centered .main, max-width: 1180px, padding 32px 24px 64px.
  • Grids: repeat(auto-fit, minmax(340px, 1fr)) for panel grids — responsive without breakpoints. Flexbox for 1D rows (.btn-row, .stat-row, .upload-row).
  • Radii: tight — 2px (controls) / 3px (surfaces). Nothing pill-shaped.
  • Dividers: centered uppercase .divider-label with rule lines both sides.

Motion

Restrained, functional. Ease-out only; no bounce/elastic.

  • Entrances: modal fade 0.12s + rise 0.14s.
  • Feedback: 0.15s color/border transitions on nav, buttons, inputs.
  • Ambient: pulse (2.4s) on session/info dots; progress-bar width 0.3s ease-out.
  • Amber glow bloom on primary-button hover.
  • Owed: a prefers-reduced-motion: reduce block — the pulsing dots and progress transitions need a static fallback. Add before shipping motion work.