Color contrast in Shopify themes must meet WCAG 2.2 success criteria 1.4.3 (4.5:1 for normal text, 3:1 for large text) and 1.4.11 (3:1 for non-text UI elements). The most common contrast failure surfaces in Shopify themes are button hover states, secondary text on tinted backgrounds, focus outlines, placeholder text in form inputs, and disabled-state buttons. Across the broader web, the WebAIM Million report has flagged insufficient text contrast as the most frequent automatable accessibility failure for several years in a row. Shopify themes mirror that pattern.
The thresholds you need to know
WCAG 2.2 defines two contrast criteria at Level AA:
- 1.4.3 Contrast (Minimum): text and images of text must have a contrast ratio of at least 4.5:1, except for large text (18 point or 14 point bold) which needs 3:1.
- 1.4.11 Non-text Contrast: user interface components (buttons, form input borders, focus indicators) and meaningful graphical objects (icons that convey information) must have a contrast ratio of at least 3:1 against adjacent colors.
A third criterion at Level AAA (1.4.6) requires 7:1 for normal text. Most stores target AA, so I focus on 4.5:1 and 3:1 throughout this article.
Contrast ratio is calculated from the relative luminance of two colors. The math is in WCAG's understanding documents. In practice, you do not calculate by hand. You feed two hex values into a tool and read the ratio. Below are the tools I use daily.
Tools for measuring contrast
| Tool | Use case | Cost |
|---|---|---|
| Chrome DevTools color picker | Quick check on a live element | Free |
| Lighthouse (Chrome DevTools) | Full-page automated scan | Free |
| axe DevTools (browser extension) | Full-page automated scan with rule explanations | Free tier |
| WebAIM Contrast Checker | Single foreground/background pair | Free |
| contrast-ratio.com | Single pair with permalink for sharing | Free |
| Stark (Figma plugin) | Design-phase check before code | Free tier |
| AccessifyAI scanner | Shopify-aware scan with Liquid fix patches | Free scan, paid fixes |
Chrome DevTools is the fastest tool for spot checks. Open DevTools, inspect an element, look at the Styles panel. The color swatch next to a color: declaration shows the contrast ratio against the element's background. If it shows a yellow or red icon, you have a failure.
Lighthouse and axe DevTools both flag contrast failures across an entire page. The difference: Lighthouse uses axe-core under the hood with a smaller rule set. axe DevTools exposes more granular rules. For Shopify scanning, axe is closer to the experience AccessifyAI provides.
Where Shopify themes fail most often
The recurring contrast trouble spots in Shopify themes, ordered roughly by how often they surface in real audits, are:
- Button hover states, when the hover color is computed by lowering opacity rather than by a designed-in hover token.
- Secondary or muted text on tinted section backgrounds (cream, light gray, soft brand tints) where the muted gray that passed on white now fails on the tint.
- Placeholder text in form inputs, which most themes set close to the input background instead of well below the body text contrast level.
- Focus outlines, especially when a theme replaces the default browser focus ring with a brand color that has insufficient contrast against the local section background.
- Disabled-state buttons, where lowered opacity hides the failure visually but not from a screen reader following the same DOM.
- Sale, new, and sold-out badges placed against pastel page backgrounds.
- Icon-only buttons (cart count, search, account) with foreground glyphs against very similar backgrounds.
The next three subsections walk through fixes for the first three.
Button hover state
The pattern: a primary button has a brand color background and white text. Default state passes contrast (say, 7:1). Hover state darkens or lightens the background by 10 to 15%. The hover state drops below 4.5:1 because most themes apply hover via a CSS variable shift without recalculating the resulting contrast.
A common pattern in themes that ship a built-in button component is to define the hover state as the base color with reduced opacity (something like rgb(var(--color-button) / 0.85)). Lowering opacity blends the button into the page background and lifts the apparent lightness of the button surface. For light page backgrounds, that lift drags the contrast of the white text on top closer to failure, sometimes pushing it below 4.5:1. Whether your specific theme exhibits this depends on the brand color and the page background, so verify with a contrast checker on the rendered hover state, not the design token.
The fix: switch from opacity-based hover to a defined hover color in the theme settings, and verify the contrast on both states.
.button--primary {
background: var(--color-button);
color: var(--color-button-text);
}
.button--primary:hover {
background: var(--color-button-hover);
color: var(--color-button-text);
}
Add --color-button-hover to your theme settings schema with a contrast-verified value.
Secondary text on tinted background
The pattern: a section uses a soft tint (light beige, light gray, light blue) as its background. The primary body text is dark enough to clear 4.5:1 on white, and clears it on the tint too. The secondary or "muted" text uses a gray tuned for the white page background. On the tint, the foreground is too close to the new background and the ratio dips below 4.5:1.
The fix is either a section-specific secondary text color or a darker baseline gray that clears 4.5:1 on every tint your theme can place behind it. Plug the foreground and the tint into a contrast checker, find a gray dark enough to clear 4.5:1 on the worst tint, and use that as your secondary text variable everywhere.
Focus outline
The pattern: a theme either removes focus outlines globally (outline: none) or replaces the browser default with a custom outline that has insufficient contrast against the element's background.
The lazy fix is:
*:focus {
outline: 2px solid var(--color-link);
}
The problem with this is --color-link is usually a brand color tuned for body copy on white. On a dark hero section or a colored card, it may have a contrast ratio under 3:1 against the surrounding background, failing 1.4.11.
The robust fix uses :focus-visible and applies a double-ring outline that combines a high-contrast color with a contrasting offset:
*:focus-visible {
outline: 2px solid var(--color-focus);
outline-offset: 2px;
box-shadow: 0 0 0 4px var(--color-focus-secondary);
}
Define --color-focus and --color-focus-secondary such that one passes against light backgrounds and the other against dark. A common pairing is #000000 and #ffffff, which gives you maximum contrast against any background color the user encounters.