javascript-today

CSS Grid Lanes

If you’ve been using CSS Flexbox for years, stepping into the world of CSS Grid can feel like moving from a one-way street to a complex intersection.

While we often think about “columns” and “rows,” CSS Grid actually operates on a coordinate system defined by lines. Understanding how to manipulate these lines is the key to mastering complex layouts.


What are Grid Lines?

Grid lines are the horizontal and vertical dividing lines that create the structure of your grid. They exist on either side of a column or row.

  • Column Lines: Run vertically, defining where a column starts and ends.
  • Row Lines: Run horizontally, defining the boundaries of your rows.

An important rule to remember: Lines are numbered starting at 1. In a 3-column grid, you actually have 4 grid lines.

Positioning Elements by Line Numbers

Instead of just telling an element to stay in “Column 2,” you tell it which lines to start and end on using the grid-column and grid-row properties.

.main-content {
  /* Starts at vertical line 1, ends at vertical line 3 */
  grid-column: 1 / 3; 
  
  /* Starts at horizontal line 2, ends at horizontal line 4 */
  grid-row: 2 / 4; 
}

This “line-based positioning” allows elements to overlap or leave intentional empty spaces, giving you far more control than traditional layout methods.


Using Negative Numbers

One of the coolest “pro tips” for CSS Grid is that lines are also indexed backwards.

  • The very last line of your grid is always -1.
  • The second to last line is -2.

This is incredibly useful when you want an element to span the full width of a layout regardless of how many columns you add later. You simply set grid-column: 1 / -1;.

Why Lines Matter More Than “Lanes”

While it’s easy to think about the “lanes” (the tracks) where content sits, the lines are the actual anchors. By focusing on the lines, you can:

  1. Create “Bleed” Layouts: Easily push images to the very edge of the viewport.
  2. Layer Content: Place multiple items within the same line boundaries to stack them.
  3. Build Responsive Bricks: Shift an item from 1 / 2 on mobile to 1 / 4 on desktop with just one line of code.

Creating a Pinterest-Style Masonry Layout Using CSS Grid Lanes

The classic “Pinterest layout” where cards of varying heights tile together without ugly gaps has historically required JavaScript libraries. With the CSS grid-template-rows: masonry spec gaining traction (currently behind a flag in Firefox), and a solid workaround using grid-row spanning, you can get surprisingly close using only CSS Grid.

The Core Trick: grid-row: span N

Instead of fixed row heights, you define a grid with many short row tracks and let each card span a different number of rows based on its content height. The key properties:

.masonry-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: masonry; /* future native support */
  gap: 1rem;
}

For the current cross-browser workaround, define small row units and use span:

.masonry-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 10px; /* small unit rows */
  gap: 1rem;
}

.card {
  grid-row: span 20; /* short card */
}

.card.tall {
  grid-row: span 35; /* taller card */
}

The trick is setting grid-auto-rows to a small value (like 10px) and then using JavaScript—just once on load—to calculate each card’s natural height and assign the right span value dynamically:

function resizeCard(card) {
  const rowHeight = 10;
  const gap = 16; // matches your CSS gap
  const cardHeight = card.querySelector('.card-content').getBoundingClientRect().height;
  const rowSpan = Math.ceil((cardHeight + gap) / (rowHeight + gap));
  card.style.gridRowEnd = `span ${rowSpan}`;
}

document.querySelectorAll('.card').forEach(resizeCard);

Defining the Lanes

This is where grid lines come back into play. With a 3-column masonry layout, you have 4 vertical lines. You can pin specific “feature” cards to span multiple columns using the same line syntax:

.card.featured {
  grid-column: 1 / 3; /* spans from line 1 to line 3 */
}

Combined with the row-span technique, this lets you create a fully editorial layout—some cards wide, some tall, all tightly packed—without a single layout library.

The Native Future: grid-template-rows: masonry

The CSS Working Group has a spec for native masonry that removes the JavaScript entirely:

.masonry-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: masonry;
}

This is currently available in Firefox behind the layout.css.grid-template-masonry-value.enabled flag. Until it lands in all browsers, the span + small grid-auto-rows approach is the most reliable pure-CSS-leaning solution.


Unfortunately this isn’t totally usable yet in production code