Text isn’t the only thing on a page that has to be seen. The edge that tells you
where an input ends, the slice of colour that distinguishes one line on a chart from
the next, the icon that means “delete”, the ring that shows which control has keyboard
focus — all of these carry meaning through their appearance alone. If they don’t stand
out from what surrounds them, people with low vision, age-related sight loss, or a
washed-out screen in bright light can’t tell the boundary, the category, or the
current position. That is exactly what WCAG success criterion
1.4.11 Non-text Contrast guards against.
The rule is a single number: the visual information needed to identify a user
interface component or a meaningful graphic must have a contrast ratio of at least
3:1 against the colour next to it. This lesson works through the three
places teams most often miss it — control borders, data and icon colours, and focus
indicators — and shows the small palette changes that bring each one back into
conformance.
What you’ll learn
What the 3:1 threshold in 1.4.11 actually covers and what it
doesn’t; how to give input and button boundaries enough contrast against the page;
how to choose chart series colours and meaningful-icon colours that clear 3:1
against their background and each other; and how to make a focus indicator visible
against every colour it lands on.
Standards this lesson maps to
Standard
Criterion
Level
What it requires
WCAG 2.2
1.4.11 Non-text Contrast
AA
The visual information needed to identify UI components and states, and meaningful graphical objects, has a contrast ratio of at least 3:1 against adjacent colour.
WCAG 2.2
2.4.11 Focus Not Obscured (Minimum)
AA
A focused component is at least partly visible — pairs with a visible focus indicator so focus can actually be located.
WCAG 2.2
2.4.7 Focus Visible
AA
Any keyboard-operable interface has a visible focus indicator; 1.4.11 then governs that indicator’s contrast.
EN 301 549
9.1.4.11 (incorporates WCAG)
—
European harmonised standard; references the WCAG AA set including Non-text Contrast.
Section 508
502.3 / 504 (incorporates WCAG A & AA)
—
US federal ICT must meet WCAG 2.0 Level A and AA; 1.4.11 is part of the harmonised 2.1/2.2 update many agencies apply.
ADA Title II
WCAG 2.1 AA (DOJ rule)
AA
US state/local government web content must conform to WCAG 2.1 AA, which includes 1.4.11.
The three problems we’ll fix
Each card below isolates one common non-text-contrast defect. For every issue you get
a plain-language statement of the problem, a Bad example (shown as
escaped, non-running code so it can’t harm this page), a Good example,
the copyable Code, and an ordered fix. All ratios refer to 1.4.11’s
3:1 minimum.
Input & button borders below 3:1 against the page
WCAG 2.2 · 1.4.11AAEN 301 549Section 508ADA Title II
The boundary that tells a person “this is a field you
can type in” or “this is a button you can press” is itself visual information that
identifies a component, so 1.4.11 applies to it. A pale grey hairline on a white
card looks tasteful in a mockup but often measures around 1.4:1 — far below the
3:1 minimum. For a low-vision user the input becomes invisible: it reads as empty
space, not as something to interact with. The same trap catches “ghost” and
outline buttons whose only border is a faint tint, and tonal buttons whose fill is
barely lighter than the page. Note the boundary needs contrast against
adjacent colour — the page or card behind it — not against the label
inside.
Bad
A near-white border on a white form. The label text passes 1.4.3, but the
field’s edge is roughly 1.4:1 against the page, so the control itself can’t be
located (1.4.11).
The border is darkened until it clears 3:1 against the page. A 1px
#767676 edge measures about 4.5:1 on white, comfortably over the
minimum, and still reads as a quiet UI line.
Drive boundaries from a token so every theme inherits a conforming value, and
remember the rule cuts both ways: on a dark surface the border must be light
enough to clear 3:1. A solid-fill button passes via its fill-to-page contrast,
so it doesn’t need a border at all.
control-border-tokens.css
:root { --control-border: #767676; } /* on light: ~4.5:1 vs #fff */
[data-theme="dark"] { --control-border: #8a8a8a; } /* on #1a1a1a: ~3.6:1 */
.field, .btn-outline { border: 1px solid var(--control-border); }
/* Solid button: identified by its fill, not a border */
.btn-primary { background: #1d4ed8; color: #fff; border: 0; } /* fill ~3.7:1 vs #fff page */
How to fix
Measure each control’s boundary against the colour directly behind it — the
page or card — and require at least 3:1.
Darken hairline borders (a value around #767676 on white) or
give the control a fill that itself clears 3:1 against the page.
For outline and ghost buttons, treat the outline as the identifying
information; if it’s decorative tint only, it will fail.
Define the border as a token and check it in every theme — light, dark, and
high-contrast all need their own conforming value.
Don’t rely on the placeholder or inner shadow to convey “this is a field”;
those aren’t guaranteed to meet 3:1.
Chart series & meaningful icons below 3:1
WCAG 2.2 · 1.4.11AAEN 301 549Section 508
1.4.11 covers “graphical objects required to understand
the content”. In a chart, the coloured line, bar, or slice is the data, so
each series must clear 3:1 against the plot background. When two adjacent series
are also told apart only by colour, they must additionally differ from each other
by 3:1, or a reader can’t separate them. The same applies to a meaningful icon — a
warning triangle, a “required” asterisk-mark, a status dot: if the shape carries
information and isn’t backed by a text label, its colour must reach 3:1 against
what’s behind it. Purely decorative graphics are exempt, but anything load-bearing
is not. Note that 1.4.11 governs identification, not data values, so pairing colour
with shape, pattern, or direct labels is the most robust route.
Bad
Pastel series on a white plot. The lightest line sits near 1.6:1 against the
background and two neighbours differ by under 3:1, so neither the line nor which
series it is can be made out.
bad-chart-series.css
/* plot background #ffffff */
--series-1: #bfdbfe; /* ~1.3:1 vs #fff — fails */
--series-2: #bbf7d0; /* ~1.3:1 vs #fff — fails */
<!-- meaningful icon, colour only, no label -->
<svg class="status" aria-hidden="true">
<circle fill="#fca5a5"/> <!-- ~1.8:1 vs #fff: error state unreadable -->
</svg>
Good
Series colours are deepened so each clears 3:1 against the plot and adjacent
pairs differ by 3:1. The status icon’s colour passes too — and it gets an
accessible name so it isn’t colour-dependent.
good-chart-series.css
/* plot background #ffffff */
--series-1: #1d4ed8; /* ~5.2:1 vs #fff */
--series-2: #047857; /* ~4.5:1 vs #fff; >3:1 vs series-1 */
<svg class="status" role="img" aria-label="Error">
<circle fill="#b91c1c"/> <!-- ~5.9:1 vs #fff — passes 1.4.11 -->
</svg>
Code
Don’t lean on contrast alone to separate series — add a second channel so the
chart also survives colour-blindness and greyscale printing. Dashes, markers, or
direct labels make each line identifiable independent of hue.
Check every chart series against the plot background and require at least
3:1; deepen pastel palettes until they pass.
Where series are told apart by colour alone, also require 3:1 between
adjacent series so they’re separable.
Give any meaningful icon a colour that clears 3:1 against its background, and
an accessible name (role="img" + aria-label) so it
isn’t colour-only.
Add a second visual channel — pattern, marker, or direct label — so the
graphic survives colour-blindness and greyscale.
Leave genuinely decorative graphics out of scope, but be honest about what’s
load-bearing.
Focus ring below 3:1 against adjacent colours
WCAG 2.2 · 1.4.11AA2.4.7AAEN 301 549ADA Title II
A focus indicator is the keyboard user’s “you are
here”. 2.4.7 requires it to be visible, and 1.4.11 requires the indicator to reach
3:1 against the colours next to it. The catch is the word adjacent: a ring
sits between the component and the page, so it can need 3:1 against both.
A subtle blue glow may clear 3:1 against a white page but vanish against a blue
button; a thin light outline may show on a dark control but disappear on the dark
surface around it. Removing the indicator entirely with outline: none
is the most common failure of all — it deletes the only cue a keyboard user has.
Bad
The default outline is suppressed and replaced with a faint ring that only
shows against the page. On the primary button — a similar blue — the indicator
drops well under 3:1 and disappears.
bad-focus.css
:focus { outline: none; } /* removes the only keyboard cue — fails 2.4.7 */
.btn:focus-visible {
outline: 2px solid #93c5fd; /* light blue: <3:1 vs the blue button it rings */
}
Good
A solid ring with an offset so a sliver of page shows between ring and control.
A dark ring clears 3:1 against both the light page and the button; the offset
guarantees separation regardless of the component’s colour.
good-focus.css
.btn:focus-visible {
outline: 3px solid #1a1a1a; /* ~16:1 vs page, ~5:1 vs the button */
outline-offset: 2px; /* page shows through — separates ring from control */
}
Code
A two-tone ring is bullet-proof: a light core and a dark edge means one of the
two always clears 3:1 whatever it lands on. Scope it to
:focus-visible so it appears for keyboard use without flashing on
every mouse click.
two-tone-focus.css
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
outline: 2px solid #ffffff; /* light edge for dark surfaces */
outline-offset: 2px;
box-shadow: 0 0 0 4px #1a1a1a; /* dark edge for light surfaces */
}
/* One of the two layers always clears 3:1 against the adjacent colour */
How to fix
Never ship outline: none without a replacement indicator — a
missing focus ring fails 2.4.7 outright.
Check the indicator against both adjacent colours, the component and
the page, and require 3:1 against each.
Use outline-offset so a slice of background separates the ring
from the control, keeping it visible on any fill.
Prefer a two-tone (light + dark) ring so one layer always clears 3:1
whatever surface the control sits on.
Scope it to :focus-visible so keyboard users get a clear
indicator without it firing on every pointer click.
Recap
Give the boundary that identifies an input or button at least
3:1 against the page behind it — a faint hairline border fails
1.4.11 even when the label text passes 1.4.3.
Make meaningful graphics carry their meaning at 3:1: chart series must clear
3:1 against the plot background and be distinguishable from each other,
and an icon that conveys information must clear 3:1 against what’s behind it.
Ensure the focus indicator reaches 3:1 against every colour it can land on —
the component and the page — so keyboard users can always see where they are
(1.4.11 with 2.4.7).
The 3:1 rule is the same wherever non-text information lives. Hit
it for borders, graphics, and focus and you satisfy WCAG, EN 301 549, Section 508,
and ADA Title II together.