Search topics
High contrast mode hiding focus, icons and selections
The chosen option is shown only by a background fill, the focus ring is a box shadow and the icon has a fixed fill, so a contrast theme removes all three.
input:checked + label { background: #1a1a1a; } The problem
People who use a contrast theme can't tell which size is chosen, where focus is, or that the save button is there.
A clothing shop's product page has a row of size chips, S, M, L and XL, and a heart button that saves the jumper to a wishlist. In the shop's own colours everything works: the chosen size is a dark chip with white text, focus is a blue ring, and the heart is a dark icon on white.
Now switch on a dark contrast theme in Windows, as many people with low vision do. Every chip turns black with a white border, so the chosen size looks exactly like the others. Tab to the chips or the heart, and no ring appears. The heart itself is gone: a gap where the button should be.
A contrast theme, which CSS calls forced colors, replaces the page's colours with the user's own palette. Browsers do it property by property, and three of the shop's choices fall through:
- The chosen chip is marked only by
background-color. The theme sets every background to the same canvas colour, so the one difference between chips is gone. - The focus ring is a
box-shadow, withoutline: none. In forced colors, box shadows compute tonone, and there is no outline left to recolour. - The heart's path has
fill="#1a1a1a". Inline SVG keeps its own colours unless they come from the text colour, so a near-black icon sits on a black canvas.
WCAG 1.4.11 is measured in the page's own colours, and in those this page passes. But the criterion exists so people can identify controls and their states, and for someone using a contrast theme, this is the page they get. A field drawn only by a fill, with no border, disappears the same way.
Windows contrast themes are the common source, through Settings, Accessibility, Contrast themes. Chromium browsers and Firefox on Windows follow them, and Firefox can also override page colours on any system. Some people use a dark theme because bright screens hurt, not because of low vision, and the page fails them in the same way.
2 of 10 groups affected
- No vision (not affected)
- Low vision (affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (not affected)
- Cognitive (not affected)
- Seizures (not affected)
- Limited vision People who switch on a contrast theme to read the screen at all see every size as unchosen, and the heart button as an empty gap they don't know to click.
- Limited manipulation Keyboard users who also use a contrast theme lose the focus ring entirely and can't tell which size an arrow key just chose or what Enter will press.
Scanners and contrast checkers measure the colours the stylesheet sets. None of them turns a contrast theme on, so a page can pass every automated check and still lose its focus ring in one. Generated components make it more likely: Tailwind's ring-* utilities and most design-system focus styles are box shadows, often paired with outline-none. A state shown by a filled chip looks finished in every mock-up.
Try it
Turn the contrast theme on, choose a size, then press Tab.
The switch draws the page as a dark Windows contrast theme would. Can you still see which size is chosen, where focus is, and the save button?
Broken Can you fix this?
Merino crew jumper
Oat · 89.00
The fix
Draw state, focus and icons with what a contrast theme keeps, or redraw them in system colours.
- 1A selected state needs a cue besides its fill: a border, a tick or a system colour.
- 2Focus needs an outline, even a transparent one, not only a box shadow.
- 3Icons in controls follow currentColor or a system colour, never a fixed fill.
<fieldset class="sizes"> <legend>Size</legend> <input type="radio" name="size" id="size-m" value="m" checked> <label for="size-m">M</label> …</fieldset><button class="save" aria-label="Save to wishlist"> <svg viewBox="0 0 24 24" aria-hidden="true"> <path fill="#1a1a1a" d="…"/> </svg></button>Styles a contrast theme keeps
<fieldset class="sizes"> <legend>Size</legend> <input type="radio" name="size" id="size-m" value="m" checked> <label for="size-m"> <svg class="tick" viewBox="0 0 24 24" aria-hidden="true"> <path fill="none" stroke="currentColor" stroke-width="3" d="M5 12l5 5 9-10" /> </svg> M </label> …</fieldset><button class="save" aria-label="Save to wishlist"> <svg viewBox="0 0 24 24" aria-hidden="true"> <path fill="currentColor" d="…"/> </svg></button>In the shop's colours each of these passes 1.4.11. Under a contrast theme the fill, the shadow and the icon's contrast are exactly what goes, so the state, the focus and the button are lost together.
A contrast theme recolours borders, outlines and the text colour rather than removing them. The tick is drawn in currentColor, so it shows in every palette, and it tells colour-blind visitors which size is chosen too. The focus ring is now an outline, which the theme draws in its own focus colour, and the heart follows the button's text colour wherever it goes.
Both pass in every contrast theme. Styles the theme keeps need no second set of rules, so new components are covered as long as they follow the same habits, but the chosen chip gains a tick in the normal design. The forced-colors block leaves the design untouched and uses the user's own highlight colour, at the cost of a list that has to grow with every component. Many sites do both: they draw every focus ring as an outline and every icon in currentColor, which costs nothing, and use the block only for the states whose design can't change.
If the design wants the box-shadow ring for its look, keep it and add outline: 3px solid transparent. A transparent outline is invisible in the normal colours, and the theme turns it into a visible one. Tailwind's outline-hidden does exactly this under forced colors; outline-none doesn't, so pair ring-* with outline-hidden.
The same habit helps filled buttons. A button with border: none becomes plain text in a contrast theme, while border: 2px solid transparent gives it back its shape at no cost to the normal design.
forced-color-adjust: none brings back the page's own colours, and with them the failure the user's theme was hiding from them. Use it only where the colour is the content, such as a colour swatch on a product, and give that swatch a border in CanvasText so it still has an edge.
Icons drawn as background-image, CSS gradients or mask-image go too: the theme drops background images that aren't files and recolours the mask's background to the canvas. Inline SVG in currentColor is the safest icon. An <img> icon keeps its own colours, so check it on both a dark and a light theme.
Fixing this with an AI coding assistant? Get this guide as Markdown
Verify the fix
5 checks, no mouse.
Test with: Forced colors, Keyboard, Contrast measurement
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after shared component styles or the icon set change.
Limits
This is a starting pattern, not a guarantee for every application. Contrast themes differ between Windows versions and browsers, and Firefox's own colour override behaves slightly differently again. Normal-colour contrast is a separate requirement, covered in low-contrast form fields, icons and focus rings. Test the complete pages with people who use a contrast theme every day.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- WCAG Understanding: non-text-contrast (opens in a new tab)
- w3.org: css-color-adjust-1 (opens in a new tab)
- developer.mozilla.org: forced-colors (opens in a new tab)
- developer.mozilla.org: system-color (opens in a new tab)
- blogs.windows.com: styling-for-windows-high-contrast-with-new-standards-for-forced-colors (opens in a new tab)
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.