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]

Reply via email to