CSS Container Queries — Responsive Components That Finally Don’t Care About the Viewport

نُشرت في

CSS container queries let components respond to their parent container’s size rather than the viewport. No more cards that break in sidebars. Here’s what changed, how to use @container, and when to reach for it over media queries.

CSS Container Queries — Responsive Components That Finally Don’t Care About the Viewport 1
إعلان · حذف؟

You’ve written a card component. It looks great at 320px, great at 768px, great at 1440px. You’re proud of it. Then a designer drops it into a sidebar, and it looks like it fell down the stairs.

The viewport is 1440px wide. Your media query fires the wide layout. But the card is sitting in a 280px sidebar. The media query has no idea. And you’ve spent the last decade pretending that’s not a fundamental problem.

CSS container queries fix this. They let components respond to the size of their parent container instead of the viewport. Here’s how they work and when to reach for them.

The actual failure case

Here’s a card component styled with a media query:

/* The card "goes wide" at 600px+ viewports */
@media (min-width: 600px) {
  .card {
    display: flex;
    flex-direction: row;
  }

  .card__image {
    width: 200px;
    flex-shrink: 0;
  }
}

At a 1440px viewport, this fires. The card goes horizontal. If the card is full-width in a main content area, that’s correct. If it’s inside a 280px sidebar, you now have a card squashing its image and text in a way that probably breaks the layout.

The viewport width is a lie. What matters is how much space the card actually has.

Container queries: the setup

Container queries require two things: define a containment context on the parent, then write the query against that container instead of the viewport.

/* Step 1: Tell CSS this element is a containment context */
.card-wrapper {
  container-type: inline-size;
}

/* Step 2: Query the container, not the viewport */
@container (min-width: 500px) {
  .card {
    display: flex;
    flex-direction: row;
  }

  .card__image {
    width: 200px;
    flex-shrink: 0;
  }
}

Now the card switches to its wide layout when its wrapper hits 500px — not the viewport. Drop the same component into a 280px sidebar and the query won’t fire. Drop it into a 700px main area and it will. The component is now self-contained.

container-type values

There are three values for container-type:

قيمةWhat it tracksمتى تستخدمها
inline-sizeThe container’s inline axis (width in horizontal writing modes)Almost always. This is the one you want.
sizeBoth inline and block dimensions (width + height)When you need to query height too. Rare, requires the element to have a defined height.
normalNothing (default — no containment)To opt out of containment. Usually just omit the property.

Stick with inline-size unless you specifically need to query height. size requires the container to have a constrained height (via explicit height or block formatting context), which trips people up.

Named containers

By default, a container query matches the nearest ancestor with container-type set. That’s usually fine. When it isn’t — because you have nested containers and want to target a specific ancestor — use container-name:

.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

.main-content {
  container-type: inline-size;
  container-name: main;
}

/* Fires only when inside .main-content, not .sidebar */
@container main (min-width: 600px) {
  .card {
    display: flex;
  }
}

Or use the shorthand: container: sidebar / inline-size;

Practical patterns

Card grid that adapts anywhere it’s placed

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

.card-grid__items {
  display: grid;
  grid-template-columns: 1fr; /* Default: single column */
  gap: 1rem;
}

@container card-grid (min-width: 400px) {
  .card-grid__items {
    grid-template-columns: repeat(2, 1fr);
  }
}

@container card-grid (min-width: 700px) {
  .card-grid__items {
    grid-template-columns: repeat(3, 1fr);
  }
}

Drop this grid into a sidebar, a modal, or a full-page section. It’ll pick the right column count every time based on how much space it actually has. No viewport math required.

Navigation that collapses in tight contexts

.nav-wrapper {
  container-type: inline-size;
}

.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* When the nav container is narrow, stack items vertically */
@container (max-width: 380px) {
  .nav {
    flex-direction: column;
  }

  .nav__item {
    width: 100%;
  }
}

