8 min read Rocky Elsalaymeh
Status is a word, not a color
A red, amber, green dot fails readers who cannot tell the colors apart, and WCAG says color cannot be the only cue. Here is the status system Team-X v3.4.0 uses instead, and the contrast bugs it shipped before fixing them.
The colored status dot only works if the reader can tell the colors apart. The US National Eye Institute says “About 1 in 12 men have color vision deficiency.” A dashboard that encodes health as red, amber, or green asks those readers to guess.
The accessibility standard closes the loophole. WCAG Success Criterion 1.4.1 requires that “Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.” A bare status dot is color as the only means, by construction.
Team-X, an open-source, local-first desktop app for running AI-agent organizations, reports status as a stencil word in a lamp tile: GO, HOLD, NO-GO, STBY, or EXEC. Color is a second channel, not the carrier. Red is split by form: steady red means live, and blinking red means an unacknowledged condition that needs an answer.
Version v3.4.0, dated 2026-07-11 in the changelog, rebuilt every renderer surface on these rules. This post covers the rules, the standards I borrowed them from, and the defects the sweep shipped before it fixed them.
Why does a colored dot fail as a status?
It puts the whole message in one channel. When that channel fails, the message fails, and nothing on screen says so.
The channel fails for two separate reasons. The first is the reader, which is the eye-institute figure above. The second is the screen. WCAG 1.4.3 sets the floor for text: “The visual presentation of text and images of text has a contrast ratio of at least 4.5:1, except for the following:”
That floor is routinely missed. The 2026 WebAIM Million report states that “Low contrast text, below the WCAG 2 AA thresholds, was found on 83.9% of home pages, an increase from 79.1% in 2025.” A status color sitting on a low-contrast surface fails twice.
A word fixes the first failure outright, because the reader does not need the hue to read GO. It does not fix the second. Text on a bad surface is still bad text, and Team-X shipped exactly that. The defects are listed below.
What did flight decks settle?
They settled that color is assigned, not chosen. 14 CFR 25.1322, the US rule on flightcrew alerting, says visual alert indications must “Conform to the following color convention: (i) Red for warning alert indications. (ii) Amber or yellow for caution alert indications.”
Two details in that rule matter more than the convention itself. The first is paragraph (f): “Use of the colors red, amber, and yellow on the flight deck for functions other than flightcrew alerting must be limited and must not adversely affect flightcrew alerting.” Red is reserved: its use for anything else must be limited.
The second is that the rule does not trust color alone. For displays that cannot show the convention, it requires the maker to “Use visual coding techniques, together with other alerting function elements on the flight deck, to distinguish between warning, caution, and advisory alert indications”. A second channel is written into the regulation.
Process control reached the same place from the other side. The UK Health and Safety Executive’s COMAH control systems guidance says: “The alarms should be processed in such a manner as to avoid operator overload at all times (alarm floods).” That concern is volume rather than hue. It is the reason I do not let a lamp light without a signal behind it.
I took the vocabulary on purpose. The Team-X design system names its reference set as “Apollo-era flight consoles / broadcast master control”, and the rules below follow from that choice.
What does a status word look like in Team-X v3.4.0?
A lamp tile holds one word and one tone. The design system states the rule in four words: “Stencil words, not icons, for status.” The words it lists are GO, HOLD, NO-GO, STBY, EXEC, and ON AIR. The component maps each tone to a style in a few lines of lamp-tile.tsx:
const toneClass: Record<Exclude<LampTone, 'off'>, string> = {
go: 'lamp-go',
hold: 'lamp-hold',
warn: 'lamp-warn',
nogo: 'lamp-nogo',
exec: 'lamp-exec',
armed: 'lamp-armed',
};
The five LED tones, with their form, come from the LED semantics section of DESIGN.md.
| Token | Hex | Meaning | Form |
|---|---|---|---|
--led-go | #41E25E | GO, running, healthy | Steady |
--led-hold | #FFB000 | HOLD, caution, pending | Steady |
--led-warn | #FF4438 | Unacknowledged warning | Blinking 1Hz only |
--led-nogo | #C8453E | NO-GO, fault, failed | Steady |
--led-scope | #58C4BC | Informational, EXEC | Steady |
Every tile carries a required label, so the word is always there. A reader who sees only letters on a dark tile still reads HOLD.
The annunciator rail is the persistent strip of these tiles. DESIGN.md lists eight lamps for it: SYS, ORG, TOKN, BUDG, GGUF, QUE, NET, and MTG. The code at v3.4.0 builds five: QUE, GGUF, BUDG, APPR, and MTG. The derivation file says in its header that it maps five real signals onto lamp tiles, so the document is the part that drifted. I trust the code.
The acknowledge ritual is deliberate. The design system says “a blinking warning tile blinks until clicked, then stays lit until resolved”, and that acknowledgment is “a physical ritual, not a dismissed toast”. A tile re-blinks when its alert instance changes, so a problem that comes back is not silently remembered as acknowledged.
Reduced-motion users get the same information as text. The blink animation is switched off under that setting, and a blinking tile gains the suffix UNACK. The state survives as a word when the motion is gone.
Nothing on the rail lights for show. The design system’s checklist bans “purely decorative LEDs, meters, or lamps”, and the rail’s data layer resolves missing data to a calm lamp rather than a lit one.
Why is steady red not an error?
Because red means live. The rule is one sentence in DESIGN.md: “steady red = LIVE/armed/command; blinking red = a question that demands an answer”. The brand red, #AA2024, is the ON AIR light of the company: it means agents on the clock and money burning.
Steady red means live. Blinking red wants an answer.
Errors need their own signal, and the system did not have one until a design review on 2026-06-16 found the blink-only warn red used for terminal faults across the app, “while the system had no steady fault tone”. The fix, logged the same day, was a separate tone. A failed item that nobody needs to acknowledge sits on NO-GO, and the rule states it plainly: “A failed thing that is not awaiting acknowledgment is NO-GO (steady)”.
I broke with the flight-deck convention here, and the tension is real. 14 CFR 25.1322 assigns red to warnings, and its paragraph (f) limits red for anything else. Team-X spends steady red on a meaning the airplane rule would not. That rule governs flightcrew alerting on airplanes, not desktop software, so it does not bind this app. The cost is that red carries two jobs, and the only thing separating them is form: steady versus blinking. That is why the word and the blink both have to be right.
What did the sweep get wrong?
More than I would like, and the changelog records it. These are the status and keyboard defects listed under Fixed and Added in the v3.4.0 changelog:
| Defect | Measured or observed | Fix |
|---|---|---|
| LED status text on the silver chassis, privacy provider list | About 1.9:1 contrast | Rendered as the recessed display well the design specified; AA restored in both shifts |
| Muted text inside always-dark wells, Day Shift | About 3.1:1 contrast | New --display-fg-mute token, about 5.9:1 on void, applied at 24 in-well sites |
| Sidenav employee status dot | Steady error used the blink-only alert red | Steady faults now carry the NO-GO token |
| Add Provider dialog | Escape did not close it; the scrim blocked the app | Window keydown listener scoped to the open state |
The Day Shift row is the instructive one. The design says displays stay dark in both shifts. Text meant for those wells used the chassis-calibrated silver, which fell to about 3.1:1 in Day Shift, and it took a dedicated token to fix it.
The sidenav row is the dual-form rule failing in plain sight. The rule existed. The review that created NO-GO had already run. One component still used the wrong red, and the sweep found it.
The release also locked one class of accessibility defect behind a test. Radix logged missing dialog descriptions, which the changelog calls an accessibility defect, and a new dialog-a11y-guards test now scans every renderer dialog and sheet and fails if one lacks a description.
Two honest limits. The first: the “no icons for status” rule is not fully kept at this tag. The privacy section still renders a check icon next to the word Allowed. The word is there, so the state is readable without color, but the icon breaks the letter of the rule and I count it as open.
The second: the changelog lists the failures the team found. It is not a clean bill for every screen, and this post does not claim one.
How do you apply this to your own dashboard?
You do not need a flight deck. You need a few rules and the discipline to write them down.
- Print the word next to the dot. If a state is conveyed by hue, add text. That is what WCAG 1.4.1 asks for.
- Give each color one meaning and one form. Write the rule as a sentence, as DESIGN.md does for red. If a color has two meanings, make the form differ.
- Keep a separate tone for settled failures. An alert that needs an answer and a failure that has already landed are different states. Do not paint them the same way.
- Measure contrast in every theme. The 1.9:1 and 3.1:1 failures above were Day Shift failures, not dark-default ones. Check the 4.5:1 floor for status text in each theme you ship.
- Bind every lamp to a signal. Team-X bans lamps and meters that are not data-bound, and the rule is easy to check in review.
The full rule set is in DESIGN.md at the v3.4.0 tag, and the accessibility guide sits beside it. Field Notes lives at /notes/.
Frequently asked questions
Why does Team-X use words instead of colored dots for status?
Team-X, an open-source, local-first desktop app for running AI-agent organizations, renders status as a stencil word in a lamp tile, such as GO, HOLD, or NO-GO. The word carries the meaning and color only supports it, so a reader who cannot tell the hues apart still reads the state. WCAG 1.4.1 asks for exactly this.
What does blinking red mean in Team-X?
In Team-X, steady red means live or armed, and blinking red means an unacknowledged warning that demands an answer. Clicking a blinking lamp on the annunciator rail acknowledges it, and the lamp then stays lit until the condition resolves. A failure nobody needs to acknowledge shows the steady NO-GO tone instead.
What accessibility bugs did the Team-X v3.4.0 design sweep find?
The Team-X v3.4.0 changelog records four: status text on the silver chassis at about 1.9:1 contrast, muted Day Shift display text at about 3.1:1 that was raised to about 5.9:1, a steady error dot that used the blink-only red, and an Add Provider dialog that Escape would not close. All four were fixed in that release.
A status you must decode by hue is a status you can misread.