Forced Colours Mode for SVG Charts
Permalink to "Forced Colours Mode for SVG Charts"Forced colours mode — Windows High Contrast themes, and the equivalent settings in some other environments — replaces a page’s colours with a small user-chosen palette: typically a background, a text colour, a link colour, a highlight and a few more. It is used by many low-vision users and people with light sensitivity. Browsers apply it to HTML automatically. SVG is handled less gracefully: inline SVG fill and stroke from CSS are forced in Chromium, but presentation attributes and some gradients are not consistently, so charts can end up with invisible lines, text drawn in the background colour, or every series rendered in the same single colour.
This page shows what happens to charts under forced colours and how to adapt them. It belongs to chart colour & contrast.
Spec reference
Permalink to "Spec reference"CSS Color Adjustment Level 1 defines:
- The
forced-colors: activemedia feature, true when a forced colour palette is in effect. - System colour keywords:
Canvas,CanvasText,LinkText,VisitedText,ActiveText,ButtonFace,ButtonText,ButtonBorder,Field,FieldText,Highlight,HighlightText,SelectedItem,SelectedItemText,Mark,MarkText,GrayText,AccentColor,AccentColorText. forced-color-adjust: auto | none | preserve-parent-color—noneopts an element out of forcing, keeping author colours.
In forced colours, browsers override color, background-color, border-color, outline-color, fill and stroke (for SVG, in Chromium) with system colours, remove box-shadow and text-shadow, and remove background images in some cases. Contrast between system colours is guaranteed by the user’s chosen theme, not by you.
WCAG does not require forced-colours support directly, but content that disappears in it fails users in the same way as SC 1.4.1, 1.4.3 and 1.4.11 failures do, and several criteria’s understanding documents discuss it.
When to preserve colours — and when to let them be forced
Permalink to "When to preserve colours — and when to let them be forced"Let the browser force colours for almost everything: text, axes, gridlines, lines, focus indicators. The user chose their palette for a reason; overriding it defeats the mode’s purpose.
Consider forced-color-adjust: none only for data marks whose specific colours carry meaning that cannot be expressed otherwise — a heat map’s colour scale, a map’s choropleth — and then provide a legend and a data table, and ensure the marks have outlines in a system colour so they remain distinct from the background.
The misapplication to name is forced-color-adjust: none on the whole chart to “keep the brand palette”. Users in high contrast mode then get a low-contrast chart in a high-contrast page — exactly what they are trying to avoid.
Annotated code example
Permalink to "Annotated code example"/* Normal mode: series by colour AND dash (see patterns guide) */
.series-1 { stroke: var(--series-1); }
.series-2 { stroke: var(--series-2); stroke-dasharray: 6 4; }
.series-3 { stroke: var(--series-3); stroke-dasharray: 2 3; }
@media (forced-colors: active) {
/* Let the palette win, but make roles explicit with system colours */
.chart-bg { fill: Canvas; }
.chart-axis,
.chart-grid { stroke: GrayText; }
.chart text { fill: CanvasText; }
.series-line { stroke: CanvasText; } /* dashes now carry identity */
.bar { fill: Canvas; stroke: CanvasText; stroke-width: 2; } /* outlined bars */
.bar.series-2 { fill: url(#hatch-forced); } /* pattern drawn in CanvasText */
/* Focus and selection: use the user's highlight colours */
.point:focus-visible,
.point.is-active { stroke: Highlight; stroke-width: 3; }
.point.is-selected { fill: SelectedItem; }
/* box-shadow focus rings disappear in forced colours; use outline instead */
.chart:focus-visible { outline: 2px solid Highlight; outline-offset: 2px; }
}
/* A heat map genuinely needs its scale: keep colours, add system-colour cell borders */
@media (forced-colors: active) {
.heatmap-cell { forced-color-adjust: none; stroke: CanvasText; stroke-width: 1; }
}
The chart’s SVG should apply colours through classes and CSS rather than presentation attributes (fill="#155c38"). CSS rules are reliably forced and can be overridden in the media query; attribute colours are handled inconsistently across browsers.
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"Forced colours affects visibility, not semantics; the checks are visual, performed with the keyboard.
| Check | Expected in forced colours | Failure indicator |
|---|---|---|
| Tab to the chart | Visible outline in Highlight | No visible focus (box-shadow removed) |
| Arrow through points | Active point ringed in Highlight | Ring invisible or same as line |
| Series distinction | Dashes, shapes or patterns differ | All series identical |
| Tooltip | Text in CanvasText on Canvas with a border | Borderless box merges with the plot |
| Selected points | SelectedItem fill | No difference from unselected |
Integration context
Permalink to "Integration context"Forced colours is where a chart’s second channel proves itself: if series are distinguished only by colour, they become identical. The encodings are covered in patterns and shapes for colour-independent charts. The audit procedure — emulation plus a real Windows contrast theme — is in Windows High Contrast audits for data UIs.
Inline SVG diagrams across a site face the same issue; theme-aware SVG styling through classes and CSS custom properties, rather than hard-coded attributes, makes both dark mode and forced colours easier.
Gotchas
Permalink to "Gotchas"Presentation attributes. fill="#..." on SVG elements is not always forced. Move colours to CSS classes.
Images of charts. A chart exported as PNG is never adapted. Prefer inline SVG, and give images a text alternative and data table.
currentColor tricks. Icons and marks drawn with currentColor adapt automatically — often the easiest route for axes and simple marks.
Design system notes
Permalink to "Design system notes"Chart components should style every mark through classes mapped to tokens, include a forced-colours stylesheet that maps roles (background, text, axis, series line, focus, selection) to system colours, and forbid presentation-attribute colours in chart code via lint rules. Colour-scale charts get a documented, targeted opt-out with outlines.
Testing checklist
Permalink to "Testing checklist"FAQ
Permalink to "FAQ"What happens to SVG charts in Windows High Contrast mode?
The browser replaces CSS colours with the user’s system palette, so series fills often become the same colour, shadows disappear, and anything relying on colour alone is lost. Dashes, shapes, patterns and outlines survive.
Should charts use forced-color-adjust: none?
Only for marks whose colour is the data, such as heat map cells, and then with system-colour borders and a legend. Opting a whole chart out ignores the palette the user chose.
How do I keep chart focus indicators visible in forced colours?
Use outline or SVG strokes rather than box-shadow, which is removed in forced colours, and colour them with the Highlight system colour.
How do I test forced colours without Windows?
Use the forced-colors emulation in Chromium DevTools’ Rendering panel for most checks, and confirm on a real Windows contrast theme before release.
Related
Permalink to "Related"- Patterns and shapes — what survives forced colours
- Windows High Contrast audits — the audit procedure
- Non-text contrast — the normal-mode requirement