DevToolBoxFREE
BlogAdvertise

CSS Grid Mastery: Complete Guide with Real-World Examples 2026

14 min readby DevToolBox

CSS Grid is the most powerful layout system ever added to CSS. While Flexbox handles one-dimensional layouts (rows OR columns), Grid handles two-dimensional layouts (rows AND columns simultaneously). In 2026, Grid Level 2 features like subgrid are fully supported across all major browsers, unlocking alignment patterns previously impossible in CSS.

Grid Basics: Columns, Rows, and Placement

Grid creates a two-dimensional coordinate system. Items are placed by specifying their position in the grid using line numbers, span values, or named areas.

/* CSS Grid fundamentals */
.grid-container {
    display: grid;

    /* Define columns */
    grid-template-columns: 200px 1fr 1fr;        /* fixed + flexible */
    grid-template-columns: repeat(3, 1fr);        /* 3 equal columns */
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); /* responsive */

    /* Define rows */
    grid-template-rows: auto 1fr auto;            /* header, content, footer */

    /* Gaps */
    gap: 1rem;            /* both row and column gap */
    row-gap: 0.5rem;
    column-gap: 1.5rem;

    /* Alignment */
    justify-items: stretch;   /* horizontal alignment of cells */
    align-items: start;       /* vertical alignment of cells */
    justify-content: center;  /* horizontal alignment of the grid */
    align-content: center;    /* vertical alignment of the grid */
}

/* Place items */
.item {
    grid-column: 1 / 3;      /* span from column 1 to 3 */
    grid-row: 1 / 2;
    grid-column: span 2;      /* span 2 columns */
    grid-area: 1 / 1 / 3 / 3; /* row-start / col-start / row-end / col-end */
}

grid-template-areas: Visual Layout Definition

grid-template-areas lets you define the layout visually using ASCII art. Each string represents a row; repeated names create spanning areas. A dot (.) creates an empty cell.

/* grid-template-areas: name regions visually */
.page-layout {
    display: grid;
    grid-template-areas:
        "header  header  header"
        "sidebar main    main"
        "sidebar aside   aside"
        "footer  footer  footer";
    grid-template-columns: 250px 1fr 300px;
    grid-template-rows: 60px 1fr auto 80px;
    min-height: 100vh;
    gap: 1rem;
}

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

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

auto-fill vs auto-fit: Responsive Without Media Queries

auto-fill and auto-fit with minmax() create responsive grids without any media queries. The difference: auto-fill preserves empty tracks; auto-fit collapses them.

/* auto-fill vs auto-fit — the most important Grid distinction */

/* auto-fill: creates as many columns as possible, even if empty */
.gallery-fill {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 1rem;
}
/* Result: always fills the row with tracks, even with empty ones */

/* auto-fit: collapses empty tracks, stretches filled items */
.gallery-fit {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1rem;
}
/* Result: items expand to fill available space */

/* Practical: responsive card grid (no media queries!) */
.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
    gap: 1.5rem;
}
/* min(300px, 100%) prevents overflow on very small screens */

minmax() and Intrinsic Sizing

minmax(min, max) sets the minimum and maximum size of a grid track. Combined with auto-fill and min(), it creates layouts that adapt naturally to any content and viewport.

/* minmax() — set minimum and maximum track size */

/* Card layout: minimum 250px, maximum 1fr */
.cards {
    display: grid;
    grid-template-columns: repeat(3, minmax(250px, 1fr));
}

/* Intrinsic sizing with min-content / max-content */
.table-grid {
    display: grid;
    grid-template-columns: max-content 1fr max-content;
    /* First and last columns size to content, middle fills rest */
}

/* fit-content() — like minmax(auto, max-content) but capped */
.nav {
    display: grid;
    grid-template-columns: fit-content(200px) 1fr fit-content(200px);
}

/* Dense packing: fill holes left by spanning items */
.masonry-like {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    grid-auto-rows: 150px;
    grid-auto-flow: dense; /* fills gaps automatically */
}

.tall-item {
    grid-row: span 2; /* takes 2 rows */
}
.wide-item {
    grid-column: span 2; /* takes 2 columns */
}

Subgrid: CSS Grid Level 2 (2026)

Subgrid allows a nested grid to participate in the parent grid's track definition. This solves the "card alignment" problem where content inside separate cards couldn't align across cards.

/* Subgrid — CSS Grid Level 2, fully supported in 2026 */
/* Allows nested grids to align to parent grid lines */

