Skip to content

fix(a11y): WCAG 1.4.1 — add non-color visual cues to status indicators#39233

Open
Aitema-gmbh wants to merge 3 commits intoapache:masterfrom
Aitema-gmbh:fix/wcag-1.4.1-use-of-color
Open

fix(a11y): WCAG 1.4.1 — add non-color visual cues to status indicators#39233
Aitema-gmbh wants to merge 3 commits intoapache:masterfrom
Aitema-gmbh:fix/wcag-1.4.1-use-of-color

Conversation

@Aitema-gmbh
Copy link
Copy Markdown
Contributor

SUMMARY

Implements WCAG 2.1 criterion 1.4.1 (Use of Color, Level A).

  • Add font-weight: 700 as secondary visual cue for active tabs
  • Add stroke-dasharray patterns for line chart data series
  • Add text labels and icons alongside color-coded status indicators
  • Ensure information is never conveyed by color alone

TESTING INSTRUCTIONS

  1. Open Alert/Report list → status icons should have text labels
  2. Open a line chart with multiple series → each line has a distinct dash pattern
  3. Tab navigation → active tabs are bold, not just colored

ADDITIONAL INFORMATION

…essages (WCAG 1.4.1)

- Replace generic icons in AlertStatusIcon with distinct shapes per status:
  Success=CheckCircleOutlined, Error=CloseCircleOutlined, Working=LoadingOutlined,
  Noop=ClockCircleOutlined, Grace=ExclamationCircleOutlined
- Add "Error:" text prefix to ChartErrorMessage and ImportModal ErrorAlert
- Add aria-label with scheme name and role="option" to ColorSchemeLabel swatches
- Mark individual color swatches as aria-hidden for cleaner screen reader output
- Add tests verifying distinct icon rendering per status, icon presence in ErrorAlert,
  and aria-label on ColorSchemeLabel
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Apr 9, 2026

Code Review Agent Run #b821eb

Actionable Suggestions - 0
Review Details
  • Files reviewed - 6 · Commit Range: 4f5cf10..4f5cf10
    • superset-frontend/src/components/Chart/ChartErrorMessage.tsx
    • superset-frontend/src/components/ErrorMessage/ErrorAlert.test.tsx
    • superset-frontend/src/components/ImportModal/ErrorAlert.tsx
    • superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.test.tsx
    • superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.tsx
    • superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@dosubot dosubot Bot added change:frontend Requires changing the frontend design:accessibility Related to accessibility standards labels Apr 9, 2026
theme,
)}
spin={isWorkingState}
aria-hidden="true"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The icon is explicitly hidden from assistive technologies, but this component renders only the icon in the table cell, so screen reader users get no status information at all. Replace the hidden attribute with an accessible label so the status can be announced. [possible bug]

Severity Level: Critical 🚨
- ❌ Alert list status column unreadable for screen reader users.
- ❌ Execution log state column inaccessible to assistive technologies.
- ⚠️ Violates WCAG 2.1 use-of-color requirements.
Suggested change
aria-hidden="true"
aria-label={lastStateConfig.label}
Steps of Reproduction ✅
1. Navigate to the alerts list at `/alert/list/`, which is wired to `AlertReportList` via
the route configuration in `superset-frontend/src/views/routes.tsx:16-24` (path
`/alert/list/` uses `Component: AlertReportList`).

2. In `AlertReportList`, observe the first table column definition at
`superset-frontend/src/pages/AlertReportList/index.tsx:138-55`, where the `Cell` renderer
returns only `<AlertStatusIcon state={lastState} isReportEnabled={isReportEnabled} />`
with no additional text in that cell.

3. Open `superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx` and note
that the component computes a human-readable status label in `lastStateConfig.label` for
each state (lines 60-89) and passes it only to the tooltip (`<Tooltip
title={lastStateConfig.label} ...>` at lines 99-100), while the rendered icon element at
lines 107-116 uses `aria-hidden="true"` on the `<Icon>` (line 115) and there is no other
text content in the `<span>` wrapper.

4. With a screen reader enabled, navigate the alerts table row-by-row; when focus/virtual
cursor reaches the status column (the cell whose contents are just `AlertStatusIcon`), no
status text (e.g., "Report sent", "Alert failed") is announced because the only element
conveying status is the icon with `aria-hidden="true"`, and the tooltip title is not
exposed as cell text. The same pattern appears for the execution log state column in
`superset-frontend/src/pages/ExecutionLogList/index.tsx:78-29`, which also renders only
`<AlertStatusIcon ... />` in the `State` column.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx
**Line:** 115:115
**Comment:**
	*Possible Bug: The icon is explicitly hidden from assistive technologies, but this component renders only the icon in the table cell, so screen reader users get no status information at all. Replace the hidden attribute with an accessible label so the status can be announced.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

*/

