Skip to lesson
Exit
Capstone: Mock Loop & Self-Exam1 / 2

1 min lesson

The problem bank

Work through the cases in "The problem bank", pairing each signal with the move that fits.

Step 1 of 2

The problem bankPick one per session

Each of these is a real interaction a Design Engineer ships and each hides a specific trap that separates a working answer from a polished one.

Accessible combobox

Input that filters a list, opens on type, commits on Enter.

Trap: keyboard. Arrow keys move a visual active option without moving DOM focus; aria-activedescendant ties them together.

Tests whether you know ARIA from memory, not from a docs tab.

Virtualized list

Render 50k rows with only the visible window in the DOM.

Trap: the math. A spacer div holds total height; you compute the start index from scrollTop and over-render a few rows so fast scroll doesn't flash blank.

Tests performance instinct under a real DOM-cost constraint.

Toast queue

Stack of dismissable notifications with enter/exit motion and auto-dismiss.

Trap: timers. Pausing on hover, resuming on leave and cleaning up on unmount so a dismissed toast's timer never fires.

Tests state machine clarity and effect cleanup.

Segmented control

Tab-style selector with a sliding indicator that animates between options.

Trap: the indicator. You measure the active segment and translate the pill with a transform, not by animating width/left.

Tests motion that stays on the compositor at 60fps.

Learn more

Full explanation

Lead with the contract before you type the component

Lead with the contract before you type the component. State the API out loud first.ts
// Combobox: filter a list, keyboard-navigable, commits a value.
// Props
type ComboboxProps = {
  options: { id: string; label: string }[];
  value: string | null;
  onChange: (id: string) => void;
};
// Invariants to say aloud:
//  - activeIndex is the visually-highlighted row, NOT DOM focus
//  - input keeps focus the whole time; aria-activedescendant points at the row id
//  - Escape closes and restores the committed value; Enter commits activeIndex
//  - empty filtered list still renders a 'No results' row, not a void
Learn more

Advanced table

Score yourself like the interviewer will

Score yourself like the interviewer willRubric

Dimension
Correctness
Pass bar
It works for the happy path and the two cases you named up front
What sinks it
Demos once, then breaks on the second interaction
Dimension
Edge cases
Pass bar
Empty list, single item, rapid input, last-item arrow wrap
What sinks it
Crashes or no-ops on an empty filter; arrow falls off the end
Dimension
Code clarity
Pass bar
Named handlers, no nested ternary soup, a reader follows it cold
What sinks it
One 80-line component nobody could review in five minutes
Dimension
Polish
Pass bar
Focus ring, hover/active states, a transition that isn't janky
What sinks it
Functionally right, visually a wireframe

On the real screen, polish is graded even when nobody asked for it - that's the Design Engineer differentiator.

Interview move

When you hit the keyboard-handling part of the combobox, say the rule before you code it: “Focus stays on the input; I move a visual active index and expose it with aria-activedescendant.” Stating the accessibility model from memory, unprompted, is exactly the craft-at-the-seam signal the unassisted screen is built to surface.

Watch out

The failure mode under the clock isn't running out of time - it's spending 40 minutes on a perfect filter algorithm and shipping zero states and zero motion. Get it working end to end first, ugly, then spend the back half on edge cases and polish. A bare-but-complete component reads far better than a half-built beautiful one.

QIn an accessible combobox, the user presses the down arrow to move through options. What should happen to DOM focus?