# Luxar Viewer UI Design Guide **This document is the authoritative ground truth for the Luxar viewer's visual design.** Every new UI surface must follow it; every deliberate deviation must be documented here. When code and this guide disagree, one of them is wrong — fix the code or fix the guide in the same PR, never let them drift silently. - Scope: the TypeScript viewer (`packages/luxar-viewer`) — panels, overlays, dialogs, the control rail, and every widget drawn over the WebGL canvas. - Not in scope: console/log output (see `CONSOLE_OUTPUT_STYLE.md`), code comments (see `JSDOC_STYLE_GUIDE.md`), rendered scene content. - Verified against the codebase: 2026-08-11. - Everything here describes the tree **as it stands**. Where a rule's implementation only exists in an open PR, it is marked **(pending #N)** — never write a claim in the present tense against an unmerged change; drop the marker when that PR lands. --- ## 1. Design philosophy — the "quiet instrument" Luxar's UI language is called the **quiet instrument**: the scientific data is the interface, and the chrome is a precision instrument that recedes until needed. Seven principles govern every surface: 1. **The data is the interface.** Panels are translucent glass over the live canvas; idle chrome fades (the control rail rests at `opacity: 0.55`, `0.12` when collapsed, `0` in fullscreen — waking on hover/movement). Never occlude data with opaque decoration. 2. **One material.** Every floating panel is built from the same surface recipe (§7.1) so the whole UI reads as a single physical material. Do not invent per-panel chrome. 3. **Color is semantics, never decoration.** Green means healthy, amber means warning, red means error, blue `highlight` means interactive/selected. Identity tints are banned (a points-count is not "points-colored"; it is neutral, and dimmed at zero). See §6. 4. **Precision typography.** Metric values are mono + `tabular-nums` so they tick without jitter; labels are a 10px letterspaced micro-voice; hierarchy comes from weight/brightness, not size explosions. See §8. 5. **Stroke iconography.** Line icons drawn with `currentColor` strokes — never emoji, never filled clip-art. See §9. 6. **Motion is acknowledgment, not spectacle.** One-shot staggered reveals, fast fades, transform-only entry pops. Everything honors `prefers-reduced-motion` (WCAG 2.3.3). See §10. 7. **Keyboard is first-class.** Focus-visible rings, real `` chips in help text, arrow-key navigation in lists, Escape always closes the topmost surface. The reference implementations of this language are the **control rail** (`src/ui/control-rail.ts` + `src/styles/components/control-rail.css`), the **data-loading monitor** (`src/styles/components/data-loading-monitor.css`, whose end-of-file "Refinement layer" block is the original manifesto), the **help overlay**, and — for the modal tier specifically — the **dataset browser** (`src/ui/dataset-browser.ts` + `styles/components/dataset-browser.css`). --- ## 2. Where the truth lives | Concern | Source of truth | | --- | --- | | Design tokens (`--luxar-*`) | `src/themes/theme-manager.ts` → `themeToCSSVariables()` — a hand-written flat map; nothing is algorithmically derived | | Token values per theme | `src/themes/themes/{dark,light,frosted-glass,liquid-glass}.theme.ts` | | Token schema | `src/themes/types.ts` (`Theme` interface) | | Component styles | `src/styles/components/*.css` — one file per UI surface | | Custom GUI library styles | `src/ui/gui/styles/{gui,controller,folder}.css` | | Glass theme overrides | `src/styles/themes/{frosted-glass,liquid-glass}.css` | | Liquid-glass SVG filter | `src/themes/glass-filters.ts` (its `defaultGlassParams` are authoritative — CSS comments describing them have historically gone stale) | | Rail/panel icons | `src/ui/control-rail/icons.ts` (`RAIL_ICONS`) | | Monitor icons | `src/ui/data-loading-monitor/templates/primitives.ts` (`MONITOR_ICONS`) | | Dataset-browser icons | `src/ui/dataset-browser/icons.ts` (`BROWSER_ICONS`) | | Shared panel header/close recipes | `src/styles/base/utilities.css` (`.luxar-panel-header`, `.luxar-panel-close`; #1508 adds `.luxar-panel-filter`, `.luxar-panel-pop`) | | Shared context-menu widget (pending #1508) | `src/ui/overlay-widgets/context-menu.ts` + `styles/components/context-menu.css` | | Modal focus trap | `src/ui/help-overlay/focus-trap.ts` (`trapFocus`) — shared by help overlay and error overlay | | Native ``/`` chrome lives in `select-menu.css` using the opaque `--luxar-menu-*` tokens. A new `