1fanwang opened a new issue, #66323: URL: https://github.com/apache/airflow/issues/66323
### Description `failed` and `upstream_failed` task state colors are visually too close in the default Airflow 3 UI palette: ΔE2000 = 6.2 in normal vision (below the ΔE>10 "easily distinguishable" threshold), and ΔE2000 = 0.4 under deuteranopia (effectively identical pixels: `rgb(147,130,0)` vs `rgb(148,131,0)`). In a Grid view with mixed task states, even a normal-vision user can't reliably tell a real failure from an upstream-cascaded failure. This is the concrete, objectively-fixable regression. It also surfaces a broader colorblind gap that the project hasn't addressed in Airflow 3 — but the broader story can be addressed incrementally; nothing in this issue requires an overhaul. ### Use case / motivation #### The immediate fix State→color in [`airflow-core/src/airflow/ui/src/theme.ts`](https://github.com/apache/airflow/blob/main/airflow-core/src/airflow/ui/src/theme.ts) (semantic tokens block): ``` failed → red.600 oklch(0.577 0.245 27.325) upstream_failed → orange.600 oklch(0.5902 0.198 35.93) ``` Hues 27.325° vs 35.93° (~8.6° apart) at near-identical lightness produce ΔE 6.2 in normal vision. A one-line change to use `amber` (hue ~58°) for `upstream_failed` fixes the regression without breaking any other pair's normal-vision distinguishability: | pair (after `amber` swap) | ΔE normal vision | ΔE deuteranopia | |---|---:|---:| | `failed` vs `upstream_failed` | 6.2 → **20.9** | 0.4 → 8.3 | | `upstream_failed` vs `up_for_retry` | 24.8 → 10.8 | 9.1 → 1.2 | `upstream_failed` vs `up_for_retry` drops to ΔE 10.8 (still above 10 in normal vision), so the immediate fix doesn't introduce a new normal-vision regression. The deutan collapse there is structural — see "broader gap" below. I'll open a draft PR alongside this issue with the one-line `amber` candidate and the full simulation table so the OKLCH choice can be discussed concretely. #### The broader colorblind gap Eleven state pairs collapse below ΔE=10 under deuteranopia simulation (Brettel/Machado, severity 1.0). The `failed`/`upstream_failed` regression above is the only one that fails in normal vision, but ~5–8% of male users see a wider problem. The architecturally-correct shape is **per-user theme preference**, not a deployment-level toggle. A shared multi-tenant Airflow webserver has users with different vision profiles; whichever palette an admin picks in `[api].theme`, some users on the same cluster are left out. This matches what every major web-app precedent ships: - **GitHub** ships four CB theme variants (`light_colorblind`, `dark_colorblind` for deuteranopia/protanopia, plus `light_tritanopia`, `dark_tritanopia`), selectable per-user in account settings. - **Grafana** themes are per-user; "user preferences take precedence over team, organization, and Grafana default preferences." - **macOS / Windows** color filters are per-user OS settings. The frameworks themselves don't help: **Chakra UI v3** (Airflow's framework), **MUI**, and **Ant Design** all provide override mechanisms but ship no curated CB palette. The curated values come from color science — **Okabe-Ito** (Okabe & Ito 2008) and **Wong** (Nature Methods 2011) are the standard references; **matplotlib** and **seaborn** ship `"colorblind"` palettes derived from these. #### Direction (incremental, not an overhaul) Three changes that stack, smallest first. Only the first is in this issue's PR. 1. **(this PR — draft)** Fix the `failed` vs `upstream_failed` regression. One-line OKLCH/family change in `theme.ts`. Benefits everyone, not gated on customization story. 2. **(separate PR, follows #64232 pattern)** Extend `ThemeColors` to allow state-color tokens (`red`, `green`, `orange`, `amber`, `yellow`, `pink`, `cyan`, `purple`, `violet`, `sky`, `blue`, `slate`, `stone`, `zinc`). Mechanical: add fields to the Pydantic model, extend the validator, regenerate openapi types/schemas. Restores config-side parity with Airflow 2 `STATE_COLORS` in the new theme shape. 3. **(future, AIP-scoped)** Per-user theme preference, with documented Okabe-Ito-derived presets — `default`, `deuteranopia-safe`, `protanopia-safe`, `tritanopia-safe`, `custom`. Persists to user profile. Picks up the intent stated by maintainers in #43054 and #55644 ("in 3.2 I want to add a theme config where a user can customize all the colors as they wish"). This is the architecturally-correct destination but is the bigger lift; flagging it here so the increments above land in a coherent direction. It's also the colorblind/contrast slice of accessibility work that the project does not currently track formally — there's no `a11y`/`accessibility` label, no AIP, and adjacent fixes have been one-off (#57455 nav focus, #43365 table sort, #54587 open for ARIA/lang). Whether an umbrella accessibility track is worth opening is a separate question for maintainers. ### Related issues Theme-customization track (precedent for `[api].theme` extension): - #64232 (merged 3.2.0) — added `gray`/`black`/`white` tokens. - #64552 (merged 3.2.1) — made `tokens` optional. Earlier colorblind work (closed without resolving the Airflow 3 default palette): - #43054 (AIP-38 colorblind palette) — closed when state icons landed. - #55644 (running cyan vs lime) — closed; thread captured intent for a user-customizable theme config. - #18391, #13114, #7843 — earlier reports of the same root cause, closed in favor of #43054. Adjacent accessibility work (one-off, no formal track): - #57455 (merged 3.1.2) — global nav focus/tabbing. - #43365 (merged) — table sort + zIndex. - #54587 (open) — ARIA/lang/iframe sandbox. ### Are you willing to submit a PR? - [x] Yes I am willing to submit a PR! Draft PR coming for the fix in #1. The `ThemeColors` extension in #2 follows the #64232 pattern exactly; happy to open it as a follow-up once the direction here is confirmed. The per-user preference in #3 is bigger and likely needs an AIP — happy to draft if maintainers want it. ### Code of Conduct - [x] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
