CSS3 & Responsive Design

Grid Container: Tracks, Gaps & Templates

35 min Lesson 28 of 60

Introduction to CSS Grid Layout

CSS Grid Layout is the most powerful layout system available in CSS. Unlike Flexbox, which is fundamentally a one-dimensional layout model (handling either rows or columns at a time), CSS Grid is a two-dimensional layout system that lets you control both columns and rows simultaneously. This makes Grid the ideal choice for building full page layouts, complex component arrangements, and any design where you need precise control over both horizontal and vertical placement.

Grid was designed from the ground up for layout. Before Grid, developers relied on hacks involving floats, table display modes, and absolute positioning to achieve complex layouts. Flexbox improved things significantly for one-dimensional flows, but it was never meant to handle two-dimensional layouts on its own. Grid fills that gap perfectly. In modern CSS, Grid and Flexbox are complementary tools: use Grid for the overall page structure and two-dimensional component layouts, and use Flexbox for one-dimensional alignment within those grid areas.

In this lesson, we will focus entirely on the grid container -- the parent element that establishes the grid context. You will learn how to define tracks (columns and rows), control gaps between them, use powerful sizing functions like fr, repeat(), and minmax(), understand the difference between explicit and implicit grids, control how items flow automatically, name your grid lines, and use template shorthands. Mastering the container properties is the foundation for everything you will do with Grid.

Enabling Grid: display: grid and display: inline-grid

To create a grid container, you apply display: grid or display: inline-grid to an element. This transforms the element into a grid container and all of its direct children become grid items. The difference between grid and inline-grid is identical to the difference between block and inline-block: a grid container behaves as a block-level element (takes up the full width available and starts on a new line), while an inline-grid container behaves as an inline-level element (only takes up as much width as needed and flows inline with surrounding content).

Example: Creating a Grid Container

/* Block-level grid container */
.grid-container {
    display: grid;
    /* This element now generates a grid formatting context */
    /* All direct children become grid items */
}

/* Inline-level grid container */
.inline-grid-container {
    display: inline-grid;
    /* Behaves like inline-block externally */
    /* Children are still grid items internally */
}

Example: Grid vs Inline-Grid Behavior

<div class="wrapper">
    <p>Text before the grid.</p>

    <div class="grid-container">
        <div>Item 1</div>
        <div>Item 2</div>
        <div>Item 3</div>
    </div>

    <p>Text after the grid.</p>
</div>

With display: grid, the container takes the full width and the paragraphs appear above and below it. With display: inline-grid, the container only takes up the space needed by its columns and can sit alongside inline content. In practice, display: grid is used in the vast majority of cases. You would use inline-grid only when you need the grid to flow inline with text or other inline elements.

Note: Once you set display: grid, the direct children automatically become grid items. Properties like float, clear, and vertical-align have no effect on grid items. The column-* properties from multi-column layout also have no effect inside a grid container.

Grid Terminology You Must Know

Before diving into properties, let us establish the vocabulary of CSS Grid. Understanding these terms is essential because every Grid property relates back to these concepts.

  • Grid Container -- The element with display: grid. It establishes the grid formatting context for its contents.
  • Grid Item -- A direct child of the grid container. Each grid item is placed into the grid either automatically or explicitly.
  • Grid Line -- The dividing lines that make up the structure of the grid. They can be horizontal (row lines) or vertical (column lines). Lines are numbered starting from 1. A 3-column grid has 4 column lines (1, 2, 3, 4).
  • Grid Track -- The space between two adjacent grid lines. A track is essentially a column or a row. A 3-column grid has 3 column tracks.
  • Grid Cell -- The space between two adjacent row lines and two adjacent column lines. It is the smallest unit of the grid, like a table cell. A 3-column, 2-row grid has 6 cells.
  • Grid Area -- A rectangular space made up of one or more grid cells. An area can span multiple tracks both horizontally and vertically.
  • Grid Gap (Gutter) -- The spacing between grid tracks. Gaps only appear between tracks, not on the outer edges of the grid.
  • Explicit Grid -- The grid you define using grid-template-columns and grid-template-rows.
  • Implicit Grid -- Additional tracks the browser creates automatically when items are placed outside the explicit grid.

