Skip to content

Design System (Phase 4)

Synced read-only from /home/synnet/mirrors/synvirt-product/docs/modules/web-ux-v2/DESIGN_SYSTEM.md. Edit at the source, not here.

The v2 console ships a tokenised design system that drives every visual decision through CSS variables. Components consume tokens via Tailwind utility classes that resolve to rgb(var(--color-x) / <alpha-value>), so alpha modifiers (bg-accent/10) work without rewriting every component when the palette changes.

File Responsibility
src/styles/tokens.css The chromatic / spatial / motion source of truth. Light + dark + density + reduced-motion variants.
src/styles/reset.css Modern CSS reset on top of Tailwind preflight (form inheritance, custom scrollbars, focus-visible).
src/styles/typography.css Display / heading / mono helpers, tabular-num figures, text-eyebrow.
src/styles/animations.css @keyframes synvirt-* plus reusable utility classes (.status-dot-online, .progress-indeterminate).
src/styles/main.css Imports the four files in order and exposes .surface-card, .surface-inset, .surface-sticky, .kbd-cap.

Tailwind reads the tokens via tailwind.config.ts::theme.extend. Adding a new colour means adding a CSS variable in tokens.css and mapping it under extend.colors so bg-foo / text-foo become valid utilities.

The palette mirrors brand/colors.md (the OKLCH source of truth) translated into RGB triplets so Tailwind’s alpha substitution works.

Token Light Dark
bg #F8FAFD #06090F
bg-2 #F3F5F9 #0A0D14
surface #FFFFFF #10141B
surface-2 #F8FAFD #171B23
surface-3 #EFF2F6 #20242D
surface-4 #E5E8ED #292E38
Token Light Dark
accent #3362E1 #6397FF
accent-2 #3445D7 #88A9FF
accent-3 #3725C6 #AABCFF
accent-soft #E8F2FF #102459
accent-ink #15197C #BED5FF

5-step scale from text (primary) through text-5 (decorative hairlines). text-3 is the de-facto secondary in cards.

ok, warn, danger, info, idle — each carries a soft variant suffixed -soft for tinted backgrounds (bg-ok-soft, text-ok).

Two attribute-driven modes plus a “system” follower:

<html data-theme="light"> <!-- or "dark" -->
<html data-density="comfortable"> <!-- or "dense" -->

The composables in src/composables/ own each attribute:

  • useThememode (light | dark | system), resolved (light | dark), setMode(...), toggle(). Persists in localStorage['synvirt:theme'].
  • useDensitydensity, setDensity(...), toggle(). Persists in localStorage['synvirt:density'].
  • useReducedMotionreduce ref tracking prefers-reduced-motion.

The inline <script> in index.html runs synchronously before paint and applies the saved attributes so the page never flashes the wrong theme on cold load.

comfortable is the default. dense shrinks vertical-rhythm spacing tokens by ~25 % (page padding 24 → 16, input height 40 → 32, etc.). Component visuals don’t change shape — only breathing room.

Token Value
--duration-instant 75ms
--duration-fast 150ms
--duration-normal 200ms
--duration-slow 300ms
--duration-slower 500ms

Easing tokens cover standard, decel, accel, emphasis, and spring. All durations collapse to 1 ms under prefers-reduced-motion: reduce or data-reduced-motion="reduce".

main.css exposes these helpers:

  • .surface-card — bordered + radius + soft shadow card.
  • .surface-card-flush — same but with overflow: hidden for tables.
  • .surface-sticky — translucent pill that sits above scroll content.
  • .surface-inset — recessed sub-card used by the wizard’s per-disk / per-NIC rows.
  • .dashed-frame — dashed-outline placeholder for upload / empty zones.
  • .kbd-cap — monospace key cap for the command palette and shortcut hints.
  • .status-dot-online / .status-dot-offline / .status-dot-warn — animated state indicators.
  • .progress-indeterminate — sliding accent bar for indeterminate progress.
  1. Never write a hex colour in a component. Add a token first.
  2. Never write dark: Tailwind variants — the design tokens carry dark mode automatically. Only the dark variant of a non-token value (e.g. one-off SVG fills) needs a manual override.
  3. Spacing comes from the var(--space-*) scale (4-px base) or the density-aware var(--density-*) tokens. Don’t hard-code padding: 17px.
  4. Animations and transitions reference token-driven durations (duration-fast, duration-normal) so reduced-motion works without per-component @media queries.