Question presented to candidate: "Build the notification bell for our app header. It shows a red badge with the number of unread notifications, shows 99+ when there are more than 99, and disappears when everything is read. Clicking the bell opens a panel listing recent notifications; clicking one marks it read, and there is a Mark all as read button. Add buttons to simulate new notifications arriving."
Requirements:
- A bell button with a badge: the unread count, capped at
99+, hidden at zero. - A panel (toggled by the bell) listing the newest 8 notifications, unread ones highlighted.
- Clicking a notification marks it read; Mark all as read clears the badge.
- Escape and clicking outside close the panel; Escape returns focus to the bell.
- Screen reader users hear the real count, and are told when new notifications arrive.
What a strong answer should cover:
- The notification list is the state; the unread count and the badge label are derived from it, never stored separately.
- Capping is a display concern: the label says
99+while the accessible name keeps the exact number. - Conditional rendering of the badge at zero, and
aria-hiddenon the visual badge so it is not read twice. - Disclosure semantics on the bell (
aria-expanded,aria-controls), Escape and outside-click handling with listener cleanup. - Immutable updates when marking items read, and where the list lives (the parent, standing in for a server push).
Clarifying questions expected:
- "Where do notifications come from: polling, WebSocket, or server-sent events?"
- "Is the cap always 99, and should a zero state show a dot or nothing?"
- "Does opening the panel mark everything as seen, or only clicking an item?"
Code / implementation expected: Yes. A bell component with a derived badge and a panel, plus a parent that simulates incoming notifications.