A product team adds a rating widget to a review card. The designer spec says “5 stars; click to rate; show the filled stars”. The junior engineer ships in fifteen lines — a row of five star glyphs, each with an onClick that sets a value state. The reviewer clicks through all five stars on a Chrome/macOS mouse session, watches them fill, nods. Merge. Ship it Friday.
Monday morning, three support tickets: a mobile user who “meant to tap 4 stars but got 5 and can't undo”; a keyboard user who “can't reach the stars at all — Tab skips the whole row”; a screen-reader user whose NVDA session reads “clickable, clickable, clickable” with nothing meaningful announced. Three users, one row of stars, same fifteen lines.
That row above is the fifteen-line version. Click a star — the value commits instantly. There is no hover feedback, no way to see “would rate 3 of 5” BEFORE your click fires. Try it: hover over 4 stars, decide to commit to 3 instead — but you can't cancel once your finger moves. Every click is a commit. That is the misclick wall.
// The rating every junior ships first.// Clicks work. Nothing else does.function BrokenRating({ value, onChange }) { return ( <div className="flex gap-1"> {[1, 2, 3, 4, 5].map((n) => ( <div key={n} onClick={() => onChange(n)} className={n <= value ? 'star filled' : 'star empty'} > ★ </div> ))} </div> );}// What breaks:// 1. No hover preview — user cannot see "would rate 3/5" before click.// Misclicks ship the wrong value. No undo.// 2. <div> not focusable — keyboard Tab skips the entire row.// 3. No role — NVDA reads "clickable, clickable" with no semantic// anchor. The value 3 is not announced at all.// 4. No half-star support — a review worth 3.5 rounds to 3 or 4,// collapsing aggregate precision.// 5. No readOnly mode — display-only ratings stay clickable; an// accidental re-rate fires from a stray tap.Three probes on the naive row. A mouse user aiming at star 4 tapped star 5 by accident. A keyboard user pressed Tab. A screen-reader user focused the row. Which probes surface a real failure in the fifteen-line version?
Three users run three probes against the naive <div onClick> star row. Which ones break?