Skip to main content

Design system

Scope: what a contributor needs to produce on-brand product UI — posture, tokens, and the rules that get violated. The full token block (exact component specs: buttons by size, inputs, badges, splash loader) lives in DESIGN.md at the monorepo root; it moves with the B-022 cutover. Governs product UI only — the optoapp.link marketing site follows its own editorial voice.

Living style guide: the portal's dev-only /ref-page route — ungated, not linked from product UI (optolink-portal/src/app/ref-page/). Update it in place after DESIGN.md edits; never create a parallel version. Its theme toggle rides the app-wide ThemeProvider (dark is the app default since v2.2.0) and persists in localStorage["theme"] — toggle back to Dark after light-mode checks or the next session starts light.

Portal paths source-checked against optolink-portal @ 7d31d45, 2026-10-06 (src/app/ref-page/ exists). The dark-default/theme-persistence facts are recorded — re-verify on next use.

Posture — "The Instrument Panel"​

The portal is operational infrastructure UI, not a marketing surface: state readable at a glance, density and precision over decoration. Dark mode is the default. Warm-neutral base (cream #F7F5F1 / ink #17242B), not stark white/black. Nothing here licenses gradients, glassmorphism, motion flourishes, or marketing copy inside the product.

Color tokens​

TokenHexRole
primary (Deep Teal)#004F70The ONLY "primary action" color — actions, active nav, links; never decorative
secondary (Electric Cyan)#01B8CAFocus rings, info accents — never small text (~2:1 on cream; use info-text #0E6B77)
accent (Precision Orange)#FF7C2BOne key CTA per screen max, or critical confirmations
cream / ink#F7F5F1 / #17242BThe warm neutral pair (light bg/text ↔ inverted in dark)
neutrals 50–900cream→ink rampBorders, muted text, surfaces — never a gray outside the scale
success / warning / error#2E9E5B / #D9962E / #D9483EEach with -bg (soft fill) + AA-safe -text variant
  • One Orange Rule: at most one orange element per screen; if two want it, the hierarchy is wrong. Warning amber ≠ orange: amber = "needs attention", orange = "act here".
  • Semantic colors split hue (icons/dots/borders) from -text (the only color for text on the soft -bg). Dark mode lightens the -text variants.
  • Charts use the fixed chart-1…5 sequence (teal, cyan, green, amber, taupe); never reassign semantically.
  • Dark mode: dark-bg #14201F, dark-surface #1C2A29, text = cream, primary brightens to #3FA9CC.

Typography​

One family: Geist Variable (self-hosted, fontsource) — hierarchy by weight (400/500/600), no second display face; do not style for "General Sans" (the licensed-swap intent) until the swap lands. Data-is-Mono Rule: literal data a user scans or copies (short codes, keys, hashes, tokens) is JetBrains Mono — prose and labels are Geist; there is no third category.

Scale: display 2rem/600 · h1 1.5rem · h2 1.25rem · h3 1.0625rem (all 600) · body 0.9375rem/400 · body-sm + label 0.8125rem · caption 0.75rem. Body text max 65–75ch. Avoid ALL-CAPS tracked labels and accented words in headings.

Shape, space, depth​

  • Radius tiers by role, never one everywhere: 6px controls (buttons, inputs, table rows) · 10px containers (cards, panels, modals) · 14px top-level (page-level panels, empty states). rounded-full only for circles and badge pills — buttons never read as pills.
  • Spacing: 8px scale (4/8/12/16/24/32/48/64); tighter rhythm in tables, looser in forms; max 720px form/detail width.
  • Two Shadows Rule: Level 1 (cards, barely visible) + Level 2 (modals/ dropdowns) only — a surface needing more depth is a layering bug, not a third shadow.
  • Every interactive element: hover, focus-visible (cyan ring), disabled. Data surfaces (tables/lists) additionally require loading, empty, and error states with real copy — no generic placeholder text.

Logo & favicon​

Three lockups × three treatments, source SVGs in optolink-portal/public/brand/: wordmark (default, where width allows) · brandmark (icon only: collapsed sidebar, favicon, avatars) · "by Optomatica" sub-lockup (legal/ about contexts only). Treatments: color on light surfaces, white on dark/solid fills, black only for single-color print contexts.

The logo's embedded navy/cyan/orange are locked, not tokens — they sit close to but not exactly on primary/secondary/accent by design. Never recolor logo paths to match tokens, never pull logo hexes into the UI palette, never recreate the mark in CSS/text. Minimums: wordmark 96px wide, brandmark 20px; clear space ≥ the brandmark's corner-notch depth. The favicon is the brandmark (public/favicon.svg).