|
| 1 | +# ADR-004: Paired Mission Control / Workstation Aesthetic with CSS Design Tokens |
| 2 | + |
| 3 | +**Status**: Accepted |
| 4 | + |
| 5 | +## Context |
| 6 | + |
| 7 | +The SPA shipped initially with a generic Tailwind-driven SaaS dashboard look: system fonts, slate-grey palette with a green accent sprinkled evenly across the chrome, circle markers, and a popup that duplicated the Vault row when a region offered multiple editions. A UX review flagged the design as "competent but indistinguishable from every other cloud-service map" and identified specific issues: an Australia-centric default map view, hidden-on-mobile region count, no provider identity in markers, and amber styling collisions between AWS branding and warning callouts. |
| 8 | + |
| 9 | +A redesign was proposed, brainstormed across two directions ("Mission Control HUD" vs "Cartographic Atlas"), and the Mission Control direction was chosen. To make light mode a first-class peer of dark mode — not a flashlight-inversion of the cockpit aesthetic — a second variant (`Workstation`) was added: same DNA, calmer language. The two themes share the same component anatomy and only swap underlying tokens. |
| 10 | + |
| 11 | +The redesign is a single-file SPA change (`layouts/index.html`) because the project's architecture deliberately keeps the entire frontend in one Hugo template. Every visible component therefore has to pick its colours, fonts, and spacing values from somewhere — a Tailwind utility class, an inline style, or a custom CSS rule. Without a shared vocabulary the redesign would have continued the original pattern of scattering hex values across hundreds of lines and would have made future theme work hard. |
| 12 | + |
| 13 | +## Decision Drivers |
| 14 | + |
| 15 | +- One toggle, one product. Users flipping dark↔light should see the same product wearing different surfaces, not two different apps. |
| 16 | +- A small palette that has *meaning*. The accent green was previously decorative everywhere; in the new system it signals "live / available / current selection" specifically. |
| 17 | +- Mono-first typography (with a display serif/sans companion) to differentiate from the SaaS-dashboard pack and lean into the data-density of the map. |
| 18 | +- Provider identity at the marker level — users shouldn't need to read the legend to know whether a marker is AWS or Azure. |
| 19 | +- Theming must work without `.dark` / `.light` selectors on every component. The CSS variable system replaces dozens of paired overrides. |
| 20 | + |
| 21 | +## Decision |
| 22 | + |
| 23 | +### Tokenized theme system |
| 24 | + |
| 25 | +A single `:root` block defines the dark-mode (default) tokens. An `html.light` block re-declares the same token names with light-mode values. Every redesigned component reads colours through `var(--bg)`, `var(--text)`, `var(--accent)`, `var(--azure)`, `var(--aws)`, etc. No component declares hex values directly except where the value is provider-identity (e.g. AWS Squid Ink) and required to render in a context that doesn't inherit CSS variables (e.g. SVG attribute fills are wrapped in `var(--azure)` / `var(--aws)` for the same reason). |
| 26 | + |
| 27 | +Token domains: |
| 28 | +- Surface: `--bg`, `--bg-elev`, `--bg-elev-2` |
| 29 | +- Hairlines: `--border`, `--border-strong` |
| 30 | +- Ink: `--text`, `--text-mute`, `--text-dim` |
| 31 | +- Accent: `--accent`, `--accent-soft`, `--accent-glow` |
| 32 | +- Brand: `--azure`, `--aws` |
| 33 | + |
| 34 | +Dark accent is the fluorescent `#00ff88`; light accent is the muted `#00805a`. The two are intentionally non-identical — bright green on white reads as highlighter, muted green on black has no presence. Both still read as "Veeam green" emotionally. |
| 35 | + |
| 36 | +### Typography |
| 37 | + |
| 38 | +- `Space Grotesk` (weights 500, 700) for headlines and product nouns. |
| 39 | +- `JetBrains Mono` (weights 400, 500, 700) for body text, UI labels, status strip, popup metadata, and coordinate readouts. |
| 40 | + |
| 41 | +Loaded once via Google Fonts CDN with `display=swap` and a system-monospace fallback chain. The mono-first body type is the single biggest visual differentiator from the previous Tailwind-default look. |
| 42 | + |
| 43 | +### Component anatomy |
| 44 | + |
| 45 | +- Header: brand mark (pulsing dot) + live "status strip" telemetry (`Online · Regions X/Y · Providers 02 · Services 05`) + unified `.ctl` control primitive (search, selects, multi-select, icon buttons share one base class with `.ctl-*` modifiers). |
| 46 | +- Map: edge-to-edge with a tokenized `.panel-legend` and tokenized Leaflet zoom/attribution chrome. No rounded corners, no green glow. |
| 47 | +- Popup: one row per service (no edition-row duplication); each row carries a distinct monochrome service icon in `--accent` — the icon's presence signals "available", its shape signals which service; edition pills aligned right; vault edition pills hover-disclose commercial limits per [ADR-005](ADR-005-custom-css-tooltip-pattern.md); official Veeam service names per [ADR-003](ADR-003-official-veeam-service-naming.md). |
| 48 | +- Markers: `L.divIcon` glyphs in provider brand colour (Azure triangle / AWS cube) replacing the prior `L.circleMarker` SVG paths. Keyboard-navigable; the parent marker carries an accessible `alt`. |
| 49 | +- Info panel: same tokens, semantic class names (`.info-section`, `.info-link`, `.info-stat`) — no `bg-slate-*` / `bg-amber-500/10` mix. |
| 50 | + |
| 51 | +## Options Considered |
| 52 | + |
| 53 | +### Option A: Polish the existing design |
| 54 | + |
| 55 | +- Pros: Lowest risk; existing tests stay green; small diffs. |
| 56 | +- Cons: Doesn't address the "indistinguishable from every SaaS dashboard" feedback; the AWS amber/warning collision remains; mobile header still consumes ~15% of vertical space. |
| 57 | + |
| 58 | +### Option B: Cartographic Atlas direction |
| 59 | + |
| 60 | +- Pros: Genuinely distinctive (serif headlines, parchment palette, atlas vibe); zero competitors in this product space. |
| 61 | +- Cons: Stronger departure from Veeam brand voice; the light-mode-first treatment doesn't translate as cleanly to a dark dashboard mode; risk of feeling like a marketing site rather than an operational tool. |
| 62 | + |
| 63 | +### Option C: Mission Control HUD + Workstation light (chosen) |
| 64 | + |
| 65 | +- Pros: Reads as a polished operational tool; live status strip + mono numerals lean into the data-density users care about; the dark/light pair shares one design system rather than being two separate aesthetics; CSS tokens scale to future theme work (e.g. a hypothetical high-contrast mode) without requiring `.theme-x` selectors on every component. |
| 66 | +- Cons: Bigger up-front diff; Google Fonts adds a render-blocking stylesheet (mitigated by `display=swap` and a system fallback chain); some popup rows can wrap due to the longer official service names. |
| 67 | + |
| 68 | +## Consequences |
| 69 | + |
| 70 | +- New UI components MUST read colours/spacing through the existing tokens. Adding a new hardcoded hex value is a smell — either a new token is needed (justify in the PR), or the component should pick the closest existing token. |
| 71 | +- Theme variants are added by extending `:root` / `html.light` (and potentially a future `html.high-contrast`, `html.print`, etc.). Components should NOT branch on `html.light` directly except where light mode genuinely needs a different *structure* (e.g. drop-shadow vs glow), and even then prefer adjusting tokens. |
| 72 | +- Typography is a brand-level choice. If a future redesign wants different fonts, this ADR is the document to supersede; doing it piecemeal will fragment the system. |
| 73 | +- The accent green is reserved for "live / active / available / focus". Don't use it as decoration. The provider blue/orange tokens are reserved for provider identity. Don't repurpose them. |
| 74 | +- Each service registered in `serviceDisplayNames` MUST also register a corresponding inline SVG in `serviceIcons` (in `layouts/index.html`). Icons are simple monochrome geometric shapes that render crisply at 14px, using `stroke="currentColor"` so they inherit `--accent`. The shape carries service identity; the colour carries the "available" semantic. A service without an icon falls back to a generic checkmark — acceptable as a defensive default, not as a long-lived state. |
| 75 | +- Metadata text tokens (`--text-dim`, `--text-mute`) carry a hard floor: both MUST clear WCAG AA against the popup background (`--bg-elev`) at the 9–10px sizes used in the popup header and coordinate readouts. This UI is regularly shown on projectors and screen-shared over compressed video; a token value that looks crisp on the design machine but fades on lossy displays violates the brief. Tune the values, not the font sizes, if a future contrast regression appears. |
| 76 | +- Marker primitive: `L.divIcon` is the contract. Reverting to `L.circleMarker` would re-introduce the accessibility regression (no `keyboard: true`, no `alt`) and break the test selectors that target `.leaflet-marker-icon .map-marker-dot`. |
| 77 | +- Cluster icons (`.cluster-small`, `.cluster-medium`, `.cluster-large`) were deliberately left on the old colour system in this redesign — a known follow-up, resolved by [ADR-006](ADR-006-cluster-marker-visual-treatment.md). The resolution re-tokenises clusters to `var(--accent)` only; per-provider colouring at cluster level (`--azure` / `--aws`) was considered and rejected because aggregated provider colouring destroyed the cluster/marker visual hierarchy — see ADR-006 §Options Considered. |
| 78 | +- Hugo's HTML minifier strips quotes around single-token attribute values. Smoke tests using `curl | grep` should account for this (`class=hud`, not `class="hud"`). |
| 79 | + |
| 80 | +## Links |
| 81 | + |
| 82 | +- [PR #103](https://github.com/comnam90/veeam-data-cloud-services-map/pull/103) — the redesign that introduced this aesthetic. |
| 83 | +- Implementation plan: `plans/mission-control-redesign/plan.md` |
| 84 | +- [ADR-003](ADR-003-official-veeam-service-naming.md) — service-naming convention used inside the popup component defined by this ADR. |
| 85 | +- UX review and mockups (session output): the brainstorming explored Mission Control and Cartographic Atlas directions at desktop + mobile, both themes, before this aesthetic was chosen. |
0 commit comments