Question presented to candidate: "Build a ProgressBar component. It takes a value from 0 to 100 and shows a bar that fills smoothly as the value changes. Show several bars stacked on one page: one driven by a simulated upload, one you can nudge with buttons, one given an out-of-range value, and one for a task whose progress is not known yet."
Requirements:
valueis clamped to 0..100 before use;NaNbecomes 0.null(or missing) means unknown, which renders an indeterminate bar.- The fill animates with a CSS transition when the value changes (using
transform, notwidth), and the animation respectsprefers-reduced-motion. - Accessibility:
role="progressbar"with an accessible name,aria-valuemin="0",aria-valuemax="100"andaria-valuenowset to the rounded clamped value;aria-valuenowis omitted while indeterminate. The percentage is also shown as text. - The demo's simulated upload adds 20 every 100 ms until it reaches 100, then stops its timer; the start button is disabled while it runs. Timers are cleaned up on unmount.
- Several bars on one page are independent.
What a strong answer should cover:
- Clamping as a pure function, and keeping the raw value separate from the displayed value.
- The ARIA progressbar attributes, including the indeterminate case, and when the native
<progress>element is a good alternative. - Why
transform: scaleX()animates more cheaply thanwidth, and reduced-motion support. - Timer lifecycle: deriving "running" from state, and cleaning up the interval on completion and on unmount (React StrictMode in particular).
- Avoiding announcement spam for screen-reader users on fast updates.
Clarifying questions expected:
- "Is the value always a percentage, or do we need a custom
maxsuch as bytes?" - "Should screen readers hear every update, or only milestones?"
- "Do we need an indeterminate state?"
Code / implementation expected: Yes. A ProgressBar component and a demo page with the four bars described above.