Frontend LLD on a Whiteboard
Box the UI, name the state, walk the data flow, leave CSS last. A 45-minute low-level design method for frontend interviews.
- interview
- lld
- system-design
- frontend
Frontend LLD is the round with a whiteboard (or a doc) and one feature, not a cluster diagram and not a repo. You lose when you spend twenty minutes on flex and radius while nobody can point at a box and say what state lives in it.
This is a 45-minute method: box the UI, name the state, walk the data flow, leave CSS last. Accessibility is the last five minutes. It is not frontend system design (cache, SSR, scale). It is not machine coding (a working slice in an editor). It is not whiteboarding one component API. LLD is the feature as boxes, ownership, events, and one network contract.
Key takeaways
- Draw regions and names before fields, files, or CSS.
- State is ownership: source of truth, derived, who writes.
- Walk one user action from event to render, then one failure.
- CSS last. Keyboard, focus, and names get the last five minutes.
- Stretch goes on a parking list. Do not decorate an unnamed model.
Box the UI, name the state, walk the data flow, leave CSS last
Run this sequence even if you have built the feature before. The board is the artifact they grade.
45-minute procedure
- Restate and cut (0-5). Users, happy path, non-goals. Write an MVP checklist the interviewer can nod at.
- Box the UI (5-12). Rectangles for regions. Label each with a component name. Mark overlay vs page. No pixels.
- Name the state (12-20). Entities, IDs, UI flags, derived values. One source of truth per fact.
- Walk data flow (20-32). One event: what writes, what is derived, what hits the network, what the next paint shows. Then one failure (empty, 500, abort, pause).
- Events and API (32-40). Public props and callbacks, key map for the primary widget, request shape. Still not CSS.
- Accessibility (40-45). Tab, arrows, Escape, focus move, accessible names. Native elements before roles.
If you are picking colors at minute 25, say so and go back to state.
Box the UI
Boxes answer “what is on screen,” not “how it is laid out.” A command palette is not a pretty modal. It is:
[App shell]
[Trigger]
[Palette] (only when open)
[Search input]
[Result list]
[Row] (icon, title, shortcut)
[Footer] (Enter / Esc hints)
Mark the scroll region. Mark overlay (palette, modal) versus inline (nested checkboxes, stopwatch). If two regions share data, draw an arrow after they have names.
Skip column counts, font sizes, and whether the overlay is position: fixed. Portal is one spoken sentence (“render into document.body so stacking is honest”), then move on.
Name the state
Three buckets. If a field can be computed from another, it is not stored.
| Bucket | Palette | Stopwatch | Nested checkboxes | Modal |
|---|---|---|---|---|
| Truth | open, query, selectedIndex, commands |
status, accumulatedMs, startedAt |
Set of leaf ids |
open, opener |
| Derived | filtered, highlight |
elapsedMs at now |
parent checked / mixed | rest of page inert |
| Do not store | isEmpty boolean |
ticks++ |
indeterminate on every node |
isCentered |
The stopwatch is the clean fake-model example. Do not increment a counter on an interval. Elapsed time is a function of clocks:
function elapsedMs(now, watch) {
if (watch.status !== 'running') return watch.accumulatedMs;
return watch.accumulatedMs + (now - watch.startedAt);
}
// pause: accumulatedMs = elapsedMs(now, watch); status = 'paused'
// start: startedAt = now; status = 'running'
// reset: accumulatedMs = 0; startedAt = null; status = 'idle'
Mixed checkboxes: mixed is derived from which leaves are in the set. Parent click writes leaves. Modal “centered” is CSS. Modal state is open plus where focus returns. Say who owns the write. Do not lift selectedIndex into a global store because you are nervous.
Walk the data flow
For one action (type in the palette, toggle a leaf):
- Name the event (
input, click,ArrowDown,Escape). - Name the state write.
- Name derived that changes.
- Name the network if any, and how a stale response dies (
AbortControlleror a sequence number). - Name the next render.
- Name the failure (no matches, 500, already paused, empty tree).
Talk in arrows:
Cmd-K → open true → focus input
input "nav" → query → filtered rows → selectedIndex = 0
ArrowDown → selectedIndex + 1 (clamp)
Enter → run command → open false → restore focus
The interviewer can attack any arrow. If you cannot walk it, the boxes were decoration.
What to put on the board vs what to skip
| Put on the board | Skip unless they ask |
|---|---|
| Region boxes and component names | Pixel padding, color tokens, fonts |
| Truth vs derived vs “do not store” | Redux vs Zustand vs context |
| One happy-path sequence | Every product edge |
| One failure: empty, error, pause | Retry copy |
| Endpoint, payload, error union | Auth provider internals |
| Key map for the primary widget | Full ARIA spec dump |
| Portal or native dialog as a note | CSS centering puzzles |
| Stretch parking list | File tree |
CSS last means you may write list: overflow auto as a region constraint. You may not open a 12-column grid. If they insist on layout, spend two minutes on stacking (overlay on top, focus inside) and return to flow.
Accessibility in the last five minutes
Checklist, out loud, pointing at the boxes. You are not implementing a focus trap unless they converted the round into machine coding.
- Native first. Search is an
<input>. Controls are<button type="button">. Modal prefers<dialog>andshowModal()over adivplus ten ARIA attributes. - Keyboard. Tab through the boxes you drew. Arrows for list rows and tree nodes. Escape closes overlays. Enter runs the highlighted command or toggles the focused checkbox.
- Focus. Open: move in. Close: restore to the stored opener. Do not leave focus on a node you are about to unmount.
- Names. Icon-only close needs an accessible name.
- What the screen reader already needs. Modal: labelled dialog, content behind inert. Nested checkboxes: mixed is DOM
indeterminate(oraria-checked="mixed"on a custom control), not a second React boolean.
If time dies at minute 44, say the next three keys you would wire. Silence does not score. Broader language: accessibility talking points. Keyboard: MDN keyboard accessible.
Footguns
- Starting with CSS or a library debate.
- Storing derived flags (
isEmpty,indeterminate,elapsedTicks) that fight the next event. - Walking the happy path only.
- Designing auth, SSR, and cache. That is system design. Stay in the feature unless they zoom out.
- Implementing a whole command palette on the board. Pseudocode one handler. Save shipping for machine coding.
Interview out-loud answer
“I would lock MVP, then draw boxes for the regions so every later field has a home. I would list state as source of truth versus derived, and refuse tick counters or mixed flags that can be computed. I would walk one event from the handler to the next render, then one failure. CSS stays last: overflow and overlay, not pixels. In the last five minutes I would name Tab, arrows, Escape, where focus moves, and the accessible name for icon buttons. Stretch stays on a parking list so we can extend if there is time.”
Further reading
- Thinking in React
- Managing state
- HTML spec: the
dialogelement - MDN:
<dialog> - web.dev: Learn Accessibility