21.1 C
New York
Sunday, June 28, 2026

CSS Container Queries + Subgrid: The Structure Trilogy That is Now in Each Browser


CSS structure has undergone three distinct evolutionary leaps over the previous eight years. Collectively, CSS Grid, Subgrid, and Container Queries exchange JavaScript-dependent structure workarounds with pure CSS. This tutorial walks by means of constructing a responsive card grid element that makes use of all three options, first in plain HTML and CSS, then built-in right into a React element.

Desk of Contents

The Structure Trilogy Is Lastly Full

CSS structure has undergone three distinct evolutionary leaps over the previous eight years. CSS Grid landed in browsers in 2017, giving builders a real two-dimensional structure engine for the primary time. Subgrid adopted, with Firefox delivery assist in model 71 (December 2019) and Chrome and Edge including assist in model 117 (August 2023). As soon as all main browsers supported the function, Subgrid reached Baseline Newly Obtainable standing in late 2023. Container queries, together with container-type, @container guidelines, and the related cqw/cqh/cqi/cqb models, reached full cross-browser stability (Chrome 105, Firefox 110, Safari 16, Edge 105), finishing what can fairly be known as the structure trilogy.

Collectively, these three options exchange JavaScript-dependent structure workarounds with pure CSS. Grid handles macro structure. Subgrid enforces alignment consistency throughout sibling elements. Container queries make particular person elements attentive to their very own container moderately than the viewport. Layouts that beforehand demanded JavaScript resize observers or fragile media question workarounds can now stay completely in stylesheets.

Grid handles macro structure. Subgrid enforces alignment consistency throughout sibling elements. Container queries make particular person elements attentive to their very own container moderately than the viewport.

This tutorial walks by means of constructing a responsive card grid element that makes use of all three options, first in plain HTML and CSS, then built-in right into a React element. Intermediate CSS data is assumed. Grid fundamentals will get a quick refresher, not a ground-up introduction.

Fast Refresher: The place Every Function Matches

CSS Grid: The Macro Structure Engine

CSS Grid supplies two-dimensional management over web page and part structure by means of properties like grid-template-columns, grid-template-rows, and hole. It handles the outermost structural considerations: what number of columns a structure makes use of, how tracks dimension themselves relative to out there area, and the way gadgets stream into implicit or express tracks. Grid is the inspiration the opposite two options construct on.

Subgrid: Aligning Youngsters to a Mum or dad Grid

A well-recognized downside exhibits up the second you nest grids: playing cards in a row with totally different content material lengths find yourself with misaligned headings, physique sections, and footers. By default, a grid merchandise that’s itself a grid container sizes its inner tracks independently of the guardian. Subgrid fixes this. The important thing syntax is grid-template-columns: subgrid and grid-template-rows: subgrid, which inform the kid to undertake the guardian’s column or row tracks moderately than defining its personal.

Container Queries: Element-Degree Responsiveness

Media queries reply to the viewport. Container queries reply to the dimensions of a guardian container, making elements actually self-contained and context-aware. The core syntax entails declaring a containment context with container-type (usually inline-size), optionally naming it with container-name, after which writing @container guidelines that fireplace based mostly on the container’s dimensions. Container question models (cqw, cqh, cqi, cqb) enable fluid sizing relative to the container moderately than the viewport.

Browser Help in 2025: The Inexperienced Board

Present Help Matrix

FunctionChromeFirefoxSafariEdge
CSS Grid57+52+10.1+16+
Subgrid117+71+16+117+
Container Queries105+110+16+105+
CQ Models (cqw/cqh)105+110+16+105+

Notice that Firefox shipped Subgrid considerably earlier (v71, December 2019) than Chrome and Edge (v117, August 2023). The “Baseline 2023” designation displays the purpose when all main engines supported the function, not any single engine’s ship date.

For any undertaking focusing on the final two variations of main browsers, no polyfills are wanted for any of those options. The complete trilogy is inexperienced throughout the board.

