- React
- Live Machine Coding Challenges and Custom Hooks
- Machine Coding: Accessible Star Rating Widget
Machine Coding: Accessible Star Rating Widget
An accessible Star Rating widget requires managing hover and selected states, full keyboard navigation with Arrow keys and Home/End, ARIA radiogroup semantics with role="radiogroup" and aria-checked, and dynamic visual state updates without DOM manipulation.
Key Learning Objectives
Build an interactive Star Rating widget supporting 1 to N stars.
Implement hover preview state that reverts to selected state on mouse leave.
Ensure complete keyboard accessibility: ArrowLeft, ArrowRight, Home, End, and Space/Enter.
Apply WAI-ARIA Radio Group pattern for screen reader accessibility.
The Interview Problem & Context
Implement an accessible, interactive `<StarRating max={5} value={3} onChange={(rating) => ...} />` component with hover feedback and full keyboard navigation.
1import React, { useState, KeyboardEvent } from 'react';23interface StarRatingProps {4 max?: number;5 value: number;6 onChange: (value: number) => void;7 disabled?: boolean;8}910export function StarRating({11 max = 5,12 value,13 onChange,14 disabled = false,15}: StarRatingProps) {16 const [hoverValue, setHoverValue] = useState<number | null>(null);1718 // Active display rating is either current hover preview or committed value19 const displayValue = hoverValue ?? value;2021 const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {22 if (disabled) return;2324 switch (e.key) {25 case 'ArrowRight':26 case 'ArrowUp':27 e.preventDefault();28 onChange(Math.min(max, value + 1));29 break;30 case 'ArrowLeft':31 case 'ArrowDown':32 e.preventDefault();33 onChange(Math.max(1, value - 1));34 break;35 case 'Home':36 e.preventDefault();37 onChange(1);38 break;39 case 'End':40 e.preventDefault();41 onChange(max);42 break;43 }44 };4546 return (47 <div48 role="radiogroup"49 aria-label="Rating"50 tabIndex={disabled ? -1 : 0}51 onKeyDown={handleKeyDown}52 onMouseLeave={() => setHoverValue(null)}53 className="star-rating-container"54 style={{ display: 'inline-flex', gap: '4px', cursor: disabled ? 'not-allowed' : 'pointer' }}55 >56 {Array.from({ length: max }, (_, index) => {57 const starNumber = index + 1;58 const isFilled = starNumber <= displayValue;5960 return (61 <span62 key={starNumber}63 role="radio"64 aria-checked={starNumber === value}65 aria-label={`${starNumber} of ${max} stars`}66 onClick={() => !disabled && onChange(starNumber)}67 onMouseEnter={() => !disabled && setHoverValue(starNumber)}68 style={{69 fontSize: '28px',70 color: isFilled ? '#f59e0b' : '#d1d5db',71 userSelect: 'none',72 transition: 'color 150ms ease-in-out',73 }}74 >75 ★76 </span>77 );78 })}79 </div>80 );81}
Deep Architectural Explanation
Key Architecture for the Star Rating Widget
- State Partitioning:
value(committed state): passed as prop or controlled state.hoverValue(preview state): localuseState<number | null>(null).displayValue = hoverValue ?? value: clean declarative derivation. On mouse leave,hoverValueresets tonull, instantly snapping back tovalue.
- WAI-ARIA Accessibility:
- Container has
role="radiogroup"andtabIndex={0}to participate in keyboard navigation. - Each star has
role="radio"witharia-checked={starNumber === value}. - Screen readers announce: "Rating, 3 of 5 stars, radio button checked."
- Container has
- Keyboard Controls:
ArrowRight/ArrowUp: increment by 1 (clamped to max).ArrowLeft/ArrowDown: decrement by 1 (clamped to 1).Home/End: jump to minimum (1) or maximum (max).
Common Interview Traps & Pitfalls
Mutating state on hover instead of using temporary hover state, causing accidental updates on mouse movement.
Failing to handle keyboard arrow keys, leaving the widget inaccessible to keyboard-only users.
Using `index` for rating calculation (0-indexed) without converting to 1-indexed stars.
Senior Interviewer Follow-Up Probes
How would you implement half-star rating rendering using SVG linearGradient or CSS clip-path?
How do you handle RTL (Right-to-Left) languages where ArrowLeft should increment rather than decrement?
How do you integrate this component into a React Hook Form or Formik form schema?
Frequently Asked Interview Questions
What WAI-ARIA role should a star rating component have?
role="radiogroup" on the container and role="radio" with aria-checked on each star element, or role="slider" with aria-valuemin, aria-valuemax, and aria-valuenow.
How do you support keyboard navigation in a star rating widget?
Add onKeyDown to the focusable container to handle ArrowUp/ArrowRight (increment), ArrowDown/ArrowLeft (decrement), Home (minimum), and End (maximum).
How do you prevent ratings from going below 1 or above max?
Use Math.max(1, value - 1) and Math.min(max, value + 1) during keyboard navigation updates.
Can you build a star rating widget using native HTML radio buttons?
Yes. Using hidden <input type="radio"> elements styled with CSS pseudo-elements is a classic semantic HTML approach that provides built-in keyboard navigation.
How do you handle disabled or read-only mode?
Add a disabled prop that sets pointer-events: none or disables hover/click handlers and sets tabIndex={-1} so keyboard focus skips the element.
