---
alwaysApply: false
globs: ["**/*"]
description: "Contrast ratios, target sizes and motion limits with WCAG numbers. Use when checking contrast, hit-target size or animation on a UI surface."
---

# UI Accessibility Thresholds

**Scope.** Measurable contrast, hit-target size and motion limits. Focus
order, keyboard paths, roles and labels belong to `ui-controls`. Every
number is WCAG 2.2 level AA unless the line says otherwise.

**Text contrast.** Produce 4.5:1 minimum for text below 24px regular or
below 18.66px bold, and 3:1 at or above those sizes (SC 1.4.3). A
reviewer reads a measured ratio beside every foreground and background
token pair, and rejects any pair under its threshold.

**Non-text contrast.** Produce 3:1 against adjacent colours for control
boundaries, input borders, meaning-carrying icons and chart series (SC
1.4.11). A reviewer checks that a hairline border at 1.2:1 fails.

**Placeholder and blocked text.** Produce 4.5:1 for placeholder text: it
is content, not decoration. Disabled controls are exempt from SC 1.4.3,
so produce a non-colour cue for them as well. A reviewer finds that cue.

**Colour is never the only channel.** Produce a second channel — text,
icon, shape or position — wherever colour carries meaning (SC 1.4.1). A
reviewer renders the surface in greyscale and still reads every state.

**Focus indicator.** Produce an indicator at least 2 CSS px thick around
the whole control, at 3:1 against both the control and the surface behind
it. Never leave `outline: none` without a replacement. A reviewer tabs the
surface at 200% zoom and sees the indicator unclipped and not covered by
a sticky bar (SC 2.4.11).

**Target size.** Produce 44x44 CSS px for every pointer target
`[ASSUMPTION]`. The AA floor is 24x24 CSS px (SC 2.5.8); 44 is the
platform-guidance figure and the default here. Never go below 24x24.
Where a target is under 24x24, produce enough clear space that 24px
circles centred on adjacent targets do not intersect. A reviewer measures
the smallest target on the surface.

**Spacing between targets.** Produce at least 8px between adjacent
independent targets `[ASSUMPTION]`. A reviewer checks that no destructive
action sits next to a non-destructive one without that gap.

**Motion limits.** Produce no content that flashes more than three times
per second (SC 2.3.1), and a pause control for any motion running longer
than 5s (SC 2.2.2). A reviewer counts flashes in one second of the fastest
animation, and times every loop and carousel.

**Reduced motion.** Produce a `prefers-reduced-motion: reduce` branch
that replaces translation, scale and parallax with an opacity change of
150ms or less `[ASSUMPTION]`, and that keeps the state change itself. A
reviewer sets the preference and confirms every transition still shows
its result.

**Transition duration.** Produce 150ms to 300ms for state transitions
`[ASSUMPTION]`. Nothing blocks input while a transition runs. A reviewer
clicks during a transition and the input registers.

**Text resize, reflow and spacing.** Produce a layout readable at 200%
text size (SC 1.4.4), usable at 320 CSS px width without two-axis
scrolling (SC 1.4.10), and unbroken by line height 1.5x, paragraph spacing
2x, letter spacing 0.12em and word spacing 0.16em applied together (SC
1.4.12). A reviewer zooms to 400% in a 1280px window, finds no horizontal
scrollbar, then applies all four spacing values at once and finds no
clipped content.