/* The problem subgrid solves: */
.card-container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}

/* Without subgrid: each card's internal layout is independent */
/* Card titles and content don't align across cards */

/* With subgrid: */
.card {
    display: grid;
    grid-row: span 3; /* card spans 3 implicit rows */
    grid-template-rows: subgrid; /* inherits parent row tracks */
}
/* Now all cards share the same row grid — titles align! */

.card .card-header { grid-row: 1; }
.card .card-body   { grid-row: 2; }
.card .card-footer { grid-row: 3; }

/* Subgrid for columns too */
.sidebar-layout {
    display: grid;
    grid-template-columns: 1fr 3fr;
}

.main-content {
    display: grid;
    grid-column: 2;
    grid-template-columns: subgrid; /* aligns to parent columns */
}

/* Browser support: Chrome 117+, Firefox 71+, Safari 16+ — all major browsers */

Grid Animations and Transitions

Modern browsers support transitioning grid-template-columns and grid-template-rows. This enables smooth sidebar expansions and CSS-only accordion animations.

/* Grid transitions and animation */

/* Animate grid column width on hover */
.sidebar-layout {
    display: grid;
    grid-template-columns: 250px 1fr;
    transition: grid-template-columns 0.3s ease;
}

.sidebar-layout:has(.sidebar:hover) {
    grid-template-columns: 350px 1fr; /* expand on hover */
}

/* CSS-only accordion with Grid */
.accordion-item {
    display: grid;
    grid-template-rows: auto 0fr; /* collapsed: 0 fraction */
    transition: grid-template-rows 0.3s ease;
    overflow: hidden;
}

.accordion-item.open {
    grid-template-rows: auto 1fr; /* expanded: 1 fraction */
}

.accordion-content {
    min-height: 0; /* required for 0fr to work */
}

CSS Grid vs Flexbox: When to Use Which

ScenarioCSS GridFlexbox
Page layout (header/footer/sidebar)PerfectAwkward
Navigation bar itemsWorksPerfect
Card grid (equal columns)PerfectNeeds JS for equal height
Centering contentEasy (place-items)Easy (align/justify)
Vertical list of itemsWorksPerfect
Complex form layoutPerfectComplex
Responsive without breakpointsauto-fill/fitLimited

Best Practices

  • Use Grid for page-level layouts (header/sidebar/main/footer). Use Flexbox for component-level layouts (navigation items, button groups, card content).
  • Prefer repeat(auto-fill, minmax()) over fixed column counts for responsive card grids — eliminates most breakpoints.
  • Name your grid areas with grid-template-areas for complex layouts. Names serve as self-documenting CSS.
  • Use subgrid whenever you need items in separate grid children to align — card footers, table cells, price rows.
  • gap is now universally supported (replacing grid-gap). Use it instead of margins for grid spacing.

Frequently Asked Questions

When should I use CSS Grid vs Flexbox?

CSS Grid excels at two-dimensional layouts where you control both rows and columns. Flexbox is better for one-dimensional layouts: a row of navigation links, a vertical stack of items, or content within a card. Many designs use Grid for the macro layout and Flexbox for components within grid cells.

What is the difference between grid-template-columns and grid-auto-columns?

grid-template-columns defines explicit columns you specify upfront. grid-auto-columns sets the size of implicitly created columns (when items overflow the defined grid). Similarly, grid-template-rows vs grid-auto-rows handles explicit vs implicit rows.

How does subgrid differ from nested grid?

A nested grid creates its own independent grid coordinate system. Subgrid inherits the parent's track definitions, so child items align to the parent grid's lines. This is crucial for multi-column card layouts where titles, content, and footers need to align across separate cards.

What does grid-auto-flow: dense do?

By default, Grid places items in order, leaving holes when a large item can't fit. grid-auto-flow: dense makes the browser try to fill those holes with later items. This is useful for image galleries with items of varying sizes, creating a tighter masonry-like layout.

Can I mix Grid and Flexbox?

Absolutely. Grid and Flexbox work at different levels. A typical pattern: CSS Grid for the page layout (header, sidebar, main), Flexbox for the header navigation (logo + links + CTA), and Grid again for a card section inside main. Mixing them is normal and encouraged.

Was this helpful?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Partner Picks

Sponsor this article

Place your product next to this developer topic with tracked clicks.

Ask about article sponsorship

This site uses cookies for analytics and to display ads. By continuing to browse, you agree. Privacy Policy