import { ClientErrorObject, SupersetError } from '@superset-ui/core';
import { t } from '@apache-superset/core';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The translation helper is imported from the package root, but @apache-superset/core only exposes translation as a namespace export. Importing t directly from the root will resolve to an undefined/non-exported symbol and can fail at build/runtime. Import t from the translation submodule instead. [type error]

Severity Level: Critical 🚨
- ❌ TS build fails: no 't' in core index.ts.
- ❌ ChartErrorMessage cannot compile where it is imported.
- ⚠️ Chart error UI unavailable across chart views.
Suggested change
import { t } from '@apache-superset/core';
import { t } from '@apache-superset/core/translation';
Steps of Reproduction ✅
1. Build or test the frontend so TypeScript compiles `superset-frontend` (e.g. running the
standard frontend build/test task that includes
`src/components/Chart/ChartErrorMessage.tsx`).

2. During module resolution, TypeScript loads `@apache-superset/core` from
`packages/superset-core/src/index.ts:19-31`, which only exports namespace objects like
`common`, `authentication`, `translation`, etc., and does not export a named `t` symbol.

3. The compiler then processes `src/components/Chart/ChartErrorMessage.tsx:21`, which
contains `import { t } from '@apache-superset/core';`, and checks this against the exports
from `packages/superset-core/src/index.ts`, finding no `t` export and raising a
compile-time error (`Module '@apache-superset/core' has no exported member 't'`).

4. Note that the correct `t` export is defined in
`packages/superset-core/src/translation/TranslatorSingleton.ts:66-80` and re-exported via
`packages/superset-core/src/translation/index.ts:20-22`, which is what existing call sites
use through `import { t } from '@apache-superset/core/translation';` (see multiple usages
in `superset-frontend/plugins/...`), confirming that the root import in
`ChartErrorMessage.tsx` is inconsistent and incorrect.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/src/components/Chart/ChartErrorMessage.tsx
**Line:** 21:21
**Comment:**
	*Type Error: The translation helper is imported from the package root, but `@apache-superset/core` only exposes `translation` as a namespace export. Importing `t` directly from the root will resolve to an undefined/non-exported symbol and can fail at build/runtime. Import `t` from the translation submodule instead.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