For groups that also want a progressive enhancement security web, @helps requires no exterior dependencies and degrades silently in unsupporting browsers. Wrapping subgrid or container question guidelines in @helps (grid-template-rows: subgrid) or @helps (container-type: inline-size) blocks ensures swish fallback with out counting on third-party polyfill scripts. Notice that @helps detects particular person function availability however can’t detect interplay points between mixed options. Check the mixed implementation in your goal browsers.

Constructing the Trilogy Card Grid: Step by Step

Step 1: Setting Up the Outer Grid

The outer grid handles macro structure, figuring out what number of playing cards seem per row and the way they reflow because the viewport adjustments. The repeat(auto-fill, minmax(280px, 1fr)) sample creates responsive columns with none media queries: columns are at the very least 280px broad, broaden to fill out there area, and the browser creates as many as will match.

Exchange image-1.jpg, image-2.jpg, and image-3.jpg with actual picture paths or a placeholder service resembling https://picsum.pictures/400/200.

<div class="card-grid">
  <article class="card">
    <div class="card-inner">
      <img src="image-1.jpg" alt="Function one" />
      <h3>Card Heading One</h3>
      <p>Quick physique textual content for this card.</p>
      <a href="#" class="cta">Study Extra</a> 
    </div>
  </article>

  <article class="card">
    <div class="card-inner">
      <img src="image-2.jpg" alt="Function two" />
      <h3>Card Heading Two</h3>
      <p>This card has considerably extra physique textual content to show alignment challenges throughout siblings.</p>
      <a href="#" class="cta">Study Extra</a>
    </div>
  </article>

  <article class="card">
    <div class="card-inner">
      <img src="image-3.jpg" alt="Function three" />
      <h3>Card Heading Three</h3>
      <p>Medium size textual content right here.</p>
      <a href="#" class="cta">Study Extra</a>
    </div>
  </article>
</div>
.card-grid {
  --card-rows: 4;
  show: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  hole: 1.5rem;
  padding: 1.5rem;
}

This offers a totally responsive macro structure with implicit monitor creation. Playing cards reflow from three columns on broad screens all the way down to a single column on slender ones, all with out breakpoints.

Step 2: Including Subgrid for Constant Card Internals

With out subgrid, every card’s inner rows are sized independently. A card with a brief heading and a card with a two-line heading may have their physique textual content and CTAs at totally different vertical positions. This misalignment is without doubt one of the most typical visible bugs in card-based layouts.

The repair: make every card a grid merchandise that spans an outlined variety of rows, then apply grid-template-rows: subgrid so its inner components lock to the guardian grid’s row tracks.

Necessary: container-type should not be utilized to the identical component that makes use of grid-template-rows: subgrid. Making use of container-type to a subgrid participant establishes dimension containment on that component, which prevents the guardian grid’s row monitor sizing from propagating into the kid, breaking subgrid alignment completely. As a substitute, place container-type on an interior little one component (.card-inner on this tutorial) to protect subgrid monitor inheritance. This architectural alternative is mirrored within the HTML construction above.

