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.
Table of Contents
- 1. Why color alone is not enough as a distinguishing feature
- 2. Color blindness and low vision: who is affected
- 3. Form validation: the classic red-green problem
- 4. Links in body text: color alone is not enough
- 5. Status badges, traffic-light systems, and charts
- 6. Techniques for a second distinguishing cue
- 7. Testing: simulating color blindness and checking contrast
- 8. Implementation in Magento and the Hyvä Theme
- 9. Before and after in direct comparison
- 10. Summary
- 11. FAQ
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>
4. Links in body text: color alone is not enough
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.