Star rating input is accessible and keyboard-navigable
Why it matters
WCAG 2.2 SC 4.1.2 (Name, Role, Value) and SC 2.1.1 (Keyboard) require that all interactive controls be operable without a mouse and expose their purpose to assistive technology. A star rating built from styled <div> elements with only onClick handlers is invisible to screen readers and completely unreachable by keyboard — failing Section 508 2018 Refresh 502.3.1 as well. Users who navigate by keyboard or rely on VoiceOver/NVDA cannot leave a rating at all, which is a measurable drop in review volume and a legal exposure under ADA and EAA for US and EU stores.
Severity rationale
Medium because the inaccessible input excludes keyboard and screen-reader users from submitting reviews without enabling a security or data-integrity attack.
Remediation
Replace click-only star elements with native radio inputs inside a role="radiogroup" container in components/AccessibleStarInput.tsx. Native radios give keyboard navigation (arrow keys), visible focus, and screen-reader announcements for free.
// components/AccessibleStarInput.tsx
export function StarInput({ value, onChange }: { value: number; onChange: (v: number) => void }) {
return (
<div role="radiogroup" aria-label="Star rating">
{[1, 2, 3, 4, 5].map(star => (
<label key={star} className="star-label">
<input
type="radio"
name="rating"
value={star}
checked={value === star}
onChange={() => onChange(star)}
aria-label={`${star} star${star > 1 ? 's' : ''}`}
className="sr-only"
/>
<span aria-hidden="true">★</span>
</label>
))}
</div>
)
}
Add CSS focus-visible styles on .star-label:has(input:focus-visible) so the focus ring is visible without cluttering mouse interactions.
Detection
-
ID:
star-input-accessible -
Severity:
medium -
What to look for: Before evaluating, quote the star rating component's JSX or HTML markup. Count the number of accessibility features present: (1) aria-label or aria-labelledby, (2) role="radiogroup" or radio inputs, (3) keyboard navigation (onKeyDown handler or native radio behavior), (4) visible focus indicator (outline, ring, or border style on focus).
-
Pass criteria: The star rating input has at least 3 of 4 accessibility features: ARIA labels (
aria-label,role="radio"orrole="radiogroup"), keyboard navigation via arrow keys or Tab+Enter, and a visible focus indicator with CSS focus styles. Report even on pass: state which 3-4 of 4 features are present. -
Fail criteria: The star rating input has fewer than 3 of 4 accessibility features, is click-only with no keyboard navigation, or lacks ARIA labels/roles making it invisible to screen readers.
-
Skip (N/A) when: No review submission form exists (submission-form-exists already failed or skipped).
-
Detail on fail:
"Star rating has 1 of 4 accessibility features (aria-label only). Missing: role=radiogroup, keyboard handler, focus indicator."or"Stars are styled divs with onClick only — 0 of 4 accessibility features." -
Remediation: Build an accessible star input in
components/AccessibleStarInput.tsxusing radio buttons or ARIA roles:// components/AccessibleStarInput.tsx export function StarInput({ value, onChange }) { return ( <div role="radiogroup" aria-label="Rating"> {[1, 2, 3, 4, 5].map(star => ( <label key={star}> <input type="radio" name="rating" value={star} checked={value === star} onChange={(e) => onChange(parseInt(e.target.value))} aria-label={`${star} stars`} /> <span>★</span> </label> ))} </div> ) }
External references
- wcag:2.2 · 4.1.2 — Name, Role, Value
- wcag:2.2 · 2.1.1 — Keyboard
- section-508:2018-refresh · 502.3.1 — Object Information
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ecommerce-reviews·automated