Color is not used as the only visual means of conveying information
1. Perceivable
1.4 Distinguishable
WCAG 2.0
Information, actions, prompts, and visual elements must be identifiable without relying solely on color. About 8% of men and 0.5% of women have some form of color vision deficiency, making color-only communication inaccessible to millions of users.
Please enter a valid email address
Password is valid
Error: Please enter a valid email address
Success: Password is valid
✅ Icons and text make the status clear regardless of color perception!
✅ Patterns and labels make data clear!
• Print page in grayscale
• Use browser color blindness simulators
• Cover color elements and check if meaning is clear
• Ask color blind users to test
• WAVE color dependency checker
• axe DevTools color analysis
• Stark browser extension
• Colour Contrast Analyser
<!-- Accessible error styling -->
<div class="form-field error">
<label for="email">Email Address *</label>
<input type="email" id="email" aria-describedby="email-error"
class="input-error" value="invalid">
<div id="email-error" class="error-message" role="alert">
<svg aria-hidden="true" class="error-icon">
<use href="#error-icon"></use>
</svg>
Error: Please enter a valid email address
</div>
</div>
<style>
.form-field.error input {
border: 2px solid #dc2626;
background-color: #fef2f2;
/* Don't rely only on color */
}
.error-message {
color: #dc2626;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
margin-top: 0.25rem;
}
.error-icon {
width: 1rem;
height: 1rem;
flex-shrink: 0;
}
/* Success state */
.form-field.success input {
border: 2px solid #16a34a;
background-color: #f0fdf4;
}
.success-message {
color: #16a34a;
display: flex;
align-items: center;
gap: 0.5rem;
}
</style>// Accessible status indicator component
function StatusIndicator({ status, message }) {
const statusConfig = {
error: {
icon: <XIcon className="w-4 h-4" />,
colorClass: 'text-red-600 bg-red-50 border-red-200',
prefix: 'Error: '
},
success: {
icon: <CheckIcon className="w-4 h-4" />,
colorClass: 'text-green-600 bg-green-50 border-green-200',
prefix: 'Success: '
},
warning: {
icon: <AlertTriangleIcon className="w-4 h-4" />,
colorClass: 'text-yellow-600 bg-yellow-50 border-yellow-200',
prefix: 'Warning: '
}
};
const config = statusConfig[status];
return (
<div
className={`flex items-center gap-2 p-3 border rounded ${config.colorClass}`}
role="alert"
aria-live="polite"
>
{config.icon}
<span className="font-medium">{config.prefix}</span>
{message}
</div>
);
}
// Accessible link component
function AccessibleLink({ href, children, isExternal }) {
return (
<a
href={href}
className="text-blue-600 underline hover:text-blue-800 focus:outline-none focus:ring-2 focus:ring-blue-500"
{...(isExternal && {
target: "_blank",
rel: "noopener noreferrer",
'aria-label': `${children} (opens in new tab)`
})}
>
{children}
{isExternal && (
<ExternalLinkIcon className="w-4 h-4 inline ml-1" aria-hidden="true" />
)}
</a>
);
}