Insufficient contrast for UI elements
contrast/control-insufficient
- WCAG success criterion
- 1.4.11 (level AA)
- Outcome class
- Automatic. An automated check that can report a confirmed violation.
- Area
- Accessibility, Visual Presentation
- Default severity
- medium
- axe-core rule
non-text-contrast
Why we flag this
Interactive elements are hard to recognize.
Contrast ratio of UI elements below 3:1.
What the rule checks
Scope. Rendered checkboxes, radio buttons, ranges and role checkbox/radio/switch elements drawn by the author (appearance: none).
Expectation. Fails when the visual boundary of an author-drawn control (its top border if drawn, otherwise its own fill) reaches less than 3:1 against the background behind it; 2.995:1 fails (no rounding), 3.03:1 passes. Native controls drawn by the browser are not the author's boundary and are not reported. A control that is not rendered is not measured. An ARIA switch drawn with a span is a control like a native input. An unresolvable background is contrast/control-undetermined.
Source. WCAG 2.2 SC 1.4.11 Non-text Contrast (user interface components)
Detection: shared; needs dom, computed_style, geometry.
Failing example
The check reports a violation here. Corpus case visual_control_contrast, element input#cb-faint. The page may contain further findings for other rules.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Custom checkbox and switch boundaries</title>
<style>
body { background: #ffffff; color: #111111; }
.custom { -webkit-appearance: none; appearance: none; width: 24px; height: 24px; margin: 0 8px 0 0;
background: #ffffff; border: 2px solid #cccccc; vertical-align: middle; }
#cb-ok { border-color: #767676; }
#cb-edge-pass { border-color: #949494; }
#cb-edge-fail { border-color: #959595; }
.switch { display: inline-block; width: 64px; height: 44px; border: 2px solid #cccccc;
border-radius: 22px; background: #ffffff; vertical-align: middle; }
.gradient { display: block; padding: 8px; background-image: linear-gradient(90deg, #ffffff, #e8e8e8); }
label { display: block; margin: 12px 0; }
</style>
</head>
<body>
<header>
<a href="#main" style="display: inline-block; min-width: 44px; min-height: 44px; line-height: 44px; padding: 0 8px">Skip to main content</a>
<p>Newsletter settings</p>
<nav aria-label="Main"><a href="#main" aria-current="page" style="display: inline-block; min-width: 44px; min-height: 44px; line-height: 44px; padding: 0 8px">Overview</a></nav>
</header>
<main id="main">
<h1>Notification preferences</h1>
<form action="#">
<!-- 1.61:1 against white: below the 3:1 a control boundary needs. -->
<label><input type="checkbox" class="custom" id="cb-faint"> Weekly digest</label>
<!-- 4.54:1: passes. -->
<label><input type="checkbox" class="custom" id="cb-ok"> Product news</label>
<!-- Threshold: 3.03:1 passes, 2.995:1 fails (no rounding up). -->
<label><input type="checkbox" class="custom" id="cb-edge-pass"> Event invitations</label>
<label><input type="checkbox" class="custom" id="cb-edge-fail"> Reminders</label>
<!-- Native checkbox: drawn by the browser, no author boundary to measure. -->
<label><input type="checkbox" id="cb-native"> Partner offers</label>
<!-- Not rendered: nothing to measure. -->
<label>Hidden option <input type="checkbox" class="custom" id="cb-hidden" style="display: none"></label>
<!-- Background is a gradient: the colour behind the control is unknown. -->
<label class="gradient"><input type="checkbox" class="custom" id="cb-gradient"> Surveys</label>
<!-- ARIA switch drawn with a div: same 1.61:1 boundary. -->
<p><span id="sw-label">Dark mode</span>
<span id="sw-faint" class="switch" role="switch" aria-checked="false" aria-labelledby="sw-label" tabindex="0"></span></p>
<button type="submit" style="min-width: 44px; min-height: 44px">Save preferences</button>
</form>
</main>
<footer><p>Footer text</p></footer>
</body>
</html>Passing example
The check passes here and reports nothing. Corpus case visual_control_contrast, element input#cb-ok. The page may contain further findings for other rules.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Custom checkbox and switch boundaries</title>
<style>
body { background: #ffffff; color: #111111; }
.custom { -webkit-appearance: none; appearance: none; width: 24px; height: 24px; margin: 0 8px 0 0;
background: #ffffff; border: 2px solid #cccccc; vertical-align: middle; }
#cb-ok { border-color: #767676; }
#cb-edge-pass { border-color: #949494; }
#cb-edge-fail { border-color: #959595; }
.switch { display: inline-block; width: 64px; height: 44px; border: 2px solid #cccccc;
border-radius: 22px; background: #ffffff; vertical-align: middle; }
.gradient { display: block; padding: 8px; background-image: linear-gradient(90deg, #ffffff, #e8e8e8); }
label { display: block; margin: 12px 0; }
</style>
</head>
<body>
<header>
<a href="#main" style="display: inline-block; min-width: 44px; min-height: 44px; line-height: 44px; padding: 0 8px">Skip to main content</a>
<p>Newsletter settings</p>
<nav aria-label="Main"><a href="#main" aria-current="page" style="display: inline-block; min-width: 44px; min-height: 44px; line-height: 44px; padding: 0 8px">Overview</a></nav>
</header>
<main id="main">
<h1>Notification preferences</h1>
<form action="#">
<!-- 1.61:1 against white: below the 3:1 a control boundary needs. -->
<label><input type="checkbox" class="custom" id="cb-faint"> Weekly digest</label>
<!-- 4.54:1: passes. -->
<label><input type="checkbox" class="custom" id="cb-ok"> Product news</label>
<!-- Threshold: 3.03:1 passes, 2.995:1 fails (no rounding up). -->
<label><input type="checkbox" class="custom" id="cb-edge-pass"> Event invitations</label>
<label><input type="checkbox" class="custom" id="cb-edge-fail"> Reminders</label>
<!-- Native checkbox: drawn by the browser, no author boundary to measure. -->
<label><input type="checkbox" id="cb-native"> Partner offers</label>
<!-- Not rendered: nothing to measure. -->
<label>Hidden option <input type="checkbox" class="custom" id="cb-hidden" style="display: none"></label>
<!-- Background is a gradient: the colour behind the control is unknown. -->
<label class="gradient"><input type="checkbox" class="custom" id="cb-gradient"> Surveys</label>
<!-- ARIA switch drawn with a div: same 1.61:1 boundary. -->
<p><span id="sw-label">Dark mode</span>
<span id="sw-faint" class="switch" role="switch" aria-checked="false" aria-labelledby="sw-label" tabindex="0"></span></p>
<button type="submit" style="min-width: 44px; min-height: 44px">Save preferences</button>
</form>
</main>
<footer><p>Footer text</p></footer>
</body>
</html>This page is generated from the rule catalogue and the rule contracts; see therule overview for how the outcome classes are derived.