Example: Visualizing Grid Terminology

/*
    Column Lines:  1     2     3     4
                   |     |     |     |
    Row Line 1 --- +-----+-----+-----+
                   |Cell |Cell |Cell |  <-- Row Track 1
    Row Line 2 --- +-----+-----+-----+
                   |Cell |Cell |Cell |  <-- Row Track 2
    Row Line 3 --- +-----+-----+-----+
                   ^           ^
                   |           |
              Column       Column
              Track 1      Track 2
*/

.grid {
    display: grid;
    grid-template-columns: 200px 200px 200px; /* 3 column tracks */
    grid-template-rows: 100px 100px;           /* 2 row tracks */
    /* This creates: 4 column lines, 3 row lines, 6 cells */
}

Defining Column Tracks: grid-template-columns

The grid-template-columns property defines the number and size of column tracks in the explicit grid. Each value you provide creates one column track. You can use any CSS length unit (px, em, rem, %), the new fr unit, or sizing functions like minmax() and repeat().

Example: Basic Column Definitions

/* Three fixed-width columns */
.grid {
    display: grid;
    grid-template-columns: 200px 300px 200px;
}

/* Three percentage-based columns */
.grid {
    display: grid;
    grid-template-columns: 25% 50% 25%;
}

/* Mixed units */
.grid {
    display: grid;
    grid-template-columns: 250px auto 250px;
    /* Sidebars are fixed, center fills remaining space */
}

/* Using rem units */
.grid {
    display: grid;
    grid-template-columns: 15rem 1fr 15rem;
}

Defining Row Tracks: grid-template-rows

The grid-template-rows property works exactly like grid-template-columns but defines row tracks instead. Each value creates one row track. If you have more items than the explicit rows can hold, the browser creates implicit rows automatically.

Example: Defining Rows

/* Three explicit rows */
.grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 80px auto 60px;
    /* Header row: 80px, content row: auto, footer row: 60px */
}

/* Two rows with different heights */
.grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 200px 300px;
}

/* Row heights using min-content and max-content */
.grid {
    display: grid;
    grid-template-rows: min-content auto min-content;
    /* First and last rows size to their content */
    /* Middle row takes remaining space */
}

The fr Unit: Fractional Remaining Space

The fr unit is unique to CSS Grid and stands for fraction. It represents a fraction of the available space in the grid container after all fixed-size tracks and gaps have been accounted for. The fr unit is the most important sizing tool in Grid because it allows you to create flexible, proportional layouts without calculating percentages manually.

Example: Understanding the fr Unit

/* Equal thirds */
.grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    /* Each column gets 1/3 of the available space */
}

/* Proportional columns */
.grid {
    display: grid;
    grid-template-columns: 1fr 2fr 1fr;
    /* Total: 4fr. Columns are 25%, 50%, 25% of space */
}

/* Mixing fr with fixed sizes */
.grid {
    display: grid;
    grid-template-columns: 250px 1fr 250px;
    /* Sidebars are 250px each */
    /* Center gets ALL remaining space */
    /* If container is 1000px: center = 1000 - 250 - 250 = 500px */
}

/* Mixing fr with percentages */
.grid {
    display: grid;
    grid-template-columns: 20% 1fr 1fr;
    /* First column is 20% of the container */
    /* Remaining 80% is split equally between the two 1fr columns */
}
Pro Tip: The fr unit distributes space after fixed tracks, gaps, and percentage tracks are calculated. This makes it different from percentages, which do not account for gaps. If you have a 1000px container with gap: 20px and three columns set to 33.33%, you will get overflow because percentages do not subtract the gap. With 1fr 1fr 1fr, the browser first subtracts the 40px of total gap space (two gaps at 20px each) and then divides the remaining 960px equally. This is why fr is almost always better than percentages for grid columns.

How fr Interacts with Content

