Masonry Layout

Pinterest-style variable-height grid using CSS columns, CSS grid subgrid, and a lightweight Alpine.js JS fallback.

Approach Comparison

Three viable techniques in this stack — each with a different trade-off profile.

Technique DOM order JS needed Image support Best for
CSS columns Top → bottom per column None After load (no reflow) Text-heavy cards, blogs
CSS grid dense Row-first None Fixed-height rows only Known-height thumbnails
JS span masonry Source order preserved Alpine (tiny) Yes — recalc on load Image galleries, Pinterest-style

1. CSS Columns Masonry

Uses the CSS columns property — the simplest approach, zero JS. Items flow top-to-bottom within each column. Add break-inside: avoid on each card to prevent mid-card column breaks. Caveat: reading order follows columns not rows (A→B→C down col 1, then D→E→F down col 2).

2 columns

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

3 columns — standard gallery density

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

Accessibility

Dark Mode

Iconography

Spacing Scale

Components

Tokens

4 columns — dense thumbnail grid

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

Accessibility

Dark Mode

Iconography

Spacing Scale

Components

Tokens

2. CSS Columns — Responsive Auto Width

columns: 16rem tells the browser "fit as many 16 rem columns as possible" — no breakpoints needed. Resize the window to watch it reflow from 1 → 2 → 3+ columns.

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

Accessibility

Dark Mode

Iconography

Spacing Scale

Components

Tokens

Equivalent CSS: columns: 16rem; column-gap: 0.75rem;

3. JS Masonry — Alpine + CSS Grid Row Span

Preserves source order (left→right across columns) and works with images. The grid uses 1 rem auto-rows; Alpine measures each card's natural height and sets grid-row-end: span N so it occupies the correct number of row tracks. Re-runs on window resize.

Columns

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

Accessibility

Dark Mode

Iconography

Spacing Scale

Components

Tokens

Source order is left→right, top→bottom — screen-reader and keyboard order matches visual order.

4. Gap Control

Gap between cards affects visual density and the feel of the layout. Small gaps suit editorial/news grids; larger gaps suit portfolio or lifestyle content.

Tight — 4px (news feed, dense catalogue)

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

Accessibility

Dark Mode

Iconography

Standard — 12px (blog, portfolio)

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

Accessibility

Dark Mode

Iconography

Airy — 24px (lifestyle, photography)

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

Accessibility

Dark Mode

Iconography

5. Content Density

Masonry shines when card heights vary significantly. These scenarios test the layout under realistic conditions.

Minimal — near-uniform height (masonry adds little value here)

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

Accessibility

Dark Mode

Iconography

High variance — 1 to 12 lines per card (masonry at its best)

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

Accessibility

Dark Mode

Iconography

Spacing Scale

Components

Tokens

6. Interactive Playground

Adjust technique, column count, and gap. The CSS snippet updates live.

Branding

Typography

UI Patterns

Motion

Grid Systems

Color Theory

Accessibility

Dark Mode

Iconography

Generated CSS

            

7. Edge Cases

Scenarios that expose weaknesses — know them before choosing a technique.

Empty — zero items, container must not collapse

No items to display

Single item — CSS columns: occupies only column 1

Only Card

With JS masonry + auto-fit minmax, a single item would expand to fill the full row instead.

Long unbreakable word — must not overflow card

Overflow Card

Supercalifragilisticexpialidocious-masonry-overflow-test-word

Fix: add overflow-wrap: break-word to cards. Without it the card widens and distorts the column.

Infinite scroll / dynamic insertion

CSS columns — new items append naturally; no action needed.
JS masonry — call layout() after each batch is inserted into the DOM (use a MutationObserver or trigger from the data-fetch callback). Wrap in requestAnimationFrame to ensure heights are settled before measuring.