> 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) — variantsprimary,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.
<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:
Lightbulb—dmx-outtopbar buttonLightbulbOfforRadioReceiver—dmx-intopbar buttonZap— pad / sequenceClock3orTimer— clock barLayoutGrid— Pads view toggleWorkflow— 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-coloris the sequence's swatch hex passed as a CSS variable.background: var(--surface)(idle),var(--surface-2)(active), with--accent-glowhalo 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.