Module 8

Layout: CSS Grid

Grid Container • Templates • Areas • Spanning • Responsive Patterns

πŸ”² Why CSS Grid Changes Everything (Again)

In the previous module, you learned Flexbox β€” a powerful one-dimensional layout system. Flexbox controls items along a single axis (row or column). But what about laying out items in rows and columns simultaneously? That is where CSS Grid comes in.

CSS Grid is a two-dimensional layout system. It lets you define both columns and rows at the same time, then place items precisely into that grid. It is the most powerful layout tool in CSS, and together with Flexbox, it gives you complete control over any design.

πŸ’‘
The City Map Analogy: Imagine a city. Flexbox is like a single street β€” you can arrange buildings along it, but only in one direction. CSS Grid is the entire city map β€” you define a grid of streets (rows and columns), then place buildings on specific blocks. You decide exactly where each building goes and how many blocks it spans.
⚠️
Grid vs. Flexbox β€” when to use which? Grid is not a replacement for Flexbox. Use Flexbox when you need one-dimensional control (a navbar, a row of buttons, centering). Use Grid when you need two-dimensional control (page layouts, dashboards, card grids with precise placement). Many layouts combine both.

🎯 By the end of this module, you will:

  • Create grid containers with display: grid
  • Define columns and rows with grid-template-columns and grid-template-rows
  • Use the fr unit and repeat() for flexible grids
  • Place items with grid-column, grid-row, and spanning
  • Name grid areas with grid-template-areas for visual layouts
  • Align items and the grid itself with justify-items, align-items, and place-items
  • Build responsive grids with auto-fit, auto-fill, and minmax()

πŸ“¦ The Grid Container

Just like Flexbox, Grid starts with a parent-child relationship. Set display: grid on a parent element, and its direct children become grid items. But unlike Flexbox, you also define the structure of the grid β€” how many columns and rows it has.

CSS
/* Create a 3-column grid */
.grid {
  display: grid;
  grid-template-columns: 200px 200px 200px;
  gap: 16px;
}

.item {
  padding: 20px;
  background: #3b82f6;
  color: white;
  border-radius: 8px;
  text-align: center;
  font-weight: 600;
}

grid-template-columns: 200px 200px 200px creates three columns, each 200px wide. Items fill from left to right, top to bottom β€” when a row is full, a new row is created automatically.

grid-template-rows

While columns are explicitly defined, rows are created implicitly (automatically) as items fill the grid. You can also define explicit row sizes with grid-template-rows:

CSS
/* Explicit rows and columns */
.grid {
  display: grid;
  grid-template-columns: 200px 200px;
  grid-template-rows: 100px 150px;
  gap: 12px;
}

.item {
  background: #6366f1;
  color: white;
  padding: 16px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
}

gap (row-gap & column-gap)

The gap property works exactly the same in Grid as in Flexbox β€” it adds space between items, never on the outer edges. You can set different row and column gaps:

CSS
/* Same gap on both axes */
gap: 20px;

/* Different row and column gaps */
row-gap: 24px;
column-gap: 16px;

/* Shorthand: row-gap column-gap */
gap: 24px 16px;

Grid Terminology

1 Grid Line β€” The dividing lines that form the structure. A 3-column grid has 4 vertical lines (numbered 1–4).
2 Grid Track β€” A row or a column β€” the space between two adjacent grid lines.
3 Grid Cell β€” A single unit of the grid β€” the intersection of one row and one column. Like a cell in a table.
4 Grid Area β€” A rectangular region spanning one or more cells. Items can span multiple cells to create areas.

πŸ“ The fr Unit & repeat()

Fixed pixel widths are rigid. CSS Grid introduced the fr (fraction) unit β€” it represents a fraction of the available space. This is the foundation of flexible, responsive grids.

CSS
/* Three equal columns */
.equal {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 16px;
}

/* Sidebar (fixed) + Main (flexible) */
.sidebar-layout {
  display: grid;
  grid-template-columns: 250px 1fr;
  gap: 24px;
}

/* 1fr 2fr 1fr β€” middle column gets twice the space */
.weighted {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  gap: 16px;
}

.item {
  padding: 20px;
  background: #6366f1;
  color: white;
  border-radius: 8px;
  text-align: center;
}

repeat()

Writing 1fr 1fr 1fr 1fr is tedious. The repeat() function saves you from repetition:

CSS
/* repeat(count, track-size) */

/* 4 equal columns */
.four-col {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}

/* 12-column layout (like Bootstrap) */
.twelve-col {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 16px;
}

