Forced Colors & High Contrast
Windows High Contrast Mode and the CSS forced-colors media query replace custom colours with a small set of system keywords. Sitka components are authored to degrade gracefully and retain usability when colour is stripped.
What forced colors mode is
When a user enables Windows High Contrast Mode (or any OS-level forced-colors setting), the browser applies @media (forced-colors: active). Custom CSS colours are replaced by system colour keywords. Gradients, box-shadows, and background-images that convey meaning are stripped.
/* Detect forced-colors */
@media (forced-colors: active) {
.my-button {
/* Use system keywords — do not use hex or rgb */
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonBorder;
}
/* Opt out for specific elements (use sparingly) */
.custom-icon {
forced-color-adjust: none;
}
}System color keywords
| Keyword | Maps to | Example usage |
|---|---|---|
| Canvas | Page/surface background | background-color: Canvas |
| CanvasText | Primary body text | color: CanvasText |
| ButtonFace | Button background | background: ButtonFace |
| ButtonText | Button label | color: ButtonText |
| Highlight | Selected / focused element | background: Highlight |
| HighlightText | Text on selected element | color: HighlightText |
| GrayText | Disabled text | color: GrayText |
| LinkText | Unvisited links | color: LinkText |
| VisitedText | Visited links | color: VisitedText |
| ActiveText | Active (pressed) links | color: ActiveText |
| Mark | Highlighted / marked text | background: Mark |
| MarkText | Text inside Mark | color: MarkText |
Per-component rules
Button
- →Borders become visible — ButtonFace + ButtonText with ButtonBorder where supported
- →Transparent backgrounds are replaced by ButtonFace
- →Box shadows are stripped — use border instead for boundaries
Input / Textarea
- →Background → Field; text → FieldText
- →Placeholder text → GrayText
- →Focus ring becomes Highlight — verify outline width ≥ 2px
Checkbox / Radio / Switch
- →Custom drawn controls fall back to native appearance unless forced-colors overrides are defined
- →Use appearance: none + explicit Highlight/HighlightText for checked state
- →Use forced-color-adjust: none sparingly — only when full custom rendering is required
Badge / Chip
- →Coloured backgrounds collapse to Canvas — use borders as differentiators instead
- →Add a 1px ButtonBorder border so the element boundary is visible
Skeleton / Spinner
- →Animation fill colours become Canvas — add ButtonBorder to show boundaries
- →Consider showing static placeholder text instead of visual shimmer
Testing
Windows — native
- 1. Settings → Accessibility → Contrast themes
- 2. Choose High Contrast Black or White
- 3. Reload browser — forced-colors activates
Chrome DevTools — emulate
- 1. Open DevTools → Rendering tab
- 2. Find "Emulate CSS media feature forced-colors"
- 3. Select "forced-colors: active"
Guidelines
- →Never use colour alone to convey state — borders, icons, and text labels must also differ (this also satisfies WCAG 1.4.1).
- →Prefer borders over box-shadows for element boundaries — shadows are stripped in forced-colors.
- →Use forced-color-adjust: none only when absolutely necessary; it opts the element out of system colour replacement entirely.
- →Test with both High Contrast Black (dark) and High Contrast White (light) themes — behaviour differs.
- →Focus indicators must remain visible — ensure outline uses a system colour keyword in your forced-colors override.