`}
data-test={id}
aria-label={t('Color scheme: %s', label)}
role="option"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Adding an explicit role="option" on this inner <span> creates invalid ARIA semantics because this component is also rendered as selected-value content, outside a listbox context, and can also nest inside an actual option element. This causes accessibility tree inconsistencies and can break screen-reader navigation. Remove the explicit option role and keep only the accessible label text. [logic error]

Severity Level: Major ⚠️
- ⚠️ Color-scheme dropdown options expose nested role="option" elements.
- ⚠️ Selected color-scheme label has role="option" outside listbox.
- ⚠️ Screen-reader navigation for color schemes becomes semantically inconsistent.
- ⚠️ Explore and dashboard color-scheme selectors risk ARIA non-compliance.
Suggested change
role="option"
Steps of Reproduction ✅
1. Open the Explore view for any chart and locate the "Color scheme" control, which is
wired up from `color_scheme` in `superset-frontend/src/explore/controls.tsx:11-19` (type
`'ColorSchemeControl'`) to the `ColorSchemeControl` React component in
`superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx:12-35`.

2. In `ColorSchemeControl`, note that the color-scheme options passed to the `Select`
component are built in `index.tsx:79-103`, where each option's `label` is a
`<ColorSchemeLabel>` element (`ColorSchemeLabel` imported at `index.tsx:39`).

3. Inspect `ColorSchemeLabel` implementation in
`superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.tsx:81-101`:
the outer `<span className="color-scheme-option">` used as the option label includes both
`aria-label={t('Color scheme: %s', label)}` (line 99) and `role="option"` (line 100). When
the `Select` (rendered in `ColorSchemeControl/index.tsx:296-32`) opens its dropdown, it
renders each option with its own listbox semantics (Set by `Select` from
`@superset-ui/core/components`), and the `ColorSchemeLabel` span becomes nested inside
that option container.

4. Run the frontend, open the "Color scheme" dropdown in Explore or in the dashboard-level
`ColorSchemeSelect`
(`superset-frontend/src/dashboard/components/ColorSchemeSelect.tsx:111-119` uses the same
`ColorSchemeLabel` as option labels and `ColorSchemeSelect.tsx:165-30` renders a `Select`
with those options). Using a screen reader or browser accessibility inspector, observe
that each option contains an inner element with `role="option"` from `ColorSchemeLabel`,
resulting in an option element inside another option, and that the same `ColorSchemeLabel`
span (still with `role="option"`) is also used as the selected-value content when the
dropdown is closed, i.e., outside any `listbox` context. This produces invalid ARIA
semantics in both the dropdown and the collapsed control, confirming the issue described
in the suggestion.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.tsx
**Line:** 100:100
**Comment:**
	*Logic Error: Adding an explicit `role="option"` on this inner `<span>` creates invalid ARIA semantics because this component is also rendered as selected-value content, outside a listbox context, and can also nest inside an actual option element. This causes accessibility tree inconsistencies and can break screen-reader navigation. Remove the explicit option role and keep only the accessible label text.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Apr 9, 2026

Code Review Agent Run #dbea3d

Actionable Suggestions - 0
Additional Suggestions - 2
  • superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.tsx - 1
    • ARIA Role Missing · Line 100-100
      Removing role="option" breaks ARIA semantics for this selectable color scheme label, as confirmed by the test expecting getByRole('option'). The component is used in Select options where screen readers rely on this role for navigation.
      Code suggestion
       @@ -100,1 +100,1 @@
      -        >
      +         role="option">
  • superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx - 1
    • Missing aria-hidden on decorative icon · Line 115-115
      The Icon is decorative and its purpose is conveyed by the Tooltip. Removing aria-hidden="true" may cause screen readers to announce the icon unnecessarily, reducing accessibility. Restoring this attribute aligns with patterns used elsewhere in the codebase for similar decorative icons.
      Code suggestion
       @@ -114,2 +114,3 @@
      -            spin={isWorkingState}
      -        />
      +            spin={isWorkingState}
      +            aria-hidden="true"
      +        />
Review Details
  • Files reviewed - 3 · Commit Range: 4f5cf10..ac86959
    • superset-frontend/src/components/Chart/ChartErrorMessage.tsx
    • superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.tsx
    • superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas
Copy link
Copy Markdown
Member

Thanks for not hard coding the 700 font weight value. I see we're just leaning on the strong tags, which is great.Again, re-blind about feedback would be helpful.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR targets WCAG 2.1 criterion 1.4.1 (“Use of Color”) by adding additional non-color cues (icons/labels/patterns) to help users distinguish statuses and UI states without relying on color alone.

Changes:

  • Updated alert/report status iconography and added a spin indicator for “working” state.
  • Improved ColorScheme option accessibility by hiding decorative swatches from assistive tech and adding an aria-label; added unit tests.
  • Adjusted error message presentation/wording and added tests asserting presence of non-color icon cues.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx Swap status icons and add spinning animation for “working” state.
superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.tsx Add aria-label to scheme label container and aria-hidden to decorative swatches.
superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.test.tsx Add tests for the new aria-label and aria-hidden behavior.
superset-frontend/src/components/ImportModal/ErrorAlert.tsx Prefix import error alert message with a bold “Error:” label.
superset-frontend/src/components/ErrorMessage/ErrorAlert.test.tsx Add tests to ensure icons render as a non-color cue (regular + compact modes).
superset-frontend/src/components/Chart/ChartErrorMessage.tsx Update default chart error title to include an “Error:” prefix (localized).

Comment on lines 107 to 115
@@ -116,6 +111,7 @@ export default function AlertStatusIcon({
isReportEnabled,
theme,
)}
spin={isWorkingState}
/>
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The icon’s accessible name currently comes from the icon filename (e.g. “check-circle”, “clock-circle”), which doesn’t convey the actual alert/report status to screen reader users. Since this component already has a localized lastStateConfig.label, consider wiring that into the rendered icon (e.g. via aria-label / title) so the status is announced meaningfully (and not just via a hover tooltip).

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +65
setup();
const option = screen.getByRole('option', {
name: 'Color scheme: Superset Colors',
});
expect(option).toBeInTheDocument();
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ColorSchemeLabel is rendered standalone in this test, but the component doesn’t render any element with role="option" (it’s a plain span), so getByRole('option', { name: ... }) will fail. Consider asserting the aria-label directly (e.g. via getByLabelText / querying [aria-label=...]) or render it within the Select option markup if you specifically want to test the option’s accessible name.

Suggested change
setup();
const option = screen.getByRole('option', {
name: 'Color scheme: Superset Colors',
});
expect(option).toBeInTheDocument();
const { container } = setup();
const swatchContainer = container.querySelector(
'[aria-label="Color scheme: Superset Colors"]',
);
expect(swatchContainer).toBeInTheDocument();

Copilot uses AI. Check for mistakes.
…lid role

The previous test queried for `getByRole('option')`, but the component
no longer renders that role (removed in ac86959 because role="option"
outside an actual listbox is invalid ARIA). Replaced the lookup with
`getByLabelText` which directly verifies the accessible name without
making structural assumptions about the rendering element.
@netlify
Copy link
Copy Markdown

netlify Bot commented May 7, 2026

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 259216c
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/69fcbc75930cb20008334144
😎 Deploy Preview https://deploy-preview-39233--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:frontend Requires changing the frontend design:accessibility Related to accessibility standards size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants