Not Using Color as the Only Means of Distinction
AI generated
A11Y
WCAG
Accessibility · WCAG 2.2 · Color Contrast · Hyvä Theme
Not Using Color as the Only Means of Distinction
Icons, text, and patterns instead of pure color coding

Marking errors only in red, success only in green, and links only through color excludes color-blind and low-vision users from important information. A second cue such as an icon, text label, or underline makes forms, status badges, and links reliably readable for every user group and satisfies WCAG 1.4.1 without extra design effort.

12 min. read WCAG 1.4.1 · Form Validation · Color Blindness Magento 2.4.8 · Hyvä Theme · Alpine.js

1. Why color alone is not enough as a distinguishing feature

A red input field for errors, a green checkmark for success, a blue link in body text: these patterns show up in almost every interface, and in most cases color is the only signal being used. That is exactly the problem. As soon as information is conveyed exclusively through a color value, part of the user base loses that information entirely, without the interface itself giving any hint that something is even missing. The page looks complete to developers and designers, but it is not complete for a portion of its visitors.

This exact case is covered by the success criterion WCAG 1.4.1 Use of Color, a Level A criterion and therefore part of the legal baseline under BITV 2.0 and EN 301 549. The rule does not require abandoning color, it requires a second, color-independent feature for any information that is currently coded only through color. Purely decorative use of color, such as a brand gradient in the header, is explicitly exempt from the rule.

2. Color blindness and low vision: who is affected

Roughly eight percent of all men and just under half a percent of all women have some form of red-green color blindness, most commonly deuteranopia or protanopia. Worldwide that adds up to a rough estimate of over 300 million people, meaning that in an average online store with a meaningful share of male traffic this is a relevant, by no means negligible user group. Rarer but equally relevant is blue-yellow color blindness, tritanopia, along with complete color blindness, achromatopsia, in which no color differences are perceived at all.

On top of that come situational and age-related limitations that go far beyond classic color blindness: cataracts and age-related macular degeneration gradually change color perception, a smartphone display in bright sunlight drastically reduces color differences, and a PDF or e-book printed or viewed in grayscale mode loses any color coding entirely. Relying on color alone therefore creates a barrier not just for one fixed group, but for practically any user under certain conditions.

3. Form validation: the classic red-green problem

The most common practical example of color as the only distinguishing feature is form validation. An input field gets nothing but a red border on error and a green border on success, with no text, no icon, and no aria-invalid. For a user with red-green color deficiency both states look nearly identical, and for screen reader users the state does not exist at all, because it lives purely in the visual CSS.

The solution combines three layers: an additional icon right at the field, a visible error text linked to the field via aria-describedby, and the aria-invalid="true" attribute for assistive technology. Color remains in place as a fast visual signal, but it no longer carries the entire burden of meaning on its own. The example below shows the difference between a color-coded state and a fully accessible state directly in the markup.


<!-- WRONG: color is the only signal, no icon, no text, no ARIA -->
<div>
  <label for="email">Email address</label>
  <input type="email" id="email" class="border-2 border-red-500">
</div>

<!-- RIGHT: color plus icon plus text plus ARIA, three redundant signals -->
<div>
  <label for="email-fixed" class="font-medium">Email address</label>
  <div class="relative">
    <input
      type="email"
      id="email-fixed"
      class="border-2 border-red-500 pr-10"
      aria-invalid="true"
      aria-describedby="email-fixed-error"
    >
    <svg class="absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 text-red-600"
         aria-hidden="true" fill="none" stroke="currentColor" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"/>
    </svg>
  </div>
  <p id="email-fixed-error" class="text-red-600 text-sm mt-1 flex items-center gap-1">
    Please enter a valid email address in the format name@example.com.
  </p>
</div>

Within body copy, links that are distinguished from surrounding text purely by a different hue are a classic finding in WCAG audits. A user with deuteranopia often barely distinguishes a muted blue from dark gray, especially when the contrast between the link color and the body text color sits just above the minimum requirement. Without a second cue, links then visually vanish into the text, and navigating by link text becomes effectively impossible, without the user even noticing that interaction options are being missed.

The reliable fix is refreshingly simple: underline links in body text by default, regardless of text color. Alternatively, WCAG 1.4.1 requires at least a 3:1 contrast ratio between link and body text color plus a non-color indicator on hover or focus, such as a stronger underline or a border. Navigation bars and buttons are exempt from this rule, because there position and shape already act as the second distinguishing feature.


/* WRONG: link relies on color only, no underline, low contrast to body text */
.prose a {
  color: #6366f1;
  text-decoration: none;
}

/* RIGHT: underline as permanent second signal, independent of color perception */
.prose a {
  color: #4338ca;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.prose a:hover,
.prose a:focus-visible {
  text-decoration-thickness: 2px;
  outline: 2px solid transparent;
  outline-offset: 2px;
}

/* Focus indicator must never rely on color change alone */
.prose a:focus-visible {
  box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px #18181b;
}

5. Status badges, traffic-light systems, and charts

Online stores use traffic-light systems almost everywhere: a green dot for "In stock", a yellow one for "Low stock", a red one for "Out of stock". Dashboards encode revenue trends in charts exclusively through line color, and legends often consist of nothing but colored boxes with no label directly on the element. For a user who cannot reliably tell red and green apart, an "In stock" dot and an "Out of stock" dot look nearly identical, with an immediate effect on a purchase decision.

The fix is usually a pure addition, not a redesign: a text label next to or inside every badge, distinct icon shapes instead of just different colors, and in charts, patterns such as dashed, dotted, or solid lines in addition to color coding. Circular, square, and triangular markers in a scatter plot stay clearly distinguishable even in grayscale, whereas a color-only legend does not.

6. Techniques for a second distinguishing cue

In practice, a handful of recurring techniques cover almost every case: icons with an unambiguous shape (checkmark, cross, exclamation mark in a triangle), visible text labels instead of pure symbolism, underlines or borders as a structural feature, hatching and patterns in filled areas, and different shapes for markers and badges. What matters is that each of these features works on its own, meaning it stays understandable even when color is removed entirely, for instance during a grayscale test.

In a Hyvä interface with Alpine.js, icon, text label, and color class can be bound to the same reactive state, so all three features change in sync with the actual validation status and cannot accidentally drift apart. The component below shows a form field whose icon, text, and border color are all driven from a single Alpine data object.


// Alpine.js component: icon, text label and color derive from one shared state
document.addEventListener('alpine:init', () => {
  Alpine.data('validatedField', (initialValue = '') => ({
    value: initialValue,
    status: 'idle', // idle | valid | invalid

    validate() {
      const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.value);
      this.status = this.value === '' ? 'idle' : (isValid ? 'valid' : 'invalid');
    },

    get borderClass() {
      return {
        idle: 'border-slate-300',
        valid: 'border-green-600',
        invalid: 'border-red-600',
      }[this.status];
    },

    get iconName() {
      return { idle: '', valid: 'check-circle', invalid: 'exclamation-circle' }[this.status];
    },

    get statusText() {
      return {
        idle: '',
        valid: 'Valid email address',
        invalid: 'Please enter a valid email address',
      }[this.status];
    },
  }));
});

7. Testing: simulating color blindness and checking contrast

Color-as-the-only-cue problems can be checked systematically before every release. Chrome DevTools ships an "Emulate vision deficiencies" option under "Rendering" that simulates protanopia, deuteranopia, tritanopia, achromatopsia, and blurred vision directly in the browser, with no plugin required. Firefox offers a comparable tool in its Accessibility Inspector. For design reviews before any coding happens, Stark for Figma and Sketch or the desktop app Sim Daltonism, which renders the entire screen live in various color-blindness modes, work well.

A simple but effective additional test: convert the whole page to grayscale, for example with a CSS filter filter: grayscale(100%) in DevTools, and check whether every piece of information is still understandable. If a meaning stays unclear in the grayscale image, it was previously coded through color alone. A contrast checker such as the one from WebAIM or axe DevTools additionally verifies whether colored indicators themselves reach the minimum 3:1 ratio against the background, independent of the second-cue question.

8. Implementation in Magento and the Hyvä Theme

In many Hyvä checkouts a single border-red-500 class in the field component marks an invalid state, with no accompanying icon or ARIA attribute. The sustainable fix is a reusable phtml component for form fields that derives icon, error text, and ARIA attributes consistently from one shared state, instead of being reimplemented, and potentially incompletely, in every individual template. That keeps accessibility maintained centrally instead of scattered across dozens of places in the theme.

For status badges such as stock level or order status, a central mapping table between status value, icon name, and text label is worth the effort, so that icon and text can never be maintained independently of color in the template and drift apart. The example below shows a Hyvä component for a stock badge along with the matching configuration as JSON, which can be maintained centrally in the ViewModel.


<!-- Hyvä phtml: stock badge with icon and text label, not color alone -->
<?php /** @var \Mironsoft\Accessibility\ViewModel\StockStatus $stockStatus */ ?>
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold
             <?= $escaper->escapeHtmlAttr($stockStatus->getBadgeColorClass($status)) ?>">
    <svg class="w-3.5 h-3.5" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20">
        <?= /* @noEscape */ $stockStatus->getBadgeIconPath($status) ?>
    </svg>
    <span><?= $escaper->escapeHtml($stockStatus->getBadgeLabel($status)) ?></span>
</span>

{
  "in_stock":  { "colorClass": "bg-green-100 text-green-700", "icon": "check-circle", "label": "In Stock" },
  "low_stock": { "colorClass": "bg-amber-100 text-amber-700", "icon": "exclamation-triangle", "label": "Low Stock" },
  "out_of_stock": { "colorClass": "bg-red-100 text-red-700", "icon": "x-circle", "label": "Out of Stock" }
}

9. Before and after in direct comparison

The overview below summarizes the most common color-as-the-only-cue patterns found in practice and shows the concrete, usually minimal-invasive fix for each one.

Element Color only (bad) Color plus second cue (good) Added benefit
Form error Red border only Border + icon + error text + aria-invalid Perceivable for screen readers too
Success message Green text only Green text + checkmark icon + word "Success" Unambiguous without color perception
Link in body text Colored text only Colored text + permanent underline Navigable even in grayscale
Required field Red asterisk alone Asterisk + word "Required" + aria-required Works for screen readers and color blindness
Stock traffic light Colored dot only Dot + text "In Stock" / "Out of Stock" Purchase decision remains unambiguous
Chart legend Color-coded lines only Color + line pattern + labels on the line Readable even in grayscale printouts

What stands out in the table: not a single fix requires giving up color. Color remains in place in every case as a fast, intuitive signal, it just loses its role as the sole carrier of information. The extra effort for an icon, a text label, or an underline is usually minimal compared to the benefit for color-blind, low-vision, and situationally limited visitors.

Mironsoft

Web accessibility, WCAG audits, and Hyvä implementation for Magento stores

Interfaces that are unambiguous for every user?

We review your store's forms, status badges, and links for pure color coding, add icons, text labels, and patterns, and make sure WCAG 1.4.1 stays satisfied permanently, not just for a one-time audit.

Accessibility Audit

Systematic review for color-as-the-only-cue violations

Hyvä Components

Reusable form and badge components with icon and ARIA support

Test Automation

Color-blindness simulation and contrast checks in the CI pipeline

10. Summary

Color as the only distinguishing feature is one of the most frequently overlooked barriers on the web, because it works fine in the design and only fails visibly for a subset of users. WCAG 1.4.1 does not require banning color, it requires a second, color-independent feature for any information that is coded through color: icon, text label, underline, pattern, or shape. Form validation, links in body text, status badges, and chart legends are the four most common practical cases where this second cue is missing.

The technical implementation is usually minimal-invasive: an additional SVG icon, a visible text, a permanent underline, or aria-invalid plus aria-describedby for screen readers. In Hyvä themes this can be solved cleanly through central, reusable components and Alpine.js state, so that icon, text, and color are never maintained independently of each other. Regular testing with color-blindness simulation and the grayscale check reliably surfaces remaining gaps before they go live.

Not Using Color as the Only Means of Distinction, the essentials at a glance

WCAG 1.4.1

Level A, a legal minimum standard. Requires a second, color-independent feature, not a ban on color.

Forms

Icon + error text + aria-invalid + aria-describedby instead of just a red border.

Links & Badges

Permanent underline in body text, text label next to every status badge.

Testing

Check DevTools vision-deficiency emulation and grayscale filters before every release.

11. FAQ: Not Using Color as the Only Means of Distinction

1Why is color alone not enough as a distinguishing feature?
Some users do not reliably perceive the color difference, for example due to color blindness or reduced visual acuity. Without an icon or text, the information is completely lost for these users.
2What exactly does WCAG 1.4.1 Use of Color require?
An additional, color-independent feature for any information coded purely through color, for example text, an icon, a pattern, or an underline. Decorative color use is exempt.
3How many people are affected by color blindness?
Roughly eight percent of all men and just under half a percent of all women, over 300 million people worldwide. Add to that low-vision and age-affected users.
4What forms of color blindness exist?
Protanopia and deuteranopia (red-green, most common), tritanopia (blue-yellow, rarer), and achromatopsia (complete color blindness).
5How do I indicate form errors without relying only on red?
An icon at the field, a visible error text linked via aria-describedby, plus aria-invalid for screen readers. The red border stays as an additional signal, but no longer carries the meaning alone.
6Do links in body text always need to be underlined?
Not strictly, but it is the simplest option. Alternatively, at least 3:1 contrast plus a non-color-based hover or focus indicator.
7Which tools simulate color blindness?
Chrome DevTools Rendering tab, Firefox Accessibility Inspector, Sim Daltonism as a desktop app, Stark as a plugin for Figma and Sketch.
8Is a contrast ratio of 3:1 enough for colored indicators?
3:1 is required by WCAG 1.4.11 for borders and icons, but alone it does not solve the pure color coding problem. Both criteria apply independently.
9How do I implement this in Hyvä and Alpine.js?
Bind icon, text, and color class to the same Alpine state, so all three change in sync. A central phtml component prevents forgotten second cues.
10Is the grayscale test a reliable check?
Yes as a quick heuristic test, but it does not replace a full review with real color-blindness simulations and screen readers.