Availability slots distinguish available, booked, blocked, and past using color plus non-color indicator
Why it matters
Color is not a reliable communication channel. Roughly 8% of men have color vision deficiency, making red/green-only slot states indistinguishable. This violates WCAG 2.2 SC 1.4.1 (Use of Color) directly, and SC 1.4.3 (Contrast Minimum) compounds the problem when low-contrast grays are used for past/blocked states. The legal exposure under ADA Title III is real for booking platforms serving the public. Beyond compliance, a color-blind user who cannot distinguish booked from available slots will either book into a conflict or abandon the flow — both outcomes cost revenue.
Severity rationale
High because color-only slot states exclude approximately 8% of male users from reliably distinguishing bookable slots, violating WCAG 2.2 SC 1.4.1 and creating legal exposure under ADA Title III.
Remediation
Pair every slot state color with an independent non-color indicator — an icon, a text label, or a pattern — so the state is decodable without color perception. Map all four states explicitly.
// src/components/CalendarSlot.tsx
const STATE = {
available: { bg: 'bg-green-100', icon: '✓', label: 'Available' },
booked: { bg: 'bg-red-100', icon: '✕', label: 'Booked' },
blocked: { bg: 'bg-gray-200', icon: '⊘', label: 'Blocked' },
past: { bg: 'bg-gray-100', icon: '◄', label: 'Past' },
} as const
export function CalendarSlot({ status, time }: { status: keyof typeof STATE; time: string }) {
const s = STATE[status]
return (
<div className={`${s.bg} p-2 rounded flex items-center gap-1`} aria-label={`${s.label}: ${time}`}>
<span aria-hidden="true">{s.icon}</span>
<span className="text-xs font-medium">{time}</span>
</div>
)
}
Verify contrast ratios for all four background/text combinations using the WCAG contrast checker — aim for ≥4.5:1 for normal text.
Detection
-
ID:
visual-distinction -
Severity:
high -
What to look for: Enumerate all slot states rendered in the calendar component. For each state (available, booked, blocked, past), count the number of independent visual cues (color, icon, text label, pattern, tooltip). Inspect the calendar slot component for CSS classes, inline styles, and rendered text/icons per state. Examine files matching
src/components/*Slot*,src/components/*Calendar*,src/components/*slot*.- Color alone: If only colors distinguish states (e.g., green for available, red for booked), fail. Color-blind users cannot distinguish them.
- Color + indicator: If colors are paired with icons, labels, patterns, or text, pass. Example: green checkmark for available, red "X" for booked, gray with striped pattern for past.
- States covered: Check if all 4 states are visually distinct: available (bookable), booked (occupied), blocked (admin-blocked), past (cannot book).
-
Pass criteria: Count all slot states rendered in the calendar. Each of the 4 states (available, booked, blocked, past) must use at least 2 independent visual cues (e.g., color + icon, color + text label, pattern + tooltip). No more than 0 states may rely on color alone. Report: "X of 4 slot states use 2+ independent visual cues."
-
Fail criteria: States are distinguished by color alone, or no visual distinction between booked and blocked slots.
-
Do NOT pass when: Booked and blocked slots share the same styling (even if both have icons) — these are distinct states that require distinct visual treatment.
-
Skip (N/A) when: Calendar does not show slot availability states (e.g., text list instead of grid, or single-state display).
-
Detail on fail: Example:
"Available slots are green, booked are red. No icons or text labels. A color-blind user cannot tell which slots are available." -
Cross-reference: Check
unavailable-feedback— visual distinction and click feedback work together for unavailable slots. -
Cross-reference: Check
keyboard-navigation— visual focus indicators must also be distinguishable alongside state colors. -
Cross-reference: Check
past-date-protection— past dates are one of the 4 states that need distinct visual treatment. -
Remediation: Use color + additional indicators:
interface SlotProps { status: 'available' | 'booked' | 'blocked' | 'past' time: string } export function CalendarSlot({ status, time }: SlotProps) { const statusConfig = { available: { bg: 'bg-green-100', text: 'text-green-900', icon: '✓', label: 'Available', }, booked: { bg: 'bg-red-100', text: 'text-red-900', icon: '✕', label: 'Booked', }, blocked: { bg: 'bg-gray-200', text: 'text-gray-700', icon: '⊘', label: 'Blocked', }, past: { bg: 'bg-gray-100', text: 'text-gray-500', icon: '◄', label: 'Past', }, } const config = statusConfig[status] return ( <div className={`${config.bg} ${config.text} p-2 rounded border`} title={config.label} > <span>{config.icon}</span> <span className="ml-1">{time}</span> </div> ) }
External references
- wcag:2.2 · 1.4.1 — Use of Color — slot states must not rely on color alone
- wcag:2.2 · 1.4.3 — Contrast (Minimum) — state indicators must meet contrast threshold
Taxons
History
- 2026-04-18·v1.0.0·Initial import from booking-calendar-availability·automated