Frontend interview: How would you progressively enhance CSS Masonry?
Prompt and context
The product wants a masonry image wall: cards have different heights, desktop uses multiple columns, mobile uses one, and content must work for keyboard and screen-reader users. CSS Grid Level 3 is defining masonry layout, but syntax and browser support require verification. Compare native CSS, columns, and script approaches and design a fallback and test plan.
What the interviewer is testing
- Separating visual packing from DOM, reading, and focus order.
- Using feature detection and a support matrix instead of browser-name assumptions.
- Handling image dimensions, CLS, responsive column counts, virtualization, and reflow cost.
- Including no-script, keyboard, screen-reader, zoom, and print acceptance.
Questions to clarify first
- Must cards follow publish time and priority, or may visual column order change?
- Is masonry decorative, or must users read and operate content in order?
- What browsers, no-script, and SEO constraints apply?
- Is the list large enough to require virtualization, pagination, or lazy loading?
- Are drag-and-drop, insertion, dynamic heights, and print required?
A 30-second answer framework
I would keep semantic DOM order fixed and use CSS only for visual enhancement. Use @supports and a real browser matrix to select masonry where supported; otherwise fall back to normal Grid or columns and accept different whitespace. Reserve image dimensions to reduce layout shift, and never make JavaScript the accessibility path. Test keyboard, screen readers, zoom, print, disabled scripts, and long lists for consistent content order.
Step-by-step deep answer
Step 1: Define the masonry trade-off
Masonry packs items along one axis into grid tracks while tightening the other axis. It reduces empty space and increases visual density; it does not automatically solve reading order, focus movement, or predictable pagination. Turn those product constraints into acceptance criteria before choosing an implementation.
Step 2: Preserve semantics and focus order
Render real links, buttons, and headings in business order. Do not move nodes with script or repair visual order with positive tabindex. If visual columns differ from DOM order, accept that explicitly or use normal Grid to preserve row-by-row reading.
Step 3: Feature-detect and fall back
Put experimental syntax behind @supports and test it in the target matrix. Use the specification's supported masonry syntax when available; otherwise fall back to display: grid, fixed tracks, or columns. Keep the same DOM, content, and interaction in the fallback; only whitespace and packing density should differ.
Step 4: Handle images and dynamic heights
Emit image dimensions or aspect-ratio from the server and combine them with lazy loading and appropriate object-fit to reduce CLS. Image loads, font swaps, and content updates trigger reflow, so paginate or virtualize long lists. Throttle script measurement and avoid interleaving layout reads and writes that force synchronous layout.
Step 5: Evaluate performance and maintenance
Measure first paint, scroll frame rate, layout time, memory, and long-list insertion separately for CSS, columns, and scripts. Document the smallest visual difference and avoid duplicating rules for every breakpoint. Enable scripts only when drag-and-drop, complex cross-column animation, or an explicit legacy requirement truly needs them.
Step 6: Cover accessibility and nonvisual use
Tab through every item and verify that focus does not jump. Use a screen reader to confirm headings, links, and alternative text follow DOM order. Test 200% zoom, high contrast, reduced motion, print, and disabled scripts. Masonry must not be the only way to obtain content.
Step 7: Release and monitor
Record the specification version, browser matrix, and fallback policy. Monitor CLS, LCP, script errors, overflow, and card-interaction completion by browser. When syntax support changes, replay screenshots and accessibility cases in an experiment before expanding rollout.
High-quality sample answer
I would preserve semantic DOM order and treat masonry as visual enhancement. Use @supports and real browser tests for the specification syntax; fall back to normal Grid or columns with the same content and interactions. Reserve image dimensions or aspect-ratio to reduce CLS, and use pagination or virtualization instead of frequent synchronous measurements for long lists. Keyboard, screen-reader, zoom, print, and no-script tests must retrieve content in DOM order. Monitor CLS, layout errors, and interaction completion by browser, and gate specification updates on replay and accessibility results.
Common mistakes
- Optimizing visual density while ignoring DOM, focus, and screen-reader order.
- Assuming every modern browser supports the same masonry syntax without
@supportsand testing. - Moving nodes with script or positive
tabindexto repair order. - Omitting image dimensions and creating CLS and reflow after load.
- Adding an always-on script for compatibility without measuring layout and memory cost.
Follow-up questions and responses
Follow-up 1: Why not use columns?
Columns can look like masonry, but content is split by column and reading or focus order may differ from business order. If order matters, prefer normal Grid or accept whitespace.
Follow-up 2: How do you detect masonry support?
Use @supports and automated tests in the target browser matrix, recording the exact properties and values that pass. Do not infer support from a User-Agent or “modern browser” label.
Follow-up 3: What about dynamically inserting cards?
Keep insertion order, reserve image dimensions, batch updates, and avoid per-item forced layout. Large lists need pagination, virtualization, and restorable focus.
Follow-up 4: When is a script fallback justified?
When drag-and-drop, complex cross-column animation, or an explicit legacy requirement cannot be met by CSS. The script remains an enhancement and semantic content must survive its failure.
Follow-up 5: How do you prove visual layout did not break accessibility?
Operate every item by keyboard and screen reader in DOM order, then test 200% zoom, reduced motion, print, and disabled scripts. Make the results a release gate.