The nav collapses in a sidebar even on a wide desktop, and stays horizontal when it has room. This is exactly the kind of thing that required either a JS resize observer or careful viewport-plus-layout-math hacks before container queries.

Sidebar-aware article layout

.article-wrapper {
  container-type: inline-size;
  container-name: article;
}

.article__hero {
  aspect-ratio: 16/9;
  background: #464aff;
}

@container article (min-width: 600px) {
  .article__hero {
    aspect-ratio: 21/9; /* wider crop when there's room */
  }

  .article__content {
    display: grid;
    grid-template-columns: 1fr 220px;
    gap: 2rem;
  }
}

The article layout adapts whether it’s in a wide main column or squashed into a tabbed panel inside a dashboard. One component definition, correct rendering everywhere.

Container query units

Container queries also introduced a set of container-relative length units:

  • cqw — 1% of the container’s width
  • cqh — 1% of the container’s height
  • cqi — 1% of the container’s inline size
  • cqb — 1% of the container’s block size
  • cqmin / cqmax — smaller/larger of cqi و cqb

These work like vw/vh but relative to the container instead of the viewport. Useful for fluid typography that scales to the component’s space rather than the page width:

.card-wrapper {
  container-type: inline-size;
}

.card__title {
  /* Scales with the card's width, not the viewport */
  font-size: clamp(1rem, 4cqi, 1.5rem);
}

One gotcha worth knowing

A container can’t query itself. Containment works on the parent-child relationship — the element with container-type sets up the context for its descendants to query. You can’t write a rule that says “when I am narrower than X.” The element doing the adapting must be inside the element that’s being measured.

This means you often need a wrapper element you wouldn’t have needed before. Usually a <div class="widget-wrapper"> around your component. Annoying but workable — and far less annoying than ResizeObserver hacks.

دعم المتصفح

Container queries have been baseline-available since February 2023 — Chrome 105+, Firefox 110+, Safari 16+. Can I Use puts global support at ~93% as of mid-2025. If you’re not targeting IE11 (and if you are, container queries are the least of your problems), you can ship this today without a polyfill.

Container query units (cqw, cqi, etc.) landed alongside the queries themselves and have the same support story.

Style queries — querying a container’s CSS custom properties rather than its size — are a newer addition. Chrome shipped them in 111, Firefox in 129, Safari in 18. Support is still catching up but the spec is stable.

When to use them vs media queries

Media queries aren’t going away. They’re still the right tool for layout-level breakpoints — switching from a single-column to multi-column page layout, showing/hiding a sidebar, changing root font sizes. Container queries handle component-level responsiveness: cards, widgets, navigation, content blocks that can appear in multiple layout contexts.

The mental model shift: media queries answer “how big is the browser?” — container queries answer “how much space do I have right now?” For components that get reused in different layout contexts, the second question is almost always the one that matters.

If you’re building a design system or a library of reusable components, container queries should probably be your default for anything below the page-layout level. A button group, an info card, a feature tile — all of these live in different contexts and should adapt to those contexts without coupling to viewport assumptions.

Useful tools for styling demos

When building container query demos or prototyping layout breakpoints, you’ll often need placeholder backgrounds and color values. IO Tools’ مُنشئ تدرج الألوان CSS is useful for generating gradient backgrounds on demo cards and wrappers — makes visual breakpoints immediately obvious. For plugging a hex value from a design file into CSS custom properties that expect rgb() notation, the HEX to RGB converter handles that in one step.

هل تريد حذف الإعلانات؟ تخلص من الإعلانات اليوم

تثبيت ملحقاتنا

أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع

أضف لـ إضافة كروم أضف لـ امتداد الحافة أضف لـ إضافة فايرفوكس أضف لـ ملحق الأوبرا

وصلت لوحة النتائج!

لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!

إعلان · حذف؟
إعلان · حذف؟
إعلان · حذف؟

ركن الأخبار مع أبرز التقنيات

شارك

ساعدنا على الاستمرار في تقديم أدوات مجانية قيمة

اشتري لي قهوة
إعلان · حذف؟