Modern CSS Layout — Grid, Flexbox, Container Query

When to Use Grid vs. Flexbox

Flexbox and Grid are both CSS layout tools, but each has different strengths. Flexbox excels at one-dimensional (horizontal or vertical) layouts, while Grid excels at two-dimensional (horizontal + vertical) layouts.

Using a bookshelf analogy, Flexbox is like arranging books side by side on a single shelf, while Grid is like designing an entire grid-style storage unit.

CriteriaFlexboxGrid
Dimension1D (row or column)2D (row + column)
Best forNavigation, card rows, alignmentPage layouts, dashboards
Item sizingContent-basedTrack-based
AlignmentSingle axisBoth axes

Flexbox Core Patterns

Basic Layout

/* Navigation bar — logo left, menu right */
.navbar {
  display: flex;
  justify-content: space-between; /* Push to opposite ends */
  align-items: center;            /* Vertically center */
  padding: 1rem 2rem;
  gap: 1rem;                      /* Spacing between items */
}

.nav-menu {
  display: flex;
  gap: 1.5rem;                    /* Spacing between menu items */
  list-style: none;
}

/* Card row — equal distribution + wrapping */
.card-row {
  display: flex;
  flex-wrap: wrap;                /* Wrap when space is insufficient */
  gap: 1rem;
}

.card {
  flex: 1 1 300px;               /* Min 300px, remaining space distributed equally */
  /* flex-grow: 1 → fills remaining space */
  /* flex-shrink: 1 → shrinks when space is tight */
  /* flex-basis: 300px → base width */
}

Common Alignment Patterns

/* Perfect centering (the cleanest approach) */
.center-everything {
  display: flex;
  justify-content: center;  /* Horizontal center */
  align-items: center;      /* Vertical center */
  min-height: 100vh;
}

/* Sticky footer at the bottom */
.page-layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.page-content {
  flex: 1;                  /* Takes all remaining space → pushes footer down */
}

.page-footer {
  /* flex: 0 maintains natural height */
}

/* Right-align only the last item */
.toolbar {
  display: flex;
  gap: 0.5rem;
}

.toolbar .spacer {
  margin-left: auto;       /* Push with auto margin */
}

CSS Grid Core Patterns

Page Layout

/* Classic sidebar + main content layout */
.page-grid {
  display: grid;
  grid-template-columns: 250px 1fr;    /* Sidebar 250px, rest for main */
  grid-template-rows: auto 1fr auto;   /* Header, content, footer */
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
  gap: 1rem;
}

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

/* Responsive — hide sidebar on mobile */
@media (max-width: 768px) {
  .page-grid {
    grid-template-columns: 1fr;
    grid-template-areas:
      "header"
      "main"
      "footer";
  }
  .sidebar { display: none; }
}

Auto-Responsive Card Grid

/* auto-fill + minmax — responsive without media queries */
.auto-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.5rem;
  padding: 1rem;
}

/* auto-fill vs auto-fit difference */
/* auto-fill: empty tracks still take up space (maintains alignment) */
/* auto-fit:  removes empty tracks (items fill remaining space) */

.auto-grid-fit {
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  /* When there are few items → items stretch to fill */
}

Dashboard Layout

/* Dashboard with widgets of varying sizes */
.dashboard {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 200px;       /* Each row height 200px */
  gap: 1rem;
}

/* Size specifications per widget */
.widget-wide {
  grid-column: span 2;         /* 2 columns wide */
}

.widget-tall {
  grid-row: span 2;            /* 2 rows tall */
}

.widget-large {
  grid-column: span 2;
  grid-row: span 2;            /* 2 columns x 2 rows */
}

/* Responsive dashboard */
@media (max-width: 1024px) {
  .dashboard {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 640px) {
  .dashboard {
    grid-template-columns: 1fr;
  }
  .widget-wide,
  .widget-tall,
  .widget-large {
    grid-column: span 1;
    grid-row: span 1;
  }
}

Container Query: Component-Level Responsiveness

Traditional media queries are based on the viewport (browser window), but Container Queries apply styles based on the parent container. The same card component can automatically switch from vertical layout in a sidebar to horizontal layout in the main area.

/* 1. Declare container */
.card-container {
  container-type: inline-size;  /* Enable container query based on width */
  container-name: card;         /* Optional name assignment */
}

/* 2. Default card style (narrow container) */
.card {
  display: flex;
  flex-direction: column;       /* Default: vertical layout */
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.card-image {
  aspect-ratio: 16 / 9;
  object-fit: cover;
  width: 100%;
}

.card-body {
  padding: 1rem;
}

/* 3. Container query — switch to horizontal layout when container is 500px or wider */
@container card (min-width: 500px) {
  .card {
    flex-direction: row;        /* Switch to horizontal layout */
  }

  .card-image {
    width: 200px;
    aspect-ratio: 1;            /* Change to square */
  }
}

/* 4. Show additional info in wider containers */
@container card (min-width: 700px) {
  .card-meta {
    display: flex;              /* Show meta information */
    gap: 0.5rem;
  }

  .card-description {
    -webkit-line-clamp: 3;     /* Show up to 3 lines */
  }
}

Subgrid: Inheriting Parent Grid Lines

Subgrid allows child elements to inherit the parent Grid’s track lines, ensuring alignment consistency even in nested grids.

/* Parent grid */
.product-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

/* Card aligns internal elements (image, title, price) across cards */
.product-card {
  display: grid;
  grid-template-rows: subgrid; /* Inherit parent's row tracks */
  grid-row: span 3;           /* Occupy 3 rows */
  gap: 0.5rem;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  overflow: hidden;
}

/* Result: image height, title height, and price height align across all cards */
.product-image { /* First row */ }
.product-title { /* Second row — aligned with other cards */ }
.product-price { /* Third row — aligned with other cards */ }

Layout Selection Guide

ScenarioRecommended Approach
Arrange items in a single rowFlexbox
Place items in an even gridGrid + auto-fill
Full page structure (header, sidebar, main)Grid + grid-template-areas
Vertical centeringFlexbox align-items: center
Responsive card layoutGrid + minmax()
Component-level responsivenessContainer Query
Consistent alignment in nested gridsSubgrid

Practical Tips

  • Mix Grid and Flexbox: Use Grid for the overall page layout and Flexbox for component-level arrangements. This is the natural combination.
  • Try auto-fill + minmax first: You can create responsive layouts without any media queries.
  • Make components independent with Container Query: When components respond to their parent’s size instead of the viewport, they work well no matter where you place them.
  • Use the gap property generously: Using margin for spacing requires special handling for first/last items, but gap only applies between items.
  • Visualize layouts with grid-template-areas: Expressing layouts like ASCII art greatly improves code readability.
  • Use DevTools Grid/Flexbox inspectors: Both Chrome and Firefox let you visually inspect Grid lines and Flexbox structures.

Was this article helpful?