CSS Grid and Modern Layouts That Actually Ship

Ship real page layouts with CSS Grid, Flexbox, gap, minmax and container queries — practical patterns for marketing sites and client builds without a design-system team.

Share LinkedIn X Facebook Email

Modern CSS finally lets us describe layout the way designers think: rows, columns, alignment and responsive behaviour without a forest of wrapper divs or a JavaScript measuring stick. Grid and Flexbox are not rivals. They are different tools. This guide focuses on patterns you can paste into a real client site today — marketing pages, card grids, dashboards and “this must look expensive on mobile” portfolios.

If you have ever fought float clearing or twelve nested Bootstrap rows, this is the reset button.

Abstract browser window with floating CSS layout cards in purple and cyan
Layout systems should flex with content — not fight it.

Flexbox vs Grid in one minute

  • Flexbox — one-dimensional. A row of nav links, a toolbar, a media object (image + text), equal-height cards in a single row that wrap.
  • Grid — two-dimensional. Full page shells, card magazines, dashboards with sidebars, “feature + side stack” heroes.

Use Flex inside Grid cells often. That is normal and healthy.

A page shell you can reuse

.page {
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  grid-template-columns: minmax(0, 1fr);
}
.page__main {
  width: min(100% - 2rem, 72rem);
  margin-inline: auto;
  padding-block: 2rem;
}

minmax(0, 1fr) avoids the classic overflow bug where grid children refuse to shrink below content size. Remember it; it saves evenings.

Responsive card grids without breakpoints first

The auto-fit + minmax pattern is the workhorse of modern marketing sites:

.card-grid {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}
.card {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1.25rem;
  border-radius: 1rem;
  background: #0f172a;
  border: 1px solid rgb(148 163 184 / 0.16);
}
.card__actions {
  margin-top: auto; /* pin buttons to bottom */
}

Cards reflow from one to many columns as space allows. You still add a couple of media queries for type scale and padding, but the column math is automatic.

Abstract floating CSS cards over a soft grid background
Gap, minmax and auto-fit replace most float hacks from a decade ago.

Named areas for readable templates

For product pages and dashboards, named grid areas keep HTML honest:

.product {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: 1.2fr 1fr;
  grid-template-areas:
    "gallery summary"
    "gallery buy"
    "details details";
}
.product__gallery { grid-area: gallery; }
.product__summary { grid-area: summary; }
.product__buy { grid-area: buy; }
.product__details { grid-area: details; }

@media (max-width: 800px) {
  .product {
    grid-template-columns: 1fr;
    grid-template-areas:
      "gallery"
      "summary"
      "buy"
      "details";
  }
}

When a designer moves “buy box” under the gallery on mobile, you change the areas map — not a pile of order hacks (though order still has niche uses).

Alignment, gap and logical properties

Prefer gap over margin tricks between siblings. Prefer logical properties when you care about internationalisation or simply cleaner code:

