5.8 KiB
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-gradientat 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
#1a1200on 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-dimon--bg-panelis 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 usefont-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.3–0.5px. Page titles 34px, brand 22px, login brand 30px. - Micro-labels: uppercase, 10–12px, letter-spacing 0.08–0.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,.dangervariant). - Status badges (
.badge+.badge-ok/-fail/-pending/-info): dot + uppercase label, tinted 6%-alpha background, colored dot (glowing/pulsing per state). - Tables (
table.tblin.tbl-wrap): dense, hairline row borders, raised uppercase header, right-aligned.num-cell, subtle row-hover,.empty-rowfor 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 (.tagamber +.payload). - Chrome: sticky
.topbarwith bracketed.brand, uppercase.topnav(active = amber),.session-indicatorwith pulsing.pulse-dot.
Layout
- App shell: sticky topbar (56px) over a centered
.main,max-width: 1180px, padding32px 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-labelwith 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: reduceblock — the pulsing dots and progress transitions need a static fallback. Add before shipping motion work.