By default, an fr track will not shrink smaller than its min-content size. This means if a grid cell contains a long word or a fixed-width element, the fr track will expand to fit it, potentially making the proportions unequal. To override this behavior, you can use minmax(0, 1fr) instead of plain 1fr.

Example: fr and Minimum Content Size

/* Default behavior: fr respects min-content */
.grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    /* If one cell has a 400px image, that column will be at least 400px */
    /* The other columns share whatever space is left */
}

/* Override: allow fr columns to shrink below content size */
.grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
    /* Now all columns are truly equal, content may overflow */
}

The repeat() Function

The repeat() function is a shorthand that lets you define multiple tracks with the same or patterned sizes without writing them out individually. It takes two arguments: the number of repetitions and the track size (or pattern of sizes) to repeat.

Example: Using repeat()

/* Instead of writing: */
.grid {
    grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
}

/* You can write: */
.grid {
    grid-template-columns: repeat(6, 1fr);
}

/* Repeating a fixed size */
.grid {
    grid-template-columns: repeat(4, 200px);
    /* Creates: 200px 200px 200px 200px */
}

/* Repeating a pattern */
.grid {
    grid-template-columns: repeat(3, 1fr 2fr);
    /* Creates: 1fr 2fr 1fr 2fr 1fr 2fr (6 columns) */
}

/* Mixing repeat() with other values */
.grid {
    grid-template-columns: 250px repeat(3, 1fr) 250px;
    /* Creates: 250px 1fr 1fr 1fr 250px */
}

/* Using repeat() for rows too */
.grid {
    grid-template-rows: repeat(4, 100px);
    /* Creates 4 rows, each 100px tall */
}

The minmax() Function

The minmax() function defines a size range for a track. It takes two arguments: a minimum size and a maximum size. The track will be at least the minimum size but will not grow beyond the maximum size. This is incredibly useful for creating responsive layouts that adapt to content while staying within reasonable bounds.

Example: Using minmax()

/* Columns that are at least 200px but can grow */
.grid {
    display: grid;
    grid-template-columns: minmax(200px, 1fr) minmax(200px, 1fr) minmax(200px, 1fr);
}

/* Rows with a minimum height */
.grid {
    display: grid;
    grid-template-rows: minmax(100px, auto);
    /* Row is at least 100px, but grows to fit content */
}

/* Combining with repeat() */
.grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(250px, 1fr));
    /* Three columns, each at least 250px, sharing space equally */
}

/* Fixed minimum, flexible maximum */
.grid {
    display: grid;
    grid-template-columns: minmax(300px, 500px) 1fr;
    /* First column: at least 300px, at most 500px */
    /* Second column: takes all remaining space */
}

/* Content-based minimum */
.grid {
    display: grid;
    grid-template-columns: minmax(min-content, 300px) 1fr;
    /* First column: at least as wide as its content, max 300px */
}
Note: Inside minmax(), you cannot use the fr unit as the minimum value, but you can use it as the maximum value. Valid minimum values include lengths (px, em, rem), percentages, min-content, max-content, and auto. Valid maximum values include all of the above plus fr.

auto-fill vs auto-fit

The auto-fill and auto-fit keywords are used inside repeat() in place of a fixed repetition count. They tell the browser to create as many tracks as will fit in the container. The difference between them becomes apparent when there are fewer items than tracks.

Example: auto-fill

/* auto-fill: creates as many 200px columns as fit */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, 200px);
    /* In a 1000px container: 5 columns of 200px */
    /* In a 600px container: 3 columns of 200px */
    /* Empty tracks are preserved even without items */
}

/* auto-fill with minmax for responsive layout */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    /* Creates as many columns as fit, each at least 250px */
    /* Columns stretch with 1fr to fill remaining space */
    /* Empty tracks are preserved (items do not stretch to fill) */
}

Example: auto-fit

/* auto-fit: same as auto-fill but collapses empty tracks */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    /* Creates as many columns as fit, each at least 250px */
    /* BUT empty tracks collapse to 0, letting items stretch further */
}

