Layout patterns
Most JS layout reads could be CSS. Plus the four states every component ships without.
What flex and grid solve
- Equal-width columns:
grid-template-columns: repeat(3, 1fr); - Aspect-ratio cards:
aspect-ratio: 4 / 3; - Responsive wrap:
flex-wrap: wrap;withmin-content. - Sidebar + main:
grid-template-columns: 240px 1fr; - Centred elements:
place-items: center; - Sticky elements:
position: sticky; top: 0; - Equal heights:
display: gridwithalign-items: stretch.
When JavaScript is appropriate
- Anchor positioning for tooltips and popovers.
- Virtualised lists past 50 items.
- Drag-and-drop hit-testing.
Batch reads and writes. Layout reads (getBoundingClientRect) followed by writes followed by reads cause layout thrashing.
overscroll-behavior in modals
Use 100dvh instead of 100vh on mobile. 100vh includes browser chrome and overflows.
Empty, loading, and error states
Empty
- First-time empty: Explain the surface. Provide a CTA.
- Filter empty: Show what matched nothing. Offer "Clear filters."
Loading
Show nothing for 150-300ms. Once a spinner appears, keep it 300ms minimum to avoid flicker. Prefer skeletons for lists.
Error
State what failed, what to do, and offer an action. Preserve typed values.
Long content safety
min-width: 0 matters because flex/grid children default to min-width: auto and refuse to shrink below content width.
Common mistakes
- JS-measured equal heights. Use
display: grid. resizeevent listeners for layout. Use CSS media queries.- No
overscroll-behavioron modals. Page scrolls behind. - No empty state. User sees blank whitespace.
- Spinner for a 50ms request. Flicker. Use a show-delay.
100vhon mobile. Content hides behind the toolbar. Use100dvh.