← All docs
Ideas

Themes and UI conventions

> Both tracks. Failures on this rule are the most common source of "looks weird in light mode" bug reports.

The rule

Use CSS custom properties from themes.ts. No literal hex colours in component code or CSS files. XOSC ships 10 themes plus a custom-theme editor; everything has to recompose.

The variables you'll use

--bg / --surface / --surface-2 / --surface-3
--border / --border-strong
--text / --text-dim / --text-mute
--accent / --accent-2 / --accent-glow
--ok / --warn / --err
--input-shadow
--font-mono
--radius-sm

For node-body accents, use node.color (the per-node user-pickable hex from ColorPicker). That's the only place a literal hex appears — and it lives in the data, not the code.

Components to reuse, not rewrite

  • TextInput (src/components/ui/Input.tsx)
  • Select (src/components/ui/Select.tsx)
  • Slider (src/components/ui/Slider.tsx)
  • Toggle (src/components/ui/Toggle.tsx)
  • Button (src/components/ui/Button.tsx) — variants primary, subtle, ghost, danger.
  • IconButton (src/components/ui/IconButton.tsx)
  • Modal (src/components/ui/Modal.tsx) — already used for SettingsPanel and config import/export.
  • HostPicker (src/components/ui/HostPicker.tsx) — useful for the Art-Net inbound IP field; plumb through the existing peer bus if mDNS catches anything.
  • ColorPicker (src/features/inspector/ColorPicker.tsx) — for node accent colours.
For pad swatches, don't use <input type="color"> directly. The existing pattern in CustomThemeEditor.tsx (a styled swatch button that opens a hidden native color input on click) is the rule. Extract it into src/components/ui/HexInput.tsx once both tracks need it; until then, copy.

Iconography

Lucide. Existing imports already in Topbar.tsx. New icons:

  • Lightbulbdmx-out topbar button
  • LightbulbOff or RadioReceiverdmx-in topbar button
  • Zap — pad / sequence
  • Clock3 or Timer — clock bar
  • LayoutGrid — Pads view toggle
  • Workflow — Graph view toggle (current default)

Pad visual conventions

A pad is a card-like cell. Reference the existing gnode styles for cohesion (src/features/mapping/graph-canvas.css):

  • border-top: 3px solid var(--pad-color)--pad-color is the sequence's swatch hex passed as a CSS variable.
  • background: var(--surface) (idle), var(--surface-2) (active), with --accent-glow halo when live.
  • Sparkline / cue preview row: thin coloured rectangles, no full gradients.
  • Hot-key key cap rendered as monospace pill (var(--font-mono), var(--surface-3) background).

Diagnostics row visual

Match <SinkRateGauge />: a small gnode-pill with an icon + value + amber/red tint above thresholds. Already proven on existing nodes; copy that look.

Don't do

  • Don't introduce a new "DMX style" CSS file that competes with the theme variables. If you need a new pattern, define it in terms of the variables.
  • Don't import third-party UI libraries (Mantine, MUI, etc.). Every existing component is hand-rolled and tiny; the bundle cost is the point.
  • Don't write inline styles for colour. Inline styles for layout (display, gap, padding) are fine; colour goes through CSS classes that read variables.