/* The difference: 3 items in a wide container */
/* auto-fill: [item][item][item][empty][empty] */
/* auto-fit:  [  item  ][  item  ][  item  ]  */
/* With auto-fit, items expand to fill ALL available space */
Pro Tip: The most common responsive grid pattern in modern CSS is grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)). This single line creates a fully responsive grid of cards without any media queries. The cards are at least 250px wide, and they automatically reflow into fewer columns as the viewport shrinks. Use auto-fit when you want items to stretch to fill the row, and auto-fill when you want to preserve consistent column widths even if some cells are empty.

Gap Properties: gap, row-gap, and column-gap

The gap properties control the spacing between grid tracks (but not at the outer edges). Previously known as grid-gap, grid-row-gap, and grid-column-gap, the modern unprefixed versions (gap, row-gap, column-gap) work in both Grid and Flexbox.

Example: Setting Gaps

/* Same gap for rows and columns */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    /* 20px between all rows and all columns */
}

/* Different row and column gaps */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    row-gap: 30px;
    column-gap: 15px;
    /* 30px between rows, 15px between columns */
}

/* Shorthand: row-gap then column-gap */
.grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 30px 15px;
    /* First value is row-gap, second is column-gap */
}

/* Using relative units for responsive gaps */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2vw;
    /* Gap scales with viewport width */
}

/* Percentage gaps (relative to the grid container size) */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2%;
}
Important: Gaps only appear between tracks, never on the outer edges. If you need spacing around the entire grid, use padding on the grid container. Also, remember that fr units calculate their size after gaps are subtracted. A grid with grid-template-columns: 1fr 1fr and column-gap: 20px in a 1000px container gives each column (1000 - 20) / 2 = 490px.

Implicit vs Explicit Grid

The explicit grid is the grid you define with grid-template-columns and grid-template-rows. The implicit grid consists of any additional tracks the browser creates automatically to accommodate items that are placed outside the explicit grid boundaries, or when there are more items than the explicit grid can hold.

Example: Explicit vs Implicit Grid

.grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;  /* 3 explicit columns */
    grid-template-rows: 100px 100px;      /* 2 explicit rows */
}

/* If you have 9 items:
   Items 1-6 go into the explicit 3x2 grid
   Items 7-9 need a third row -- this is IMPLICIT
   The implicit row height defaults to auto (fits content) */

Controlling Implicit Tracks: grid-auto-rows and grid-auto-columns

By default, implicit tracks size to auto, which means they only grow as tall or wide as their content. You can control the size of implicit tracks using grid-auto-rows and grid-auto-columns.

Example: Sizing Implicit Tracks

/* Set a fixed height for all implicit rows */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: 150px;  /* Only 1 explicit row */
    grid-auto-rows: 150px;      /* All additional rows are also 150px */
}

/* Use minmax for flexible implicit rows */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: minmax(100px, auto);
    /* Implicit rows are at least 100px, grow with content */
}

/* Multiple values create a repeating pattern */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: 100px 200px;
    /* Implicit rows alternate: 100px, 200px, 100px, 200px... */
}

/* Controlling implicit columns */
.grid {
    display: grid;
    grid-template-rows: repeat(3, 1fr);
    grid-auto-columns: 200px;
    /* If items are placed beyond defined columns, new 200px columns appear */
}
Pro Tip: A common pattern is to define only columns and let rows be implicit: grid-template-columns: repeat(3, 1fr) combined with grid-auto-rows: minmax(150px, auto). This way, you do not need to know how many items there are -- the grid creates rows as needed, each at least 150px tall. This is the most flexible and maintainable approach for dynamic content like card grids.

grid-auto-flow: Controlling Item Placement Direction

The grid-auto-flow property controls how auto-placed items are inserted into the grid. By default, items fill in row by row (left to right, top to bottom). You can change this to column-by-column, and you can also enable a dense packing algorithm that fills in gaps left by larger items.

Example: grid-auto-flow Values

/* Default: items flow row by row */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-flow: row;
    /* Items: 1 2 3
              4 5 6
              7 8 9 */
}