.card {
  show: grid;
  grid-row: span var(--card-rows);
  grid-template-rows: subgrid;
  border: 1px stable var(--card-border-color, #e0e0e0);
  border-radius: 8px;
  overflow: hidden;
}

.card-inner {
  show: grid;
  grid-row: 1 / -1;
  grid-template-rows: subgrid;
}

.card img {
  width: 100%;
  top: 200px;
  object-fit: cowl;
}

.card h3 {
  padding: 0 1rem;
  margin: 0;
}

.card p {
  padding: 0 1rem;
  margin: 0;
}

.card .cta {
  padding: 0 1rem 1rem;
  align-self: finish;
}

The grid-row: span var(--card-rows) declaration is essential. It tells the guardian grid that every card occupies 4 row tracks (picture, heading, physique, CTA). The --card-rows customized property lives on .card-grid so the span worth and HTML little one rely keep coupled in a single place. Then grid-template-rows: subgrid distributes the cardboard’s kids throughout these 4 parent-defined tracks. All sibling playing cards share the identical monitor sizing, so headings align with headings, physique textual content with physique textual content, and CTAs sit at a constant baseline no matter content material size. The span worth should match the variety of direct grid kids in every card. Playing cards with totally different little one counts will misalign.

Notice: When grid-template-rows: subgrid is energetic, the kid component’s row-gap is ignored. Spacing between card sections is managed by the guardian .card-grid hole worth. Use padding on particular person card kids in case you want totally different inner spacing.

Step 3: Making Playing cards Container-Question-Conscious

With the macro grid and subgrid alignment in place, container queries add the ultimate layer: every card can adapt its personal inner structure based mostly on how a lot area it truly occupies. The container-type declaration goes on .card-inner, not on .card itself, to keep away from breaking subgrid monitor inheritance.

.card-inner {
  container-type: inline-size;
  container-name: card;
}

@container card (min-inline-size: 480px) {
  .card-inner {
    grid-template-columns: 200px 1fr;
  }

  .card-inner img {
    top: 100%;
    grid-row: 1 / -1;
  }

  .card-inner h3 {
    font-size: clamp(1.25rem, 3cqi, 2rem);
  }
}

@container card (max-inline-size: 479.999px) {
  .card-inner h3 {
    font-size: clamp(1rem, 5cqi, 1.5rem);
  }
}

When a card’s inline dimension exceeds 480px (which occurs when the outer grid provides it sufficient room), the structure switches from a vertical stack to a horizontal association with the picture on the left and textual content on the best. The cqi unit sizes the heading relative to the cardboard container’s inline dimension, protecting typography proportional. Each the slender and broad guidelines use clamp() to make sure headings have a minimal flooring and most ceiling, stopping accessibility points at excessive container sizes. The max-inline-size: 479.999px worth ensures there isn’t a fractional-pixel hole between the 2 breakpoints.

Utilizing container-name: card makes the question express about which containment context it targets. This turns into important when containers are nested, stopping queries from unintentionally matching an ancestor container.

Step 4: The Mixed Consequence

Right here is the whole, unified implementation combining all three options:

<div class="card-grid">
  <article class="card">
    <div class="card-inner">
      <img src="image-1.jpg" alt="Function one" />
      <h3>Card Heading One</h3>
      <p>Quick physique textual content for this card.</p>
      <a href="#" class="cta">Study Extra</a>
    </div>
  </article>

  <article class="card">
    <div class="card-inner">
      <img src="image-2.jpg" alt="Function two" />
      <h3>Card Heading Two</h3>
      <p>This card has considerably extra physique textual content to show alignment challenges throughout siblings.</p>
      <a href="#" class="cta">Study Extra</a>
    </div>
  </article>

  <article class="card">
    <div class="card-inner">
      <img src="image-3.jpg" alt="Function three" />
      <h3>Card Heading Three</h3>
      <p>Medium size textual content right here.</p>
      <a href="#" class="cta">Study Extra</a>
    </div>
  </article>
</div>
.card-grid {
  --card-rows: 4;
  show: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  hole: 1.5rem;
  padding: 1.5rem;
}

.card {
  show: grid;
  grid-row: span var(--card-rows);
  grid-template-rows: subgrid;
  border: 1px stable var(--card-border-color, #e0e0e0);
  border-radius: 8px;
  overflow: hidden;
}

.card-inner {
  show: grid;
  grid-row: 1 / -1;
  grid-template-rows: subgrid;
  container-type: inline-size;
  container-name: card;
}

.card img {
  width: 100%;
  top: 200px;
  object-fit: cowl;
}

.card h3 {
  padding: 0 1rem;
  margin: 0;
  font-size: clamp(1rem, 4vw, 1.5rem);
}

.card p {
  padding: 0 1rem;
  margin: 0;
}

.card .cta {
  padding: 0 1rem 1rem;
  align-self: finish;
}

@container card (min-inline-size: 480px) {
  .card-inner {
    grid-template-columns: 200px 1fr;
  }

  .card-inner img {
    top: 100%;
    grid-row: 1 / -1;
  }

  .card-inner h3 {
    font-size: clamp(1.25rem, 3cqi, 2rem);
  }
}

@container card (max-inline-size: 479.999px) {
  .card-inner h3 {
    font-size: clamp(1rem, 5cqi, 1.5rem);
  }
}

Resizing the browser triggers a cascade: the outer grid reflows its columns, which adjustments every card’s out there inline dimension, which fires the container queries, which restructure the cardboard’s internals, whereas subgrid alignment holds regular throughout sibling playing cards all through your complete course of.

Resizing the browser triggers a cascade: the outer grid reflows its columns, which adjustments every card’s out there inline dimension, which fires the container queries, which restructure the cardboard’s internals, whereas subgrid alignment holds regular throughout sibling playing cards all through your complete course of.

The bottom .card h3 font-size makes use of viewport-relative vw models moderately than cqi models. The cqi unit is just legitimate inside @container blocks the place the containment context is assured to be established. Exterior these blocks, cqi might resolve unpredictably. The @container guidelines override the bottom worth with cqi-based sizing as soon as containment is confirmed.

Integrating the Trilogy in a React Element

Element Structure

The React implementation makes use of a CardGrid wrapper element and a Card little one element. The essential architectural determination: all structure logic lives in CSS. React handles information mapping and rendering solely. No JavaScript-based resize observers are wanted for the responsive conduct.

This implementation requires React 17+ with the automated JSX rework enabled. For React variations beneath 17, add import React from 'react'; on the high of the element file. CSS Modules assist is required out of your bundler (Vite, Subsequent.js, and Create React App all assist this out of the field).

When You Nonetheless Want JavaScript

Container queries get rid of most resize-observer use circumstances for structure, however edge circumstances stay. Studying actual container dimensions for animation thresholds, triggering analytics occasions at particular sizes, or integrating with third-party libraries that require pixel values nonetheless requires ResizeObserver. It enhances container queries moderately than changing them.

React Code Walkthrough


import types from './CardGrid.module.css';

operate sanitizeUrl(url) {
  if (!url) return '#';
  const trimmed = url.trim().toLowerCase();
  if (trimmed.startsWith('javascript:') || trimmed.startsWith('information:')) {
    return '#';
  }
  return url;
}

operate Card({ picture, alt, title, physique, ctaUrl, ctaText }) {
  return (
    <article className={types.card}>
      <div className={types.cardInner}>
        <img src={picture} alt={alt ?? ''} />
        <h3>{title}</h3>
        <p>{physique}</p>
        <a href={sanitizeUrl(ctaUrl)} className={types.cta}>{ctaText}</a>
      </div>
    </article>
  );
}

export default operate CardGrid({ playing cards }) {
  return (
    <div className={types.cardGrid}>
      {playing cards.map((card, index) => {
        const stableKey = card.id != null ? card.id : `card-fallback-${index}`;
        if (card.id == null && course of.env.NODE_ENV !== 'manufacturing') {
          console.warn('CardGrid: card at index', index, 'is lacking a secure `id` prop.');
        }
        return <Card key={stableKey} {...card} />;
      })}
    </div>
  );
}

Use a secure distinctive identifier out of your information because the key prop. Index keys trigger reconciliation errors when playing cards are reordered, filtered, or eliminated. The element will warn in growth if a card is lacking an id.





.cardGrid {
  --card-rows: 4;
  show: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  hole: 1.5rem;
  padding: 1.5rem;
}

.card {
  show: grid;
  grid-row: span var(--card-rows);
  grid-template-rows: subgrid;
  border: 1px stable var(--card-border-color, #e0e0e0);
  border-radius: 8px;
  overflow: hidden;
}

.cardInner {
  show: grid;
  grid-row: 1 / -1;
  grid-template-rows: subgrid;
  container-type: inline-size;
  container-name: card;
}

.card img {
  width: 100%;
  top: 200px;
  object-fit: cowl;
}

.card h3 {
  padding: 0 1rem;
  margin: 0;
  font-size: clamp(1rem, 4vw, 1.5rem);
}

.card p {
  padding: 0 1rem;
  margin: 0;
}

.cta {
  padding: 0 1rem 1rem;
  align-self: finish;
}

@container card (min-inline-size: 480px) {
  .cardInner {
    grid-template-columns: 200px 1fr;
  }

  .cardInner img {
    top: 100%;
    grid-row: 1 / -1;
  }

  .cardInner h3 {
    font-size: clamp(1.25rem, 3cqi, 2rem);
  }
}

@container card (max-inline-size: 479.999px) {
  .cardInner h3 {
    font-size: clamp(1rem, 5cqi, 1.5rem);
  }
}

React owns the information stream; CSS owns each responsive conduct. No resize handlers run in JavaScript, and each structure change occurs by means of CSS alone. One sensible observe on tooling: Tailwind CSS v3.2+ helps container queries through the @tailwindcss/container-queries plugin; Tailwind v4 contains native assist. Subgrid utilities stay restricted as of Tailwind v4.x, so confirm present protection to your model.

Superior Patterns and Gotchas

Nesting Container Queries Inside Subgrid Gadgets

A subgridded card can itself comprise a nested element, like a media object, that makes use of its personal @container guidelines. This works, however requires consciousness of two constraints:

  1. container-type and subgrid can’t coexist on the identical component. container-type: inline-size establishes dimension containment, which creates a structure boundary that blocks the guardian grid’s monitor sizes from reaching the contained component. In case you want each subgrid and container queries, place container-type on an interior wrapper component and maintain grid-template-rows: subgrid on the outer grid participant.
  2. container-type: inline-size establishes a brand new block formatting context on the component it is utilized to. Little one margins not collapse by means of the containment boundary, and overflow defaults to seen inside the new BFC. Account for each behaviors when nesting containment inside subgrid buildings.

Efficiency Concerns

Container queries introduce a structure dependency chain: the browser sizes the container, then evaluates the question and lays out the container’s kids. At three or fewer nesting ranges, this provides no seen body value (verified in Chrome DevTools Efficiency panel). Past that depth, every further containment context provides structure recalculation passes. Profile with the DevTools Efficiency panel to verify whether or not deeper nesting impacts your particular structure. Subgrid reuses the guardian’s already-computed monitor sizing and customarily doesn’t add significant structure value in comparison with common grid.

Widespread Errors

Three errors seem repeatedly when builders first mix these options. Forgetting to declare container-type on the container component is the commonest: with out it, @container guidelines silently do nothing. No console warnings, no errors, simply types that by no means apply. One other frequent mistake is utilizing subgrid with out setting span on the guardian grid merchandise. With out an express span, the merchandise occupies a single monitor, so subgrid has nothing helpful to inherit, and the structure collapses. The third is declaring container-type: dimension (which incorporates each block and inline dimensions) when solely inline-size containment is required. The dimension worth forces the browser to comprise the component’s top as properly, which might produce sudden structure outcomes when the component’s top must be decided by its content material.

Forgetting to declare container-type on the container component is the commonest: with out it, @container guidelines silently do nothing. No console warnings, no errors, simply types that by no means apply.

Implementation Guidelines

  • Outline macro grid on the outermost wrapper
  • Set grid-row: span N on playing cards matching inner content material sections (N should equal the variety of direct grid kids)
  • Apply grid-template-rows: subgrid on every card
  • Add container-type: inline-size to an interior wrapper component (not the subgrid participant itself)
  • Identify containers with container-name when nesting (use distinctive names per element to keep away from world collisions)
  • Write @container breakpoints based mostly on element widths, not viewport
  • Use cqi / cqb models solely inside @container blocks the place containment is established; use viewport-relative models (vw) for base types exterior container queries
  • Use clamp() with cqi models to set minimal and most bounds for fluid inner sizing
  • Check with browser DevTools container question overlay: Chrome DevTools (Parts → Structure → Container Queries, Chrome 105+) or Firefox Inspector (Grid/Flexbox overlay panel, Firefox 110+)
  • Validate with @helps for any legacy viewers, and complement with useful testing for the mixed function implementation
  • Audit nesting depth and maintain containment at three ranges or fewer

Ship It With no Polyfill

The structure trilogy of CSS Grid, Subgrid, and Container Queries is production-ready in each main browser (Chrome, Firefox, Safari, and Edge). No polyfills or JavaScript fallbacks are required. Evaluation the Superior Patterns part for recognized gotchas earlier than delivery. The CSS Containment Degree 3 and CSS Grid Degree 2 specs present the authoritative references for edge circumstances and future additions.



Supply hyperlink

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles