contrast-minimum
Text meets minimum color contrast (AA)
Checks that visible text has a contrast ratio of at least 4.5:1 (normal) or 3:1 (large), when contrast is computable from CSS.
Applies to. Applies to the visible text contrast-computable applies to, see that rule for the eligibility gates, narrowed to text whose background and foreground are actually computable. Eligible text that is not computable leaves this rule notApplicable rather than cantTell: reporting that uncertainty belongs to contrast-computable, so the two never report the same text twice.
Expectation. Every computable text node reaches the ratio SC 1.4.3 requires for its size: 3:1 for large text, 4.5:1 for everything else. Text is large at 24px or more, or at 14pt (about 18.667px) or more when the computed font weight is 700 or higher. Text drawn by a -webkit-text-stroke or a text-shadow is left to contrast-computable, except where every color it is drawn in, its fill among them unless the fill draws nothing (transparent, or the color of its background), is below the ratio against a flat background: no reading of it can then meet the criterion, and it fails, measured by the best of those colors (drawnBy: textStroke or textShadow). Margin (contrast-ratio): of the text that reaches its ratio, the element closest to it, with the ratio unrounded against the one its size requires; context.largeText says which. Only text within 10% of its ratio counts as close, so text further above it gives no margin. measuredCount counts the elements whose contrast was computed. Text part of which a ::first-line or ::first-letter paints in a color of its own is measured in both colors: where both reach their ratio it passes, where both fall short it fails (pseudoElementColor names the pseudo-element), and where they disagree it is left to contrast-computable. contrast-computable decides what to ask about at WCAG's ratios. At ratios of this rule's own (a variant's settings), text in two colors that disagree only at those ratios, and text drawn by an outline or a shadow whose best color reaches them but not WCAG's AA ratio, are asked about here (PSEUDO_ELEMENT_COLOR, TEXT_STROKE, TEXT_SHADOW), since no other rule would. Text drawn by an outline whose best color lies between WCAG's AA ratio and a higher ratio of the variant's fails here and is asked about by contrast-computable too: two answers to two requirements, the variant's and WCAG 1.4.3. A field's placeholder below its ratio (PLACEHOLDER_BELOW_THRESHOLD) fails when the page chose its colors: the placeholder's color, opacity or background, or the field's background, differ from the browser's own, read in a blank frame. A placeholder left in the browser's own colors is taken to meet the ratio (WCAG G148), and when that can't be told it is a cantTell occurrence. Text with no opaque background behind it, up to the root, is measured on the canvas the browser paints under the page, in either contrast mode: the Canvas color of the color scheme the page is drawn in, which the page's own color-scheme (on the root, or in <meta name="color-scheme">) decides, as CSS Color Adjustment 1 defines it. A page that declares no scheme is drawn in light, on white, whatever the user prefers; one that declares only dark is drawn on the dark canvas. In a browser the colors are read from the page; an emulator (jsdom), which resolves no system color, takes Chromium's (#121212 for the dark canvas, #ffffff for the text a dark scheme gives). A canvas the scan names, when contrast.rootCanvasFallback gives one, is measured in its place. Where the canvas can't be known (in a frame, where the page around it may show through; under forced colors, where it is the user's palette) the text is left to contrast-computable, or measured on the fallback with the auditorAssist mode. A page that supports both schemes (light dark) is drawn in the one the user prefers. The scan sees that one (in a browser, as it is drawn; jsdom draws light), and its text is measured there and, where its colors can't change with the scheme, in the other too, on that scheme's canvas: not where a rule in a prefers-color-scheme query, a light-dark() value or a system color (other than the scheme's own text and link colors, where no rule colors the text) reaches the text's color or a background behind it. It fails where it fails in either scheme measured, and passes where it passes in each.
What a finding reports.
metrics.ratio(text below the threshold): the text's contrast ratio against its background (4.5 for 4.5:1), againstmetrics.threshold, the ratio its size requires.colors.foregroundHex,colors.backgroundHex(text below the threshold): the text color (the -webkit-text-fill-color it is painted in,colorunless set) and the background color behind it, as hex, after any transparency is blended in.colors.foregroundRgbaandcolors.backgroundRgbagive the same two colors inrgba()form.selectField(a drop-down select in the browser's own background):backgroundHex, its computed background, andfieldHex, the Field color Chromium paints it in. The text is measured against both and the lower ratio is reported.typography.fontSizePx,typography.fontSizePt(text below the threshold): the font size as drawn, in CSS pixels and in points: the computed size times CSS zoom, or for SVG text its viewBox and transform scale, and times what font-size-adjust scales the font by (its adjust value over the font's own aspect, read in a browser). This is the size the large-text threshold is judged on, as WCAG's "size when the content is delivered".typography.declaredFontSizePx,typography.sizeScaledBy(text below the threshold, drawn at another size): the computed font-size, and what drew it otherwise (zoom,svg,font-size-adjust). Where that puts the text on the other side of the large-text line, the hint says so (contrast_hint_drawnSize).typography.fontWeight,typography.fontWeightLabel(text below the threshold): the computed font weight as a number (400, 700) and as written.typography.isBold,typography.isLargeText(text below the threshold): whether the text counts as bold, and as large text, the size that only needs 3:1.- The ratio, threshold and typography fields above are a stable contract, unlike the rest of
data.details. assumptionsApplied,assumedRootCanvasColor(text below the threshold):nullunless the page sets no opaque background and the text was measured on a canvas color the scan names (contrast.rootCanvasFallback), or, with theauditorAssistmode where the page's own canvas can't be known (in a frame, under forced colors), on the fallback (white by default). ThenassumptionsAppliedis['ROOT_CANVAS_FALLBACK']andassumedRootCanvasColoris that color.colorScheme(text below the threshold, on the canvas of a page that supports light and dark):'light'or'dark', the scheme in which it falls short, where it passes in the other, and the hint names it; the ratio and colors are that scheme's. Absent where it falls short in every scheme measured (then reported in the scheme the page is drawn in) and on a page with one scheme.colorSchemesChecked(text below the threshold, and the pass): the schemes the text on the canvas was measured in,['light']or['dark'], where the page supports both and one could not be measured. A binding can scan again with the browser in the other scheme. Absent where every scheme the page supports was measured.generatedBy(generated text below the threshold):::before,::afteror::marker, the pseudo-element whose text is measured; the selector names its element, and the colors and font are the pseudo-element's.eligibleTextCount,computableTextCount(the pass, and the result's owndata.detailswhen it is notApplicable because no text was computable, which lists no occurrence): how many text nodes the rule looked at, and how many of them had colors it could compute. Repeated undermetricson a pass.forcedColors(every occurrence, and that notApplicable result's details, when the colors measured were forced):true, and the hint says to check the page again with forced colors off. A scan under forced colors measures the page's own colors, so this is there only where it could not keep the page in them.
These data.details fields help to read and reproduce a finding. Apart from reasonCode and the few fields Output schema names as stable, they are not a stable contract: a field may be renamed or dropped in a minor release, so don’t build on them.
Margin. This rule measures contrast-ratio as a ratio, and the value must reach the threshold. When a pass came near the threshold, the result’s margin names the element that came closest while still meeting it, so a pass that a small change would flip shows before it flips. Unlike the fields a finding reports, it is a stable contract. See Output schema.
Examples
<p style="background:#ffffff; color:#767676;">Body text</p>
About 4.6:1, above the AA 4.5:1 minimum for normal text.
<div style="background:#ffffff;"><div style="opacity:0.5; background:#ffffff;"><p style="color:#000000;">Body text</p></div></div>
50% ancestor (group) opacity over white composites to a flat gray around 4:1, below the AA minimum.
How to check
When this rule can’t decide, it reports cantTell and leaves the question to a person. Each question it can ask is listed here under its reason code, with the steps to answer it. A finding carries the same steps as howToCheck, in the scan’s language. See Output schema.
CONTRAST_SWITCH_OFFERED
- Find the contrast switch the report names and check that it is easy to find, near the top of the page or on every page, and reachable by keyboard.
- Turn the switch on.
- Find the text the report names and use the color picker in the browser's developer tools, or a contrast calculator with colors picked by an eyedropper, to measure it against its background, comparing with 4.5:1 (3:1 for large text: at least 24px, or 18.66px and bold).
- Move to another page and check that the setting holds.
Pass. The switch is easy to find and use, and with it on the text reaches its ratio (G174).
Fail. The switch is hard to find or does not raise this text to its ratio, so the text fails WCAG 1.4.3.
ENGINE_EXCEPTION
- Find the text the report names (the check stopped early, so nothing was measured).
- Use the color picker in the browser's developer tools, or a contrast calculator with colors picked by an eyedropper, to measure the text color against the background directly behind the letters.
- Note the text size and weight and compare the ratio with 4.5:1 (3:1 for large text: at least 24px, or 18.66px and bold).
Pass. The ratio reaches the threshold for its size.
Fail. The ratio is below the threshold for its size, which fails WCAG 1.4.3.
LOGO_TEXT_BELOW_THRESHOLD
- Find the text the report names (often in the site header or its home link).
- Decide whether it is part of a logo or brand name, such as the site's name in its logo treatment, rather than a slogan, menu item or other text.
- If it is not logo text, use the color picker in the browser's developer tools, or a contrast calculator with colors picked by an eyedropper, to measure it against its background and compare with 4.5:1 (3:1 for large text: at least 24px, or 18.66px and bold).
Pass. The text is part of a logo or brand name, which has no contrast requirement, or it reaches its ratio.
Fail. The text is not part of a logo or brand name and is below its ratio, which fails WCAG 1.4.3.
PLACEHOLDER_BELOW_THRESHOLD
- Find the field the report names and empty it so the placeholder shows.
- In the Styles pane, check whether the page's CSS sets the ::placeholder color or opacity, or the field's background.
- Use the color picker in the browser's developer tools, or a contrast calculator with colors picked by an eyedropper, to measure the placeholder text against the field background and compare with 4.5:1 (3:1 for large text: at least 24px, or 18.66px and bold).
- Check whether the placeholder holds information needed to fill the field (a format or instruction), rather than just an example.
Pass. The placeholder reaches 4.5:1 (or 3:1 if large), or it is drawn entirely in the browser's own default colors.
Fail. The page sets the placeholder or field colors and the ratio is below the threshold, which fails WCAG 1.4.3.
PSEUDO_ELEMENT_COLOR
- Find the text the report names and note the ::first-line or ::first-letter color in the Styles pane.
- Use the color picker in the browser's developer tools, or a contrast calculator with colors picked by an eyedropper, to measure both the element's text color and the first-line or first-letter color against the background.
- Resize the window to see which words the first-line color paints at different widths.
- Compare each ratio with the one the report gives as needed for that color: 4.5:1 for most text, or 3:1 for large text (at least 24px, or 18.66px and bold).
Pass. Both colors reach the ratio they need.
Fail. Either color gives a ratio below the one it needs, so the text fails the check.
SEMIBOLD_BELOW_THRESHOLD
- Find the semi-bold text the report names.
- Compare it side by side with bold text (weight 700) of the same size and font on the page or in the developer tools, toggling font-weight.
- Decide whether it reads as bold; if it does, it counts as large and needs 3:1, otherwise 4.5:1.
Pass. The text reads as bold, so it is large text and its ratio meets 3:1.
Fail. The text does not read as bold, so it is normal text below 4.5:1, which fails WCAG 1.4.3.
SYMBOLIC_CHARACTER_BELOW_THRESHOLD
- Find the character the report names (such as ×, ☰, › or an icon-font glyph).
- Decide whether it is text people read (part of words, a number) or an icon standing in for an action, like a close or menu symbol.
- If it is text, use the color picker in the browser's developer tools, or a contrast calculator with colors picked by an eyedropper, to measure it against its background and compare with 4.5:1 (3:1 for large text: at least 24px, or 18.66px and bold).
- If it is an icon, check first whether visible text next to it says the same thing, such as the word "Close" beside a close symbol. If it does, the icon is not the only way to understand it and does not need the 3:1 ratio.
- Otherwise, measure the icon against its background and compare with 3:1, the ratio for graphical objects.
Pass. It is text and reaches its ratio, or it is an icon that reaches 3:1 or whose meaning is also given by visible text next to it.
Fail. It is text below 4.5:1, which fails WCAG 1.4.3; or an icon below 3:1 that is the only visible way to know what it means or does, which fails WCAG 1.4.11.
TEXT_SHADOW
- Find the text the report names and note its text-shadow.
- Zoom in and check whether the shadow forms a solid outline or halo around every letter.
- Use the color picker in the browser's developer tools, or a contrast calculator with colors picked by an eyedropper, to measure the color that forms the letters against the background.
- Compare the ratio with the one the report gives as needed: 4.5:1 for most text, or 3:1 for large text (at least 24px, or 18.66px and bold).
Pass. The color that forms the letters reaches the ratio needed.
Fail. The color that forms the letters gives a ratio below the one needed, so the text fails the check.
TEXT_STROKE
- Find the text the report names and note its -webkit-text-stroke width and color.
- Zoom in to see which color forms most of each letter (a thin stroke counts less than the fill).
- Use the color picker in the browser's developer tools, or a contrast calculator with colors picked by an eyedropper, to measure that color against the background.
- Compare the ratio with the one the report gives as needed: 4.5:1 for most text, or 3:1 for large text (at least 24px, or 18.66px and bold).
Pass. The color that forms the letters reaches the ratio needed.
Fail. The color that forms the letters gives a ratio below the one needed, so the text fails the check.