UI Color Guide¶
Universal color vocabulary for the DCS gateway web UI. Each color has one semantic meaning across the entire interface. Never reuse a color for a different meaning.
Every value in this guide is verified against the shipped stylesheets in
internal/gateway/static/css/ by make lint-docs-color-guide. If you change a
token in CSS, update this guide in the same commit. The lint fails on any
mismatch, on tokens documented here but absent from CSS, and on semantic
tokens (--state-, --io-, --cat-, --alarm-, --sfc-, --cmd-
families) added to CSS but missing from this guide.
Semantic Domains¶
Process State (ISA-88)¶
Used for equipment and procedural state badges, health indicators, and success/error feedback.
| Semantic | CSS Variable | Color | Hex | Usage |
|---|---|---|---|---|
| Active / Running / Healthy | --state-running |
#22c55e |
Running state (and Starting / Restarting / Resuming transitionals), healthy health indicator, runtime ready, success toasts | |
| Warning / Paused / Stale | --state-paused |
#f59e0b |
Paused/Pausing state, degraded health, stale data, Rejected decisions | |
| Held | --state-held |
#f59e0b |
Held/Holding state (ISA-88). Same amber as paused — the badge text carries the distinction; kept as a separate token so a future re-tint is a single edit | |
| Error / Fault / Stopped | --state-stopped |
#ef4444 |
Stopped/Stopping states, procedural Aborted/Aborting state badges, Failure outcomes, runtime offline, error toasts, validation errors | |
| Abnormal termination (Aborted) | --state-aborted |
#ea580c |
Device/equipment symbol run-state for Aborted — orange, deliberately off the health red so it does not read as an active fault alarm (issue 644) | |
| Idle / Neutral | --state-idle |
#888888 |
Idle state, Resetting, initial/unknown | |
| Complete / Finished | --state-complete |
#60a5fa |
Complete state (blue, distinct from idle gray), Approved decisions, success confirmations, valid-input feedback |
Aborted is colored in two places by design (#644). Procedural state badges
(stateColor(), js/utils.js) keep --state-stopped (red), and the badge
text distinguishes it from Stopped. The device/equipment symbol run-state map
(SYMBOL_STATE_COLORS, js/symbols/renderer.js) uses the dedicated
--state-aborted (orange), because a symbol painted with the reserved health
red would read as a live fault. Moving procedural Aborted off the reserved
token is follow-up work.
I/O Values¶
Used for digital input/output value display, toggle buttons, and device/equipment symbol run-state. A device on/off is a field reading with no health verdict in it (#644, see Device run-state below).
| Semantic | CSS Variable | Color | Hex | Usage |
|---|---|---|---|---|
| Energized / On / True | --io-on |
#22d3ee |
Digital output ON button, digital value display, live/real-time indicators, device symbol running/on | |
| De-energized / Off / False | --io-off |
#888888 |
Digital output OFF button, digital value display, device symbol stopped/off |
--io-off matches the neutral gray (--state-idle) numerically, because
de-energized is a normal operating state. An error accent would be the
wrong claim for it. (--text-muted was bumped to #9a9a9a for AA contrast
in issue 708 and no longer shares the value.)
Device run-state¶
A device/equipment run-state (a pump running, a valve open, a motor stopped) is a field reading. It is not a health verdict. A stopped pump is de-energized and still healthy. Painting it with the reserved health green/red would erode the rule that green/red mean a health verdict, exactly where operators read it most (#644). Device run-state therefore uses the neutral palette:
| Run-state | Token | Rationale |
|---|---|---|
| running / on (energized) | --io-on |
live / energized, like any digital-ON value |
| stopped / off (de-energized) | --io-off |
a normal operating state (never a fault) |
| aborted (abnormal end) | --state-aborted |
abnormal/attention, but not the health red |
This applies to the symbol run-state map (SYMBOL_STATE_COLORS,
js/symbols/renderer.js), the faceplate boolean status dot (.hmi-fp-status-dot.on),
and the equipment toggle (.hmi-equip-toggle.on). Genuine health/alarm signals
(.hmi-fp-status-dot.alarm, control-module health banner) keep the reserved red.
Command Buttons¶
All ISA-88 command buttons share a single amber: commands are operator actions that demand attention. They are never green ("go") or red ("danger"). Green and red are reserved for health semantics (rules 1–2 below).
| Semantic | CSS Variables | Color | Hex | Usage |
|---|---|---|---|---|
| ISA-88 command (any) | --cmd-start, --cmd-stop, --cmd-hold, --cmd-pause, --cmd-resume, --cmd-restart, --cmd-abort, --cmd-reset |
#f59e0b |
cmd-btn border/text, faceplate command buttons, btn-danger hover |
Per-command tokens exist so an individual command could be re-tinted in one edit, but they are deliberately identical today.
Alarm Severity¶
Used exclusively in alarm banners, badges, and diagnostic severity indicators.
| Semantic | CSS Variable | Color | Hex | Usage |
|---|---|---|---|---|
| Critical | --alarm-critical |
#dc2626 |
Critical alarms | |
| High | --alarm-high |
#ea580c |
High severity alarms | |
| Medium | --alarm-medium |
#d97706 |
Medium severity alarms (deliberately distinct from High — issue 694) | |
| Low | --alarm-low |
#707070 |
Low severity alarms | |
| Alarm strip wash | --alarm-strip-bg |
rgba(245, 158, 11, 0.15) |
Translucent amber backdrop of the inline faceplate alarm strip | |
| Alarm strip border | --alarm-strip-border |
rgba(245, 158, 11, 0.45) |
Border of the inline faceplate alarm strip |
The values intentionally coincide with the state palette (critical = stopped red, high/medium = paused amber, low = idle gray), but the tokens are separate: alarm severity is its own domain (rule 9), and the coincidence is not a contract.
NE 107 Device Health¶
Used exclusively for an instrument's NE 107 self-diagnosis (ADR 0043): the
faceplate DEVICE HEALTH row and any device-health badge. The colors are the
NAMUR-standardized vocabulary operators know from vendor device-management
screens. Normal and Failure carry no dedicated tokens. They render with
the reserved health pair (--state-running / --state-stopped), because a
device-health verdict is precisely what that reservation exists for. The
three tokens below are reserved to NE 107 surfaces only. In particular they
are not substitutes for --state-aborted (abnormal termination) or
--state-complete (done/released), whose meanings they deliberately avoid
overloading.
| Semantic | CSS Variable | Color | Hex | Usage |
|---|---|---|---|---|
| Check Function (C) | --ne107-check |
#e8964a |
Work in progress on the device; output temporarily invalid | |
| Out of Specification (S) | --ne107-offspec |
#d4b842 |
Operating outside specified conditions | |
| Maintenance Required (M) | --ne107-maintenance |
#6ea8dc |
Reading still valid; wear demands attention |
Unknown renders in the neutral muted gray with its reason text. It is an
absence of a verdict. A sixth color would claim one.
Categorical Palette¶
Used for type/kind identification in diagrams, editors, and palettes. These colors never carry state, health, or severity meaning. They identify what something is, not how it's doing.
| CSS Variable | Color | Hex | Sample Assignments |
|---|---|---|---|
--cat-rose |
#fb7185 |
Enterprise (hierarchy), Math blocks (FB) | |
--cat-lime |
#84cc16 |
Area (hierarchy), Control blocks (FB) | |
--cat-purple |
#a855f7 |
ProcessCell (hierarchy), UnitProcedure (proc), Comparison blocks (FB) | |
--cat-violet |
#7c3aed |
Unit (hierarchy), Timer blocks (FB) | |
--cat-pink |
#ec4899 |
Reserved for future use | |
--cat-teal |
#14b8a6 |
ControlModule (hierarchy), Phase (proc), I/O blocks (FB), Composite blocks (FB) | |
--cat-indigo |
#6366f1 |
Site (hierarchy), Operation (proc), Counter blocks (FB) |
SFC Chart Progress¶
Used by the live Sequential Function Chart viewer (HMI Phase / Operation / UnitProcedure / Procedure / Batch drill-down). Three tiers express temporal position (future, past, present) without re-using state colors. ISA-88 nuance (Running vs Holding vs Aborting) is conveyed by the adjacent state pill alone.
| CSS Variable | Color | Hex | Usage |
|---|---|---|---|
--sfc-active |
#22d3ee |
Active step border + in-flight TRUE transition bar (the "present" element). Defined as an alias of --io-on |
|
--sfc-active-fill |
rgba(34, 211, 238, 0.12) |
Active step translucent fill | |
--sfc-history |
#888888 |
Completed step border + already-fired transition bars (the "past" trail) | |
--sfc-history-line |
#888888 |
Travelled wires (line weight 2.5 px distinguishes from untravelled) | |
--sfc-future-line |
#3a3a3a |
Untravelled wires at default 1.5 px (matches --border) |
--sfc-active is declared as var(--io-on) in CSS: the active step is a
live/real-time indicator, which is cyan's job (rule 5). The history grays
match --state-idle and the future-line matches --border numerically.
Keeping each value defined once in base.css is intentional. A future
tweak (warmer history, dimmer untravelled) is then a single edit.
Diagram Surfaces¶
Shared node/wire palette for the FB, SFC, LD, and procedure-flow SVG
diagrams and editors. Node boxes sit between --bg-card and --border
in visual depth. The diagram muted gray is dimmer than --text-muted
on purpose: labels on nodes compete with wire routing, a fight page
text never has.
| CSS Variable | Color | Hex | Usage |
|---|---|---|---|
--node-fill |
#333333 |
Diagram node/block fill (FB blocks, SFC steps) | |
--node-fill-error |
#5f1e1e |
Node fill when the network/chart is in error | |
--node-stroke |
#444444 |
Node borders, connector lines, flow arrows | |
--diagram-muted |
#666666 |
LD rails, address/category labels on nodes | |
--wire-data-hl |
#5eead4 |
Data-wire hover highlight (lighter --cat-teal) |
|
--wire-event-hl |
#bbbbbb |
Event-wire hover highlight (lighter --accent) |
|
--canvas-grid |
rgba(136, 136, 136, 0.05) |
Editor-canvas background grid pattern |
Chart Series¶
Multi-pen line-chart palette (HMI trend, historian). Series identity
only. A pen carries no state, health, or severity meaning. The set
deliberately avoids the reserved health green/red
(--state-running/--state-stopped) so a pen never reads as a verdict.
Emerald and rose stand in for them.
| CSS Variable | Color | Hex |
|---|---|---|
--chart-1 |
#60a5fa |
|
--chart-2 |
#f59e0b |
|
--chart-3 |
#34d399 |
|
--chart-4 |
#fb7185 |
|
--chart-5 |
#a78bfa |
|
--chart-6 |
#f472b6 |
|
--chart-7 |
#14b8a6 |
|
--chart-8 |
#fb923c |
--chart-limit (amber) paints the high/low deviation reference lines on
trend charts. It never paints a series.
I/O Rack Grays¶
Module-type coding for the I/O rack sketch on the controller detail page. The rack is structural and stays out of the way: lighter shades for analog, darker for digital, outputs slightly warmer.
| CSS Variable | Color | Hex | Module type |
|---|---|---|---|
--rack-ai |
#909090 |
AI | |
--rack-ao |
#989898 |
AO | |
--rack-do |
#787878 |
DO | |
--rack-di |
#707070 |
DI |
UI Chrome (non-semantic)¶
Neutral colors for layout, text, borders. No state meaning.
| CSS Variable | Color | Hex | Usage |
|---|---|---|---|
--bg-primary |
#1a1a1a |
Main background | |
--bg-secondary |
#222222 |
Sidebar, topbar | |
--bg-card |
#2a2a2a |
Cards, modals | |
--bg-input |
#141414 |
Input fields | |
--border |
#3a3a3a |
Borders, dividers | |
--text-primary |
#e2e2e2 |
Primary text | |
--text-secondary |
#aaaaaa |
Secondary text | |
--text-muted |
#9a9a9a |
Muted text, data port labels | |
--accent-bg |
#555555 |
Button backgrounds |
Rules¶
-
Green (
--state-running) = process active / healthy. Never use for I/O on/off, device run-state (use--io-on), command buttons, or category identification. -
Red (
--state-stopped) = error / fault / needs attention. Procedural Aborted state badges share this red (rule 8). Never use for I/O on/off, device run-state, command buttons, neutral markers, or category identification. -
Amber (
--state-paused) = warning / attention needed. Also used for stale data readings (data freshness IS a warning), held state, high/medium alarms, and all command buttons. Never for category identification. -
Held (
--state-held) shares amber with paused. The badge text carries the ISA-88 distinction. It stays a separate token so held could be re-tinted in a single edit. Do not introduce a distinct held color (e.g. orange) without updating this guide and the CSS together. -
Cyan (
--io-on) = I/O energized / live. Digital value display, toggle buttons, and live/real-time indicators. The SFC active step (--sfc-active) aliases it. -
Gray (
--io-off) = I/O de-energized. A normal operating state (never an error). Numerically the neutral gray, but use the I/O token in I/O contexts. -
Blue (
--state-complete) = finished. Distinct from idle gray. -
Aborted: red for procedural state,
--state-aborted(orange) for device symbols. Procedural Aborted/Aborting badges render with--state-stoppedand rely on the badge text to distinguish them from Stopped. Device/equipment symbol run-state uses the dedicated--state-abortedso an aborted symbol does not read as a live fault alarm (#644). Do not paint device run-state with the reserved health red. -
Alarm colors are reserved for alarm severity. Don't reuse
--alarm-criticalfor general errors (use--state-stopped) or for category identification (use--cat-rose). -
Categorical colors (
--cat-*) are for type identification only. They identify resource types, procedure levels, FB block categories, and port types. They never indicate state, health, severity, or data quality. -
Module Rack slots use neutral grays. The rack diagram is structural, and module type is conveyed by text labels alone. Slightly different gray shades provide subtle grouping (lighter for analog, darker for digital) without competing for attention with live I/O values, state badges, or alarms.
-
Diff colors (red/green for old/new values) are an exception. The red-removed / green-added convention in audit trail field changes is universally understood and appears only in diff context.
-
Syntax highlighting (ST editor) is a separate domain with its own palette.
-
Always use CSS variables where possible. Canvas and SVG-string renderers cannot read CSS custom properties. There, consume brand neutrals via the generated
BRANDglobal (js/brand-tokens.js, regenerated bymake brand). For product-semantic colors (states, severities, trend series), use hex values from this guide and keepvarMap/SYMBOL_STATE_COLORSobjects in sync.make lint-docs-color-guideverifies this guide against the shipped CSS, andmake lint-ui-tokensverifies everyvar()reference in the gateway static files resolves to a defined custom property. An undefined token reference is invalid at computed-value time and silently falls back to inherit/initial. The check is shell-aware (issue 694): a token only counts as defined for the shells whose<link>tags load its stylesheet, and quoted token names in JS ('--alarm-critical') are checked too, since dynamically constructedvar(${...})references are invisible to the literal scan. Similarly,make lint-js-classesverifies every class emitted in JS-generated markup resolves to a stylesheet selector or JS hook. An undefined class silently renders unstyled (colorless badges, #693). -
SFC chart progress (
--sfc-*) is its own vocabulary. Use cyan-active / history-gray / future-dim-gray for the live chart's three-tier hierarchy. Do not paint active steps green/amber/red based on ISA-88 state. The state pill carries that nuance. Do not animate (no pulse, blink, or breathing). The lone cyan element carries the liveness cue.
Quick Reference¶
PROCESS STATE (how is it doing?)
Green #22c55e --state-running active / healthy
Amber #f59e0b --state-paused paused / warning / stale data
Amber #f59e0b --state-held held (ISA-88) — badge text distinguishes from paused
Red #ef4444 --state-stopped stopped / error / fault / procedural aborted
Gray #888888 --state-idle idle / neutral
Blue #60a5fa --state-complete complete / finished
I/O VALUE (what is its electrical state?)
Cyan #22d3ee --io-on energized / on / true / live
Gray #888888 --io-off de-energized / off / false
DEVICE RUN-STATE (field reading, not a health verdict — #644)
Cyan #22d3ee --io-on running / on (energized)
Gray #888888 --io-off stopped / off (de-energized)
Orange #ea580c --state-aborted aborted (abnormal termination)
COMMANDS (operator actions — uniformly amber)
Amber #f59e0b --cmd-start --cmd-stop --cmd-hold --cmd-pause
Amber #f59e0b --cmd-resume --cmd-restart --cmd-abort --cmd-reset
CATEGORY (what kind of thing is it?)
Rose #fb7185 --cat-rose Enterprise, Math blocks
Lime #84cc16 --cat-lime Area, Control blocks
Purple #a855f7 --cat-purple ProcessCell, UnitProcedure
Violet #7c3aed --cat-violet Unit, Timers
Pink #ec4899 --cat-pink Reserved for future use
Teal #14b8a6 --cat-teal ControlModule, Phase, I/O blocks
Indigo #6366f1 --cat-indigo Site, Operation, Counters
SFC CHART PROGRESS (where in the chart's timeline?)
Cyan #22d3ee --sfc-active active step / in-flight TRUE transition (present; aliases --io-on)
Gray #888888 --sfc-history completed step border / fired transition bar (past)
Gray #888888 --sfc-history-line travelled wires (past, 2.5 px)
Gray #3a3a3a --sfc-future-line untravelled wires (future, 1.5 px)
MODULE RACK (structural — type shown by text label, not color)
Grays #707070–#b0b0b0 Lighter = analog, darker = digital
ALARM SEVERITY (how urgent is it?)
Red #dc2626 --alarm-critical
Orange #ea580c --alarm-high
Amber #d97706 --alarm-medium
Gray #707070 --alarm-low