CSS Grid in Practice: From repeat() to grid-template-areas#

I was refactoring an admin dashboard recently. The layout seemed simple at first: fixed sidebar on the left, main content on the right. But the main area needed a stats section with 3 auto-fitting columns above a data table. After nesting Flexbox containers three levels deep, I realized—this is Grid’s home turf.

Grid vs Flexbox: The Key Difference#

Flexbox is one-dimensional. Grid is two-dimensional.

/* Flexbox: controls one direction at a time */
.container {
  display: flex;
  flex-direction: row; /* or column */
}

/* Grid: controls rows AND columns simultaneously */
.container {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;
  grid-template-rows: 60px 1fr;
}

Grid properties fall into two categories:

  1. Container properties: grid-template-columns, grid-template-rows, gap, justify-items, align-items
  2. Item properties: grid-column, grid-row, grid-area

The repeat() Function: Stop Writing Repetitive Values#

repeat() is Grid’s most used function:

/* 3 equal-width columns */
.container {
  grid-template-columns: repeat(3, 1fr);
}

/* Equivalent to */
.container {
  grid-template-columns: 1fr 1fr 1fr;
}

/* Mixed usage */
.container {
  grid-template-columns: 200px repeat(3, 1fr) 100px;
  /* Fixed 200px left, 3 auto columns middle, fixed 100px right */
}

1fr is a Grid-specific unit meaning “one fraction of available space.” In a 1000px container, repeat(3, 1fr) gives each column 333.33px.

minmax(): Responsive Boundaries#

The pain of responsive layouts: content gets squeezed on small screens, stretched on large ones. minmax() sets both minimum and maximum:

.container {
  /* Each column: minimum 200px, maximum 1fr */
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

auto-fit calculates column count based on container width. A 1000px container fits 5 columns (1000 ÷ 200 = 5). A 600px container fits only 3.

Perfect for card layouts:

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
}

grid-template-areas: Semantic Layout#

Grid’s most powerful feature is grid-template-areas. You “draw” the layout with strings:

.container {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: 60px 1fr 40px;
  grid-template-areas:
    "sidebar header"
    "sidebar main"
    "sidebar footer";
}

.sidebar { grid-area: sidebar; }
.header { grid-area: header; }
.main { grid-area: main; }
.footer { grid-area: footer; }

Benefits:

  1. Readable: Layout structure is obvious at a glance
  2. Easy to modify: Change strings to change layout
  3. Responsive-friendly: Rewrite grid-template-areas in media queries
@media (max-width: 768px) {
  .container {
    grid-template-columns: 1fr;
    grid-template-rows: 60px 1fr 40px;
    grid-template-areas:
      "header"
      "main"
      "footer";
    /* Hide sidebar on mobile */
  }
  .sidebar { display: none; }
}

The gap Property: Spacing Made Simple#

Grid’s gap is much cleaner than Flexbox’s margin approach:

.container {
  gap: 20px; /* Both row and column gap: 20px */
  
  /* Or set separately */
  row-gap: 16px;
  column-gap: 24px;
  
  /* Shorthand */
  gap: 16px 24px; /* Row gap 16px, column gap 24px */
}

justify-items and align-items#

These control how items align within their cells:

.container {
  justify-items: stretch; /* Horizontal stretch (default) */
  align-items: stretch;   /* Vertical stretch (default) */
  
  /* Other values: start | center | end */
}

Common use case: centering buttons in cells:

.button-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  justify-items: center; /* Buttons centered horizontally */
  gap: 12px;
}

.button-grid button {
  width: 120px; /* Fixed button width */
}

Real-World Example: Admin Dashboard#

Back to my original requirement, here’s the Grid implementation:

.admin-layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: 60px 1fr;
  grid-template-areas:
    "sidebar header"
    "sidebar main";
  height: 100vh;
}

.sidebar { grid-area: sidebar; }
.header { grid-area: header; }
.main {
  grid-area: main;
  display: grid;
  grid-template-rows: auto 1fr;
  gap: 24px;
  padding: 24px;
}

/* Stats cards */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}

/* Data table */
.table-container {
  overflow: auto;
}

Clean structure, no nesting hell.

Debugging Grid#

Chrome DevTools has built-in Grid visualization:

  1. Open DevTools, select a Grid container
  2. Click the grid badge in the Elements panel
  3. Check “Show grid lines” and “Show track sizes”

You’ll see grid lines and dimensions visually, making layout issues easy to spot.

Browser Support#

Grid has been supported by all major browsers since 2017. IE11 supports a prefixed version with some behavioral differences. For IE11 support, use Autoprefixer:

/* Autoprefixer output */
.container {
  display: -ms-grid;
  display: grid;
  -ms-grid-columns: 200px 1fr;
  grid-template-columns: 200px 1fr;
}

Online Tool#

I built a Grid Layout Generator for quick prototyping: Grid Generator

Features:

  • Visual row/column adjustment
  • Live preview
  • One-click CSS copy
  • justify-items and align-items controls

Related: Flexbox Generator | CSS Gradient Generator