The UI coding round rewards the parts that don't show in a screenshot
In a UI coding round you are asked to build a component — a dropdown, a tab set, an autocomplete — under a clock. Almost everyone gets it to look right. What separates a pass from a near-miss is the invisible half: does it work from the keyboard, does focus go where it should, do the roles announce correctly, is the state coherent when the user does something out of order. Those are the things that do not show in a screenshot, and they are exactly what a good interviewer is watching for — because they are what separates someone who renders UI from someone who builds it. Doing the invisible half while the clock runs, not bolting it on at the end, is the skill the round measures.
Build the keyboard behaviour as you go
The single strongest move is to wire the keyboard while building, not after. A dropdown that opens on click but not on Enter, or a list you cannot arrow through, reads as unfinished to an interviewer even if it looks perfect. Handle the keys as part of the component’s core, out loud:
// keyboard handled as first-class, not an afterthought — narrate this as you write it
function onKeyDown(e) {
if (e.key === "ArrowDown") { e.preventDefault(); setActive((i) => Math.min(i + 1, last)); }
if (e.key === "ArrowUp") { e.preventDefault(); setActive((i) => Math.max(i - 1, 0)); }
if (e.key === "Enter") select(active);
if (e.key === "Escape") close();
}
Saying “let me wire arrow keys and Escape now” signals to the interviewer that you know a widget is not done when it renders.
Manage focus and roles deliberately
The second submerged skill is focus and semantics. When a listbox opens, focus (or
aria-activedescendant) should move into it; roles should describe the widget so a
screen reader can announce it. You do not need every ARIA attribute — you need the
few the widget’s pattern requires, applied correctly:
<input role="combobox" aria-expanded={open} aria-controls="lb" aria-activedescendant={activeId} />
<ul role="listbox" id="lb">
{items.map((it, i) => (
<li role="option" id={`opt-${i}`} aria-selected={i === active}>{it.label}</li>
))}
</ul>
An interviewer who sees role="combobox" and a moving aria-activedescendant knows
you have built this pattern before.
Manage the clock, and say what you’re deferring
The round is time-boxed, so part of the skill is sequencing: get a working, accessible core first, then layer polish, and say what you are deferring (“I’ll get selection and keyboard working, then add the async loading state if time allows”). That verbalised triage is itself signal — it shows you know what matters most and that accessibility is in the “must” bucket, not the “if time” bucket. Interviewers forgive an unfinished feature far more readily than a component that ignores the keyboard, because the former is a time constraint and the latter is a values one. Build the invisible half first and narrate the trade-offs, and you turn a “looks right” submission into a clear pass. The accessible-combobox exercise is this exact round in practice — the widget whose whole difficulty is the submerged half — and tabs-molecule and data-table-sort drill the same keyboard-and-state discipline.