.stack {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.cluster {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  align-items: center;
}
.media {
  display: flex;
  gap: 1rem;
  align-items: flex-start;
}
.media > img {
  flex: 0 0 4.5rem;
  border-radius: 0.75rem;
}

margin-inline: auto, padding-block, inset — small upgrades that make intent obvious.

Container queries: components that respond to their parent

Media queries look at the viewport. Container queries look at the space a component actually has — perfect for sidebars and modular blocks in WordPress or custom PHP templates.

.card-wrap {
  container-type: inline-size;
  container-name: card;
}
@container card (min-width: 28rem) {
  .card {
    display: grid;
    grid-template-columns: 8rem 1fr;
    align-items: center;
  }
}

Browser support is strong in current evergreen browsers. Progressive enhancement still applies: design a good single-column default first.

Subgrid (when you need aligned rows across cards)

If card titles, prices and buttons must line up across a row, subgrid helps when supported:

.pricing {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}
.plan {
  display: grid;
  grid-row: span 4;
  grid-template-rows: subgrid;
  gap: 0.75rem;
}

Check support for your audience; provide a flex fallback if you still need older browsers for a specific client.

Fluid type and spacing (without magic numbers everywhere)

:root {
  --step-0: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
  --step-1: clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
  --step-2: clamp(1.5rem, 1.25rem + 1.1vw, 2rem);
  --space-s: clamp(0.75rem, 0.6rem + 0.5vw, 1rem);
  --space-m: clamp(1.25rem, 1rem + 1vw, 2rem);
}
h1 { font-size: var(--step-2); line-height: 1.15; }
p  { font-size: var(--step-0); line-height: 1.6; }
section { padding-block: var(--space-m); }

Fluid scales reduce breakpoint thrash. Pair with a sensible max width for long text (65ch is a friendly measure).

Common layout bugs (and fixes)

  • Horizontal scroll on mobile — usually a child with fixed width or 100vw ignoring scrollbars. Prefer 100% and minmax(0, 1fr).
  • Images blowing out grid cellsimg { max-width: 100%; height: auto; display: block; }
  • Equal height columns with mystery gaps — use Grid/Flex gap, not margin-bottom on the last child hacks.
  • Sticky sidebar failing — parent overflow hidden kills sticky; check ancestors.
.layout {
  display: grid;
  grid-template-columns: 1fr 18rem;
  gap: 2rem;
  align-items: start;
}
.layout__side {
  position: sticky;
  top: 1rem;
}

Accessibility is part of layout

  • Do not rely on colour alone for state
  • Focus styles must remain visible when you restyle buttons
  • Order in the DOM should still make sense when CSS is limited — visual order can confuse keyboard users if abused
  • Respect prefers-reduced-motion for layout transitions
@media (prefers-reduced-motion: reduce) {
  * {
    scroll-behavior: auto !important;
    transition: none !important;
  }
}

A shipping checklist for client layouts

  1. Mobile single column is excellent before you invent the desktop grid
  2. Use gap consistently (design tokens help)
  3. Test real content lengths — English marketing copy and German compound words both matter
  4. Check 320px width and a large desktop zoom
  5. Verify forms and sticky CTAs do not cover content on iOS Safari

Holy albatross and full-bleed sections

Marketing sites often need a narrow text column with the occasional edge-to-edge band (colour, image, testimonial). A simple pattern:

.content {
  --content: 65ch;
  --gutter: 1.25rem;
  width: min(100% - (var(--gutter) * 2), var(--content));
  margin-inline: auto;
}
.full-bleed {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}
.full-bleed > .content {
  /* keep inner copy readable while background spans the viewport */
}

If 100vw introduces a horizontal scrollbar on some browsers, prefer a grid “breakout” technique with full-width tracks — but for many brochure sites the classic full-bleed trick is fine once you test Chrome, Safari and Firefox.

Design tokens without a design-system team

You do not need Style Dictionary on day one. A short :root block shared across the site already prevents “seventeen blues”:

:root {
  --color-bg: #070b14;
  --color-surface: #111827;
  --color-text: #e5e7eb;
  --color-muted: #94a3b8;
  --color-accent: #38bdf8;
  --radius: 0.9rem;
  --shadow: 0 18px 40px rgb(0 0 0 / 0.35);
  --font-sans: "Plus Jakarta Sans", system-ui, sans-serif;
}
body {
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
}
.button-primary {
  background: var(--color-accent);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

Name tokens by role (--color-accent) not by temporary paint (--blue-3). When a rebrand lands, you change tokens once.

Performance notes that affect layout CSS

  • Avoid layout thrash from reading geometry in JS during scroll; prefer CSS sticky and scroll-snap when possible
  • Large box-shadows and heavy blur are not free on low-end phones — use sparingly
  • Prefer modern image formats and explicit width/height to reduce cumulative layout shift
  • Critical CSS for above-the-fold layout is still useful on marketing homes; do not inline your entire design system

Layout quality and performance are the same product to a visitor: if the page jumps or janks, the brand feels cheaper no matter how nice the Figma file looked.

Working with WordPress / PHP templates

Keep structural classes stable in PHP templates and put the clever layout in CSS. That way editors can change copy without breaking the grid. Useful habits:

  • One outer .section with inner .section__inner width constraints
  • Modifiers like .section--tight or .section--band instead of one-off inline styles
  • Block patterns (Gutenberg) that map to your grid classes so marketing can self-serve
<section class="section section--band">
  <div class="section__inner card-grid">
    <!-- cards -->
  </div>
</section>

Beautiful layout is not more divs. It is clearer relationships between regions.

Want a site layout that feels premium on every screen?

I design and build modern marketing sites and WordPress themes with clean CSS — Grid, Flex, performance and accessibility included.

Talk about your project →

Share this guide LinkedIn X Email
Need a site built? Free call · UK studio Book a free call