Skip to content

fix(a11y): restore icon visibility in Windows High Contrast Mode#10845

Closed
bmatar wants to merge 2 commits intoswagger-api:masterfrom
bmatar:fix/a11y-hcm-icons
Closed

fix(a11y): restore icon visibility in Windows High Contrast Mode#10845
bmatar wants to merge 2 commits intoswagger-api:masterfrom
bmatar:fix/a11y-hcm-icons

Conversation

@bmatar
Copy link
Copy Markdown

@bmatar bmatar commented Apr 19, 2026

Description

In Windows High Contrast Mode (Edge / Chromium with forced-colors: active), several icon SVGs disappear because their paths use hardcoded fills (or no fill, defaulting to black). Chromium's spec-correct default forced-color-adjust: preserve-parent-color for SVG preserves these author colors against the HCM-substituted Canvas, so a black-fill icon on HCM-dark Canvas sits at 1.00:1 contrast.

Affected controls: operation expand chevrons, lock indicators (auth state), the modal close icon, the Authorize-button lock, JSON-schema accordion chevrons, the model expand toggle, copy-to-clipboard buttons, the dark-mode form checkmark.

In the released swagger-ui (no dark-mode plugin), these icons render as black-on-black in HCM dark — completely invisible. With the dark-mode plugin active, the plugin's CSS coincidentally sets non-default colors on the SVG element, so they render at low contrast (gray-on-black) rather than full disappearance. The fix addresses both.

Two layers: (1) inline icon SVG paths use fill="currentColor" so the inherited (HCM-substituted) color cascades to the path, with a @media (forced-colors: active) block in _buttons.scss pinning the paths to ButtonText for theme-independent contrast and resetting the unlocked-icon opacity dimming that would otherwise composite the system color back to ~40% alpha. (2) Data-URL background-image chevrons get @media blocks that re-render the same SVG shape as a mask-image filled by background-color: ButtonText/CanvasText.

Motivation and Context

Refs #7350 (the existing accessibility issue lists "Expand collapsed chevrons and Unlock controls present in the End Points tab section is not visible highcontrast black mode" as a HCM finding).

Detected by Tactual. WCAG 1.4.11 Non-text Contrast.

Screenshots

All captured under forced-colors: active. Built from the same swagger-ui commit on both ports, only this patch differing.

Operations list — page-light + HCM dark (no dark-mode plugin, what petstore.swagger.io ships):

BEFORE AFTER
<drag ops-no-darkmode_hcm-dark-before.png> <drag ops-no-darkmode_hcm-dark-after.png>

Operations list — page-dark + HCM dark (with dark-mode plugin):

BEFORE AFTER
<drag ops-darkmode_hcm-dark-before.png> <drag ops-darkmode_hcm-dark-after.png>

Models section — page-light + HCM dark:

BEFORE AFTER
<drag models-no-darkmode_hcm-dark-before.png> <drag models-no-darkmode_hcm-dark-after.png>

Operations list — page-light + HCM light (no regression in light HCM theme):

BEFORE AFTER
<drag ops-no-darkmode_hcm-light-before.png> <drag ops-no-darkmode_hcm-light-after.png>

Out of scope

  • <select> chevron in HCM (the <select> background-image chevron, both released _topbar.scss HTTPS dropdown variant and the dark-mode rule at _dark-mode.scss:156): <select> is a replaced element with no usable pseudo-element, so the mask-image approach used here can't apply without wrapping the <select> in a markup change. Followup PR.
  • Native control color-scheme in dark mode: PR fix(dark-mode): indicate dark color-scheme for native browser controls #10844 adds color-scheme: dark to html.dark-mode for native scrollbars/inputs. Independent and non-conflicting; HCM substitutes system colors regardless of color-scheme.

Checklist

My PR contains...

  • Bug fixes (non-breaking change which fixes an issue)

My changes...

  • are not breaking changes.

Documentation

  • My changes do not require a change to the project documentation.

Automated tests

  • All new and existing tests passed.

bmatar added 2 commits April 19, 2026 15:15
Icon SVGs (chevrons, lock indicators, expand/collapse arrows, modal
close, authorize button) disappear in Windows High Contrast Mode.
Their paths have no fill attribute — SVG default is black — or an
explicit author color, which Chromium's default forced-color-adjust:
preserve-parent-color preserves against the HCM-substituted Canvas.
In HCM dark, black-on-black sits at 1.00:1.

Path fills now use fill="currentColor" so the inherited color (which
IS HCM-substituted) cascades to the path. _buttons.scss sets
color: $black on the icon-button parents so currentColor resolves to
the prior effective black in normal light mode. _dark-mode.scss
swaps fill: $color to color: $color on the same SVG selectors so the
cascade carries in dark mode too. An @media (forced-colors: active)
block in _buttons.scss pins the icon paths to ButtonText with
forced-color-adjust: none — ButtonText is the system color paired
with ButtonFace and guarantees contrast in any user HCM theme,
independent of the cascade. The same block resets opacity on the
.unlocked auth icon, since opacity composites multiplicatively with
fill and would otherwise dim the system-color contrast guarantee.

Refs swagger-api#7350
The model expand chevron, copy-to-clipboard button, and form
checkmark all render as data-URL background-image SVGs with
hardcoded fill colors. In Windows High Contrast Mode the system
strips background-image, so these icons become invisible — the
underlying button/checkbox still functions but loses its visual
indicator.

Added @media (forced-colors: active) blocks that re-render the same
SVG shape as a mask-image filled by background-color: ButtonText (or
CanvasText for the model toggle on a div pseudo-element). With
forced-color-adjust: none the system color is honored as authored,
so the icon shape stays visible against any HCM theme background.
Light-mode and dark-mode variants of each rule both updated.

Refs swagger-api#10699
@bmatar bmatar closed this Apr 19, 2026
@bmatar bmatar deleted the fix/a11y-hcm-icons branch April 19, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant