A junior ships a 20-line gallery: grid grid-cols-4 gap-2, thumbnails in a flat grid, onClick on each <div> to open a viewer. Passes Figma. Works in the reviewer's mouse. Ships. Then the bug queue lights up from four directions at once.
A code reviewer with a mouse and Chrome clicks thumb #1. Viewer opens. Clicks X. Closes. Approves the PR. Ships on Friday. Four different probes — keyboard, screen reader, layout paint, bandwidth — are each about to expose a separate wall the 20-line grid has no defenses against.
// Naive gallery — the 20-line version juniors ship.// Works with a mouse. Fails for keyboard, AT, bandwidth, and LCP.function BrokenGallery({ images }) { const [openIdx, setOpenIdx] = useState(null); return ( <> <div className="grid grid-cols-4 gap-2"> {images.map((img, i) => ( <div key={img.id} onClick={() => setOpenIdx(i)}> <img src={img.src} alt="" /> </div> ))} </div> {openIdx !== null && ( <div className="fixed inset-0 bg-black/90 flex items-center justify-center" onClick={() => setOpenIdx(null)}> <img src={images[openIdx].src} alt="" /> </div> )} </> );}// Silent failures:// 1. <div onClick> — not keyboard focusable, not a button, AT misses it// 2. No Arrow navigation — open #1, want #2? Close, then click #2// 3. All <img> paint at mount — 200-image gallery eats data budget// 4. No width/height — browser paints each image as 0x0 until loaded,// then reflows the grid on each decoder callback. CLS disaster.// 5. No focus trap in the viewer — Tab falls through to the page// 6. alt="" — all images invisible to screen readers. alt is REQUIRED.// 7. No aria-modal / role="dialog" on the viewer — AT walks past itThe simulator runs the naive version. Four probe buttons on the right correspond to the four walls. Tap one; the grid below reacts. The naive <div onClick> thumbs look fine to your mouse — tap one and the viewer opens. But each probe pulls a different thread, and the whole construction unravels from a different direction every time.
Probe 1 — keyboard. A user presses Tab. Focus skips every thumbnail; the <div onClick> is not focusable. Press Tab again: focus lands on the footer link. The gallery is invisible to the keyboard.
In the simulator above, the “Tab probe” button simulates pressing Tab from the page's navigation bar down through the gallery. Try it: click the TAB button on the right. A glowing focus ring will try to land on the first thumbnail. Then click it a second time. What happens across all twelve thumbs?
The naive thumb is <div onClick={open}><img alt="" /></div>. With no tabIndex, no role, no <button> — what does Tab do when the keyboard focus reaches the gallery row?