.item {
  padding: 16px;
  background: #3b82f6;
  color: white;
  border-radius: 8px;
  text-align: center;
  font-weight: 600;
}

minmax()

The minmax(min, max) function sets a flexible range for a track. It ensures a column is at least min wide but can grow up to max. This is essential for responsive grids:

CSS
/* Column is at least 200px, at most 1fr */
grid-template-columns: minmax(200px, 1fr) 1fr 1fr;

/* Row is at least 100px, grows to fit content */
grid-template-rows: minmax(100px, auto);
πŸ’‘
fr vs. % β€” what is the difference? Percentages are based on the container width and do not account for gaps. fr distributes the remaining space after gaps. With gap: 20px and three 33.33% columns, the total exceeds 100%. With 1fr 1fr 1fr, the gaps are subtracted first, then space is split equally. Always prefer fr over % in Grid.

πŸ“ Placing Items on the Grid

By default, grid items fill cells from left to right, top to bottom β€” just like text. But you can place any item anywhere on the grid using grid line numbers.

A 3-column grid has 4 column lines (numbered 1, 2, 3, 4) and each row adds row lines. You tell an item which lines to start and end at:

CSS
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 80px 80px 80px;
  gap: 12px;
}

/* Span 2 columns */
.header {
  grid-column: 1 / 3;  /* line 1 to line 3 */
}

/* Span 2 rows */
.sidebar {
  grid-row: 2 / 4;     /* line 2 to line 4 */
}

/* Full-width footer */
.footer {
  grid-column: 1 / -1; /* line 1 to last line */
}

.item {
  padding: 16px;
  border-radius: 8px;
  color: white;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}