/* Items flow column by column */
.grid {
    display: grid;
    grid-template-rows: repeat(3, 100px);
    grid-auto-flow: column;
    /* Items: 1 4 7
              2 5 8
              3 6 9 */
}

/* Dense packing: fills in gaps left by larger items */
.grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-auto-flow: row dense;
    /* If item 2 spans 3 columns and creates a gap,
       smaller items will backfill that gap */
}

/* Column-based dense packing */
.grid {
    display: grid;
    grid-template-rows: repeat(3, 100px);
    grid-auto-flow: column dense;
}
Accessibility Warning: Using grid-auto-flow: dense can cause items to appear in a different visual order than their source order. This creates a disconnect between what sighted users see and what screen readers announce, and keyboard navigation may jump around unexpectedly. Use dense only for purely visual content like image galleries where the order does not carry meaning.

Named Grid Lines

Instead of referring to grid lines by their numbers, you can give them names. Named lines make your CSS more readable and maintainable, especially in complex layouts. You define line names inside square brackets within your track definitions.

Example: Naming Grid Lines

/* Naming column lines */
.grid {
    display: grid;
    grid-template-columns: [sidebar-start] 250px [sidebar-end content-start] 1fr [content-end];
    grid-template-rows: [header-start] 80px [header-end body-start] 1fr [body-end footer-start] 60px [footer-end];
}

/* A line can have multiple names */
/* [sidebar-end content-start] -- this single line has two names */

/* Using named lines for placement (covered in next lesson) */
.header {
    grid-column: sidebar-start / content-end;
    grid-row: header-start / header-end;
}

.sidebar {
    grid-column: sidebar-start / sidebar-end;
    grid-row: body-start / body-end;
}

.content {
    grid-column: content-start / content-end;
    grid-row: body-start / body-end;
}

Example: Named Lines with repeat()

/* Named lines inside repeat() */
.grid {
    display: grid;
    grid-template-columns: repeat(3, [col-start] 1fr [col-end]);
    /* Creates: [col-start] 1fr [col-end col-start] 1fr [col-end col-start] 1fr [col-end] */
    /* Lines are referenced as col-start 1, col-start 2, col-start 3 */
}

/* Practical use: 12-column grid with named lines */
.grid {
    display: grid;
    grid-template-columns: repeat(12, [col] 1fr);
    gap: 20px;
}

The grid-template Shorthand

The grid-template shorthand combines grid-template-rows, grid-template-columns, and grid-template-areas into a single declaration. There are several syntax forms.

Example: grid-template Shorthand

/* Syntax: rows / columns */
.grid {
    display: grid;
    grid-template: 80px 1fr 60px / 250px 1fr;
    /* Rows: 80px, 1fr, 60px */
    /* Columns: 250px, 1fr */
}

/* Equivalent longhand: */
.grid {
    display: grid;
    grid-template-rows: 80px 1fr 60px;
    grid-template-columns: 250px 1fr;
}

/* With named areas (ASCII art layout): */
.grid {
    display: grid;
    grid-template:
        "header header"  80px
        "sidebar content" 1fr
        "footer footer"  60px
        / 250px 1fr;
    /* Each quoted string defines a row of named areas */
    /* The value after the string is the row height */
    /* The / separates rows from column definitions */
}

Example: grid-template-areas for Visual Layout

.grid {
    display: grid;
    grid-template-areas:
        "header  header  header"
        "sidebar content aside"
        "footer  footer  footer";
    grid-template-columns: 200px 1fr 180px;
    grid-template-rows: 80px 1fr 60px;
    gap: 10px;
}

/* Place items by area name */
.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.aside   { grid-area: aside; }
.footer  { grid-area: footer; }

/* Use a dot (.) for empty cells */
.grid {
    grid-template-areas:
        "header header header"
        ".      content aside"
        "footer footer  footer";
}
Note: Each row in grid-template-areas must have the same number of cells. Area names must form rectangles -- you cannot create L-shaped or T-shaped areas. Each area name must be used in a contiguous rectangular block. Use a period (.) or multiple periods (...) to denote empty cells.

The grid Shorthand

The grid shorthand is the most comprehensive Grid shorthand. It can set all of the following in a single declaration: grid-template-rows, grid-template-columns, grid-template-areas, grid-auto-rows, grid-auto-columns, and grid-auto-flow. However, because it resets all grid properties (including the implicit grid properties), many developers prefer using the more specific grid-template shorthand or individual properties for clarity.

Example: The grid Shorthand

/* Explicit grid: same as grid-template */
.grid {
    display: grid;
    grid: 80px 1fr 60px / 250px 1fr;
    /* grid-template-rows: 80px 1fr 60px */
    /* grid-template-columns: 250px 1fr */
}

/* Auto-flow rows with implicit row size */
.grid {
    display: grid;
    grid: auto-flow 150px / repeat(3, 1fr);
    /* grid-auto-flow: row */
    /* grid-auto-rows: 150px */
    /* grid-template-columns: repeat(3, 1fr) */
}

/* Auto-flow columns with implicit column size */
.grid {
    display: grid;
    grid: repeat(3, 100px) / auto-flow 200px;
    /* grid-template-rows: repeat(3, 100px) */
    /* grid-auto-flow: column */
    /* grid-auto-columns: 200px */
}

/* Dense packing */
.grid {
    display: grid;
    grid: auto-flow dense 100px / repeat(4, 1fr);
    /* grid-auto-flow: row dense */
    /* grid-auto-rows: 100px */
    /* grid-template-columns: repeat(4, 1fr) */
}

Practical Example: Responsive Card Grid

Let us build a responsive card grid that adapts to any screen size without media queries. This is one of the most common and powerful Grid patterns.

Example: Responsive Card Grid

<div class="card-grid">
    <div class="card">Card 1</div>
    <div class="card">Card 2</div>
    <div class="card">Card 3</div>
    <div class="card">Card 4</div>
    <div class="card">Card 5</div>
    <div class="card">Card 6</div>
</div>

CSS for Responsive Card Grid

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

.card {
    background: var(--bg-white);
    border: 1px solid var(--border-light);
    border-radius: 12px;
    padding: 24px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

/* How it works:
   - repeat(auto-fit, minmax(280px, 1fr)) creates responsive columns
   - On wide screens: 4+ columns
   - On tablets: 2-3 columns
   - On phones: 1 column
   - No media queries needed!
   - grid-auto-rows: minmax(200px, auto) ensures minimum card height
   - gap: 24px provides consistent spacing between all cards */

Practical Example: Full Page Layout

Here is a complete page layout using grid-template-areas for clarity and maintainability.

Example: Full Page Layout with Grid

<div class="page-layout">
    <header class="page-header">Header</header>
    <nav class="page-nav">Navigation</nav>
    <main class="page-main">Main Content</main>
    <aside class="page-sidebar">Sidebar</aside>
    <footer class="page-footer">Footer</footer>
</div>

CSS for Full Page Layout

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

.page-header  { grid-area: header; padding: 20px; background: var(--primary); color: white; }
.page-nav     { grid-area: nav; padding: 20px; background: var(--bg-light); }
.page-main    { grid-area: main; padding: 20px; }
.page-sidebar { grid-area: sidebar; padding: 20px; background: var(--bg-light); }
.page-footer  { grid-area: footer; padding: 20px; background: var(--text-dark); color: white; }

/* Responsive: stack everything on mobile */
@media (max-width: 768px) {
    .page-layout {
        grid-template-areas:
            "header"
            "nav"
            "main"
            "sidebar"
            "footer";
        grid-template-columns: 1fr;
        grid-template-rows: auto auto 1fr auto auto;
    }
}

Practical Example: Dashboard Grid

Dashboards often have complex layouts with panels of varying sizes. Grid makes this straightforward.

Example: Dashboard Grid Layout

.dashboard {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: minmax(120px, auto);
    gap: 16px;
    padding: 16px;
}

/* Stat cards across the top */
.stat-card {
    background: var(--bg-white);
    border-radius: 8px;
    padding: 20px;
}

/* Main chart spanning 3 columns and 2 rows */
.main-chart {
    grid-column: span 3;
    grid-row: span 2;
    background: var(--bg-white);
    border-radius: 8px;
    padding: 20px;
}

/* Side panel spanning 2 rows */
.side-panel {
    grid-row: span 2;
    background: var(--bg-white);
    border-radius: 8px;
    padding: 20px;
}

/* Full-width table at the bottom */
.data-table {
    grid-column: 1 / -1;  /* Spans all columns */
    background: var(--bg-white);
    border-radius: 8px;
    padding: 20px;
}

Alignment on the Grid Container

Grid provides alignment properties on the container that control how the entire grid tracks are distributed within the container, and how items are aligned within their cells. These are the container-level alignment properties.

Example: Container Alignment Properties

/* justify-content: aligns the GRID TRACKS along the inline (row) axis */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 200px); /* Fixed-size columns */
    justify-content: center;    /* Centers all column tracks */
    /* Other values: start, end, space-between, space-around, space-evenly */
}

