From 4859f1a843479222e5a026c58d13d34dfceeb86e Mon Sep 17 00:00:00 2001 From: Fedo Hagge-Kubat Date: Thu, 9 Apr 2026 20:49:55 +0200 Subject: [PATCH 1/3] =?UTF-8?q?fix(a11y):=20WCAG=201.4.3=20=E2=80=94=20enf?= =?UTF-8?q?orce=20accessible=20link=20color=20contrast=20(#0d7090,=205.62:?= =?UTF-8?q?1=20ratio)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../packages/superset-core/src/theme/GlobalStyles.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx index 33a30a9fa613..b73ec8fbc7fa 100644 --- a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx +++ b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx @@ -55,6 +55,16 @@ export const GlobalStyles = () => { color: ${theme.colorLink}; } + /* WCAG 1.4.3: Minimum Contrast — override link color from colorPrimary (#2893B3, + 3.55:1 on white) to a darker shade that meets the 4.5:1 text contrast threshold. + Excludes links that are intentionally styled as buttons. */ + a:not([class*="ant-btn"]):not([role="button"]) { + color: #0d7090 !important; + } + a:not([class*="ant-btn"]):not([role="button"]):hover { + color: #0a5a73 !important; + } + h1, h2, h3, From 4793676875f37419bdaf4b6578dcd19184eab227 Mon Sep 17 00:00:00 2001 From: Fedo Hagge-Kubat Date: Sat, 18 Apr 2026 01:07:46 +0200 Subject: [PATCH 2/3] =?UTF-8?q?fix(a11y):=20WCAG=201.4.3=20=E2=80=94=20use?= =?UTF-8?q?=20theme=20tokens=20instead=20of=20hardcoded=20link=20colors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address Bito/CodeAnt code review finding: the previous hardcoded hex values (#0d7090, #0a5a73) only reached the 4.5:1 contrast threshold on light backgrounds. On dark themes where colorBgBase is near-black the same values drop to ~3.47:1 and ~2.5:1, regressing contrast below WCAG 1.4.3. Route link colors through theme.colorLink / colorLinkHover so they adapt to each theme's paired background and so custom themes can adjust link colors without patching GlobalStyles. --- .../packages/superset-core/src/theme/GlobalStyles.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx index b73ec8fbc7fa..ef124dcc3680 100644 --- a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx +++ b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx @@ -55,14 +55,17 @@ export const GlobalStyles = () => { color: ${theme.colorLink}; } - /* WCAG 1.4.3: Minimum Contrast — override link color from colorPrimary (#2893B3, - 3.55:1 on white) to a darker shade that meets the 4.5:1 text contrast threshold. + /* WCAG 1.4.3: Minimum Contrast — route link colors through theme tokens + so they adapt to light, dark, and custom themes. The token defaults + (colorLink / colorLinkHover) are tuned to meet the 4.5:1 contrast + threshold on the paired colorBgBase; hardcoded hex values previously + used here were light-mode-only and failed WCAG in dark themes. Excludes links that are intentionally styled as buttons. */ a:not([class*="ant-btn"]):not([role="button"]) { - color: #0d7090 !important; + color: ${theme.colorLink}; } a:not([class*="ant-btn"]):not([role="button"]):hover { - color: #0a5a73 !important; + color: ${theme.colorLinkHover}; } h1, From efcc335cf9d1f2b0532139599e265b83c9333e93 Mon Sep 17 00:00:00 2001 From: Kolja Date: Thu, 7 May 2026 18:22:17 +0200 Subject: [PATCH 3/3] =?UTF-8?q?fix(a11y):=20WCAG=201.4.3=20=E2=80=94=20col?= =?UTF-8?q?lapse=20redundant=20link=20selectors,=20clarify=20scope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses bot review on PR #39234: - Removed the duplicate `a:not([class*="ant-btn"]):not([role="button"])` rule. The general `a { color: theme.colorLink }` already covers all anchors; AntD button-styled anchors carry their own component-level styling that overrides the global rule, so the negation selector was a no-op. - Updated the comment to make explicit that the 4.5:1 contrast guarantee is a property of the active theme's `colorLink` / `colorLinkHover` tokens — this layer just routes through them. Themes whose default `colorLink` doesn't pass WCAG should be tuned at the token level, not here. This also matches Rusackas' implicit framing (this is general correct behavior, not a11y-specific scoping). - Removed the inaccurate reference to "hardcoded hex values previously used here" — those were never in this file. --- .../superset-core/src/theme/GlobalStyles.tsx | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx index ef124dcc3680..a1de0f659d59 100644 --- a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx +++ b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx @@ -51,20 +51,18 @@ export const GlobalStyles = () => { font-family: ${theme.fontFamily}; } + /* WCAG 1.4.3: Minimum Contrast — route link colors through theme + tokens so they adapt to light, dark, and custom themes. Buttons + rendered as `` or `role="button"` carry + their own component-level coloring and override these values, so + a single global rule is enough; the previous duplicated selector + had no effect over the simple `a` rule. The 4.5:1 contrast + guarantee depends on the active theme's `colorLink` / + `colorLinkHover` tokens being tuned for the paired `colorBgBase`. */ a { color: ${theme.colorLink}; } - - /* WCAG 1.4.3: Minimum Contrast — route link colors through theme tokens - so they adapt to light, dark, and custom themes. The token defaults - (colorLink / colorLinkHover) are tuned to meet the 4.5:1 contrast - threshold on the paired colorBgBase; hardcoded hex values previously - used here were light-mode-only and failed WCAG in dark themes. - Excludes links that are intentionally styled as buttons. */ - a:not([class*="ant-btn"]):not([role="button"]) { - color: ${theme.colorLink}; - } - a:not([class*="ant-btn"]):not([role="button"]):hover { + a:hover { color: ${theme.colorLinkHover}; }