.header  { background: #3b82f6; }
.sidebar { background: #6366f1; }
.main    { background: #8b5cf6; }
.footer  { background: #1e293b; }

The span Keyword

Instead of counting line numbers, you can use span to say "take up N cells":

CSS
/* These are equivalent: */
grid-column: 1 / 3;       /* from line 1 to line 3 */
grid-column: 1 / span 2;  /* from line 1, span 2 columns */
grid-column: span 2;      /* span 2 from current position */

/* Negative lines count from the end: */
grid-column: 1 / -1;      /* span the entire row */
πŸ’‘
Negative line numbers -1 always refers to the last grid line. So grid-column: 1 / -1 means "from the first column line to the last" β€” a full-width element. This works regardless of how many columns exist, making it very flexible.

grid-auto-flow

By default, items flow in row order (left to right, new row when full). You can change this with grid-auto-flow: column to fill columns first (top to bottom, new column when full). The dense keyword fills in gaps: grid-auto-flow: row dense.

πŸ—ΊοΈ Named Grid Areas

Line numbers work, but they are hard to read. grid-template-areas lets you name each region of your grid and assign items to those names β€” creating a visual map of your layout right in CSS.

CSS
.page {
  display: grid;
  grid-template-columns: 220px 1fr;
  grid-template-rows: 60px 1fr 50px;
  grid-template-areas:
    "header  header"
    "sidebar content"
    "footer  footer";
  gap: 12px;
  min-height: 400px;
}

.page-header  { grid-area: header;  background: #3b82f6; }
.page-sidebar { grid-area: sidebar; background: #6366f1; }
.page-content { grid-area: content; background: #8b5cf6; }
.page-footer  { grid-area: footer;  background: #1e293b; }

.page div {
  color: white;
  padding: 16px;
  font-weight: 600;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
}
πŸ’‘
Empty cells with a dot Use a . (dot) to leave a cell empty: "header header" ". content" "footer footer". The dot cell will be blank space.

The beauty of grid-template-areas is readability β€” you can look at the CSS and see your layout. Each quoted string is a row. The names must form a rectangle (no L-shapes). Repeat a name to span cells.

⚠️
Areas must be rectangular Each named area must form a rectangle. You cannot create L-shaped or T-shaped areas with grid-template-areas. If you need complex shapes, use grid-column/grid-row line numbers on individual items.

⬑ Grid Alignment

Grid has a rich alignment system similar to Flexbox but with more options, since it works on two axes simultaneously. There are two levels: aligning items within their cells and aligning the grid tracks within the container.

justify-items & align-items (cell alignment)

These properties align all grid items within their respective cells. justify-items aligns along the inline (horizontal) axis. align-items aligns along the block (vertical) axis.

Alignment Values

1 stretch β€” Item fills the entire cell (default).
2 start β€” Item aligns to the start of the cell.
3 end β€” Item aligns to the end of the cell.
4 center β€” Item is centered within the cell.
CSS
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 150px 150px;
  gap: 12px;
  /* Center items in their cells */
  justify-items: center;
  align-items: center;
}

/* Shorthand: place-items: align justify */
.grid-shorthand {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 150px 150px;
  gap: 12px;
  place-items: center; /* both axes */
}

.item {
  padding: 16px 24px;
  background: #6366f1;
  color: white;
  border-radius: 8px;
  font-weight: 600;
}

justify-self & align-self (individual items)

Just like Flexbox's align-self, Grid lets individual items override their alignment with justify-self and align-self (or the shorthand place-self).

CSS
/* Override one item's alignment */
.special-item {
  justify-self: end;
  align-self: start;
}

/* Shorthand: place-self: align justify */
.special-item {
  place-self: start end;
}

justify-content & align-content (track alignment)

When the total size of the grid tracks is smaller than the container, you can align the entire grid with justify-content (horizontal) and align-content (vertical). These take the same values as Flexbox: start, end, center, space-between, space-around, space-evenly.

πŸ“± Responsive Grid Patterns

The real power of CSS Grid shines in responsive design. By combining repeat(), auto-fit/auto-fill, and minmax(), you can create grids that adapt to any screen size without a single media query.

auto-fit vs. auto-fill

Instead of a fixed number of columns, auto-fit and auto-fill let the browser decide how many columns fit:

CSS
/* The holy grail of responsive grids */
.auto-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
}

.card {
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  padding: 24px;
}
.card h3 { margin: 0 0 8px; color: #1e293b; }
.card p  { margin: 0; color: #64748b; }

This single line β€” repeat(auto-fit, minmax(250px, 1fr)) β€” creates a grid where: (1) each column is at least 250px wide, (2) columns expand to fill all available space, (3) the number of columns adjusts automatically as the viewport changes. On a wide screen you might get 4 columns; on a phone, just 1.

auto-fit vs. auto-fill

1 auto-fit β€” Fits as many columns as possible, then collapses empty tracks. Items stretch to fill the container. Use this when you want items to grow.
2 auto-fill β€” Fills as many columns as possible, but preserves empty tracks. Items do not stretch into empty tracks. Use this when you want consistent column widths even with fewer items.
πŸ’‘
Which one to use? In most cases, auto-fit is what you want β€” items stretch to fill the row. Use auto-fill only when you need items to maintain a maximum width even if there is extra space.

Responsive Areas with Media Queries

For full page layouts, combine grid-template-areas with media queries to rearrange entire layouts:

CSS
/* Mobile: single column */
.page {
  display: grid;
  grid-template-areas:
    "header"
    "nav"
    "main"
    "footer";
  gap: 12px;
  min-height: 400px;
}

/* Desktop: sidebar layout */
@media (min-width: 768px) {
  .page {
    grid-template-columns: 200px 1fr;
    grid-template-rows: auto 1fr auto;
    grid-template-areas:
      "header header"
      "nav    main"
      "footer footer";
  }
}

.hd { grid-area: header;  background: #3b82f6; }
.nv { grid-area: nav;     background: #6366f1; }
.mn { grid-area: main;    background: #8b5cf6; }
.ft { grid-area: footer;  background: #1e293b; }

.page div {
  color: white;
  padding: 16px;
  border-radius: 8px;
  font-weight: 600;
}

πŸ—οΈ Real-World CSS Grid Patterns

Let's combine everything into practical, production-ready patterns you'll use on real websites.

Pattern 1: Dashboard Layout

A classic dashboard with a header, sidebar, main content, and widgets panel:

CSS
.dashboard {
  display: grid;
  grid-template-columns: 220px 1fr 280px;
  grid-template-rows: 60px 1fr;
  grid-template-areas:
    "header header  header"
    "nav    main    aside";
  gap: 0;
  min-height: 400px;
}

.dash-header { grid-area: header; background: #1e293b; color: white; }
.dash-nav    { grid-area: nav;    background: #334155; color: white; }
.dash-main   { grid-area: main;   background: #f1f5f9; }
.dash-aside  { grid-area: aside;  background: #e2e8f0; }

.dashboard div {
  padding: 16px;
  font-weight: 600;
  display: flex;
  align-items: center;
}

Pattern 2: Auto-Fit Card Grid

The most common Grid pattern β€” responsive cards that automatically reflow. No media queries needed.

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

.card {
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  padding: 24px;
  transition: box-shadow 0.2s;
}
.card:hover {
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.card h3 { margin: 0 0 8px; }
.card p  { margin: 0; color: #64748b; }

Pattern 3: Image Gallery with Spanning

A photo gallery where some images span multiple cells, creating a magazine-like layout:

CSS
.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 180px;
  gap: 12px;
}

/* Feature image: spans 2 cols, 2 rows */
.gallery .featured {
  grid-column: span 2;
  grid-row: span 2;
}

.gallery div {
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-weight: 700;
  font-size: 1.2rem;
}
.gallery .featured { background: #6366f1; }
.gallery .small:nth-child(2) { background: #3b82f6; }
.gallery .small:nth-child(3) { background: #8b5cf6; }
.gallery .small:nth-child(4) { background: #ec4899; }
.gallery .small:nth-child(5) { background: #10b981; }

Pattern 4: Holy Grail Layout

The "Holy Grail" is the classic web layout: header, footer, main content, and two sidebars. Grid makes it trivial:

CSS
.holy-grail {
  display: grid;
  grid-template:
    "header header header" 60px
    "left   main   right"  1fr
    "footer footer footer" 50px
    / 180px 1fr 180px;
  min-height: 400px;
  gap: 8px;
}

.hg-header { grid-area: header; background: #3b82f6; }
.hg-left   { grid-area: left;   background: #6366f1; }
.hg-main   { grid-area: main;   background: #f1f5f9; color: #1e293b; }
.hg-right  { grid-area: right;  background: #8b5cf6; }
.hg-footer { grid-area: footer; background: #1e293b; }

.holy-grail div {
  color: white;
  padding: 16px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}
πŸ’‘
The grid-template shorthand The grid-template property combines grid-template-areas, grid-template-rows, and grid-template-columns into one declaration. The row sizes appear after each area string, and columns come after the /.

✏️ Practice: CSS Grid Layout

Test your understanding of grid containers, templates, areas, spanning, and responsive patterns.

Exercise 1: Three Equal Columns

Write CSS for a .grid container that creates three equal-width columns with 20px gap between them.

Reveal Solution

.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }

πŸ’‘ repeat(3, 1fr) creates three columns that each get an equal fraction of the available space. gap is subtracted before the fr units are calculated.

Exercise 2: Sidebar + Main with Grid

Create a two-column grid layout where: (1) the sidebar is exactly 260px, (2) the main content fills the rest, (3) there is a 24px gap between them.

Reveal Solution

.layout { display: grid; grid-template-columns: 260px 1fr; gap: 24px; }

πŸ’‘ 260px gives the sidebar a fixed width. 1fr takes all remaining space. Compare this to the Flexbox approach: flex: 0 0 260px + flex: 1. Grid is more concise for this pattern.

Exercise 3: Responsive Card Grid

Create a responsive card grid where: (1) cards are at least 300px wide, (2) cards automatically fill available columns, (3) cards stretch to fill any remaining space, (4) 24px gap between all cards.

Reveal Solution

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

πŸ’‘ auto-fit creates as many columns as possible. minmax(300px, 1fr) ensures each column is at least 300px and grows to fill space. This one line replaces multiple media queries.

Exercise 4: Page Layout with Named Areas

Create a page layout using grid-template-areas with: (1) a full-width header, (2) a 200px sidebar on the left, (3) main content filling the rest, (4) a full-width footer. The page should be at least 100vh tall.

Reveal Solution

.page { display: grid; grid-template-columns: 200px 1fr; grid-template-rows: auto 1fr auto; grid-template-areas: "header header" "sidebar main" "footer footer"; min-height: 100vh; }

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

πŸ’‘ Each area name in the template must form a rectangle. Repeating "header" across both columns makes it span the full width. 1fr on the middle row makes main content fill the remaining height.

πŸ“‹ Module Summary

πŸ“¦
Grid Container

display: grid activates Grid. Define structure with grid-template-columns and grid-template-rows. Items flow automatically into cells.

πŸ“
fr & repeat()

fr divides remaining space. repeat(3, 1fr) for equal columns. minmax(250px, 1fr) for flexible ranges. Always prefer fr over %.

πŸ“
Placing Items

grid-column: 1 / 3 or span 2 to span cells. grid-column: 1 / -1 for full-width. Line numbers start at 1.

πŸ—ΊοΈ
Named Areas

grid-template-areas creates a visual map. Assign items with grid-area: name. Use . for empty cells. Areas must be rectangular.

⬑
Alignment

place-items: center centers all items. place-self overrides one item. justify-content/align-content positions the grid tracks.

πŸ“±
Responsive

repeat(auto-fit, minmax(250px, 1fr)) β€” one line for fully responsive grids. Combine areas + media queries for layout shifts.

Next Module β†’

Module 9: Responsive Web Design

Media queries, mobile-first approach, responsive images, viewport units, and the full strategy for building websites that work on every device.

β†’