color-contrastwcagshopifydeveloper

Color Contrast in Shopify Themes: A Developer's Checklist

WCAG 1.4.3 and 1.4.11 color-contrast thresholds applied to Shopify themes. Where contrast usually breaks, which tools surface it, and the CSS and Liquid patterns that fix it.

By Radoslaw Fedorczuk10 min read

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:

  1. Button hover states, when the hover color is computed by lowering opacity rather than by a designed-in hover token.
  2. 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.
  3. Placeholder text in form inputs, which most themes set close to the input background instead of well below the body text contrast level.
  4. 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.
  5. Disabled-state buttons, where lowered opacity hides the failure visually but not from a screen reader following the same DOM.
  6. Sale, new, and sold-out badges placed against pastel page backgrounds.
  7. 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.

How to audit a theme's brand colors before launch

When a merchant picks brand colors, the typical Shopify onboarding flow does not warn about contrast. The merchant uploads a logo, picks a "primary" and "secondary" hex value in theme settings, and ships. Two months later they get an accessibility complaint.

Before launching with a new brand palette, run every pair of colors that will sit adjacent in the UI through a contrast check. There are 12 typical adjacency pairs in a Shopify theme:

  1. Body text / page background
  2. Heading / page background
  3. Link / page background
  4. Button text / button background
  5. Button text / button hover background
  6. Input text / input background
  7. Placeholder text / input background
  8. Label text / form section background
  9. Footer text / footer background
  10. Footer link / footer background
  11. Secondary text / tinted section background
  12. Badge text / badge background

Build a spreadsheet with these 12 rows, populate hex values from your theme settings, run each pair through the WebAIM Contrast Checker, record the ratio. Anything below 4.5:1 for text or 3:1 for UI needs adjustment.

A practical checklist for a Shopify color contrast audit

Run through this list on every theme change or new app installation.

  1. Body text passes 4.5:1 against page background.
  2. All heading levels pass 4.5:1 (or 3:1 if they qualify as large text per WCAG).
  3. Primary button default state passes 4.5:1 for button text.
  4. Primary button hover state passes 4.5:1 for button text.
  5. Secondary button default and hover states pass 4.5:1.
  6. Disabled button text passes 4.5:1 against disabled background (often missed).
  7. Form input border passes 3:1 against page background.
  8. Form input text passes 4.5:1 against input background.
  9. Placeholder text passes 4.5:1 against input background.
  10. Focus indicator passes 3:1 against the element's background.
  11. Link color passes 4.5:1 against page background and 3:1 against adjacent body text (so links are distinguishable from non-link text by something other than color, per WCAG 1.4.1).
  12. Footer text and links pass 4.5:1 against footer background.
  13. Sale or new badge text passes 4.5:1 against badge background.
  14. Icon-only buttons have a 3:1 contrast outline or filled background.

For more context on broader accessibility failures in Shopify themes, see our WCAG 2.2 theme breakdown.

Why color contrast scanners disagree sometimes

You may see Lighthouse pass a page that axe DevTools flags, or vice versa. This happens for three reasons:

  1. Dynamic content. If a button color is set by JavaScript after page load, some scanners measure the pre-script state.
  2. Background image detection. If text overlays an image, contrast cannot be reliably measured because the background varies pixel by pixel. Some scanners skip, some flag, some pick a sample.
  3. Overlapping elements. If a fixed-position header sits behind text in a transparent section, the scanner may use the wrong background for contrast math.

The most accurate signal is a hand check with Chrome DevTools color picker on the specific element you care about, with the page in its real rendered state.

What to do today

Open your store. Pick three pages: the homepage, a high-traffic product page, and the cart. Run Lighthouse on each. The "Contrast" entries under accessibility are your starting list. Fix the top 3 most-frequent failures (the same color variable usually causes failures in many places, so one fix can clear dozens of issues).

If you want a Shopify-specific scan that gives you the actual Liquid patch for each contrast failure, AccessifyAI does this. The scan is free for the homepage, a product page, and a collection. Find us on the Shopify App Store.

Frequently asked questions

Does WCAG distinguish between brand color and a chosen color palette?

No. WCAG applies the same contrast thresholds regardless of brand identity. If a brand color fails 4.5:1 in body text use, the brand color cannot be used in body text use. Brand identity is preserved by using the brand color in other roles (large headings, button backgrounds with white text, accent borders).

What is "large text" under WCAG?

Large text is defined as 18 point or larger, or 14 point bold or larger. In CSS pixels with the default browser size of 16 pixels per em, that is approximately 24 pixels for regular weight and 18.66 pixels for bold. Below these sizes, the 4.5:1 ratio applies. Above, 3:1 is sufficient.

Does dark mode change my contrast requirements?

The same thresholds apply. Dark mode inverts the typical foreground and background, but the math is identical. Many themes implement dark mode with a single CSS variable swap that does not verify all 12 adjacency pairs above. Re-audit when you ship dark mode.

Are gradients allowed?

Yes, provided the text contrasts against the lightest and darkest parts of the gradient (worst case). For text over a gradient, pick the gradient color closest to the text under the text region and verify against that. For UI elements over gradients, pick a fallback color in case the gradient does not load.

Do I need to fix contrast on images of text?

WCAG treats images of text the same as live text for contrast purposes. The criterion 1.4.5 Images of Text discourages this pattern altogether. The fix is usually to convert the image to HTML and CSS, which removes the contrast issue and improves SEO at the same time.

How often do I need to re-audit?

Re-audit on every theme version bump, when you change brand colors, when you install a new app that injects DOM, and quarterly as a baseline. AccessifyAI runs continuous scans for paid users.

Share:

Get accessibility tips by email

One short email per month with new guides and Shopify accessibility updates. No spam, unsubscribe anytime.

Color Contrast in Shopify Themes: A Developer's Checklist | AccessifyAI