/* align-content: aligns the GRID TRACKS along the block (column) axis */
.grid {
    display: grid;
    height: 600px;
    grid-template-rows: 100px 100px 100px; /* Fixed-size rows */
    align-content: center;     /* Centers all row tracks vertically */
}

/* justify-items: aligns grid ITEMS within their cells horizontally */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    justify-items: center;     /* Centers all items in their cells */
    /* Other values: start, end, stretch (default) */
}

/* align-items: aligns grid ITEMS within their cells vertically */
.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: 200px;
    align-items: center;       /* Vertically centers items in their cells */
}

/* place-items shorthand: align-items / justify-items */
.grid {
    display: grid;
    place-items: center;       /* Centers items both ways in their cells */
}

/* place-content shorthand: align-content / justify-content */
.grid {
    display: grid;
    place-content: center;     /* Centers the entire grid in the container */
}
Pro Tip: The simplest way to perfectly center a single element on the page is: display: grid; place-items: center; min-height: 100vh;. This is even shorter than the Flexbox centering trick and works beautifully.

Common Grid Container Patterns Summary

Here is a quick reference of the most useful grid container patterns you will use regularly.

Quick Reference: Common Patterns

/* 1. Responsive card grid (no media queries) */
.cards { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }

/* 2. Holy grail layout */
.page { grid-template: auto 1fr auto / 200px 1fr 200px; }

/* 3. 12-column framework */
.framework { grid-template-columns: repeat(12, 1fr); gap: 20px; }

/* 4. Sidebar layout */
.sidebar-layout { grid-template-columns: 300px 1fr; }

/* 5. Equal height rows */
.equal-rows { grid-auto-rows: 1fr; }

/* 6. Masonry-like (dense packing) */
.masonry { grid-auto-flow: dense; grid-template-columns: repeat(3, 1fr); }

/* 7. Centered content */
.centered { display: grid; place-items: center; min-height: 100vh; }

/* 8. Full-bleed layout */
.full-bleed {
    grid-template-columns: 1fr min(65ch, 100%) 1fr;
    /* Center column is readable width, sides are gutters */
}

Practice Exercise

Build a responsive photo gallery using CSS Grid. Create a grid container with repeat(auto-fit, minmax(200px, 1fr)) for the columns and grid-auto-rows: 200px for consistent row heights. Add a gap of 16px. Place at least 8 items in the grid and verify that the layout automatically adjusts from 4 columns on desktop to 2 on tablet and 1 on mobile without any media queries. Next, convert the gallery to a dashboard layout using grid-template-areas with a header spanning the full width, a sidebar on the left, main content in the center, and a footer spanning the full width. Define explicit row heights using grid-template-rows and make the main content row use 1fr so it fills the remaining space. Finally, add a responsive breakpoint at 768px that stacks all areas into a single column using updated grid-template-areas and grid-template-columns: 1fr.