Skip to content

Fix: CSS Container Query Not Working — @container and container-type Issues

FixDevs ·

Quick Answer

How to fix CSS container queries not working — setting container-type correctly, understanding containment scope, fixing @container syntax, and handling browser support and specificity issues.

The Problem

A CSS container query has no effect — styles inside @container don’t apply regardless of the element’s size:

/* This has no effect — the card never changes style */
.card-wrapper {
  container: sidebar / inline-size;
}

@container sidebar (min-width: 400px) {
  .card {
    display: flex;
  }
}

Or the container query fires at the wrong size:

/* Expected to trigger at 400px — but triggers at a different width */
@container (min-width: 400px) {
  .item {
    font-size: 1.25rem;
  }
}

Or a specific error in the browser DevTools:

Property 'container-type' is not valid.  (old browsers)

Or the container query works in isolation but breaks inside a grid or flex layout.

Why This Happens

Container queries are fundamentally different from media queries. Media queries respond to the viewport; container queries respond to a specific ancestor element — but only if that ancestor is correctly established as a containment context:

  • Missing container-type — the most common cause. Without container-type: inline-size (or size), an element is not a container and @container rules targeting it never fire.
  • Querying the container itself@container styles apply to descendants of the container, not to the container element itself. Trying to restyle the container based on its own size doesn’t work.
  • Wrong ancestor — named containers let you specify which container to query. Without a name, the query uses the nearest ancestor with container-type set, which may not be the element you intended.
  • container-type: size without explicit dimensionssize containment requires both inline and block sizes to be defined. If the container’s height is auto, using container-type: size may cause layout collapse.
  • Browser support — container queries require Chrome 105+, Firefox 110+, and Safari 16+. Older browser versions (especially older Safari) don’t support them at all.
  • Specificity and cascade — styles inside @container compete with other CSS rules by specificity. An @container rule with lower specificity is overridden by a non-container rule.

Fix 1: Set container-type on the Correct Element

The single most common mistake is forgetting container-type on the parent element. Without it, @container rules never apply:

/* WRONG — no container-type, @container rules have no effect */
.card-wrapper {
  width: 100%;
}

@container (min-width: 400px) {
  .card {
    display: flex;
  }
}
/* CORRECT — set container-type on the parent */
.card-wrapper {
  container-type: inline-size;  /* ← This establishes the containment context */
  width: 100%;
}

@container (min-width: 400px) {
  .card {
    display: flex;
  }
}

Matching HTML structure:

<div class="card-wrapper">   <!-- container-type: inline-size -->
  <div class="card">         <!-- @container rules apply to this -->
    <h2>Title</h2>
    <p>Content</p>
  </div>
</div>

container-type values and when to use each:

ValueContainmentUse when
inline-sizeHorizontal width onlyQuerying width (most common)
sizeBoth width and heightQuerying width or height — requires explicit height
normalNo size containment (default)Resets; only name is set

Common Mistake: Using container-type: size when you only need inline-size. size containment prevents the element from sizing itself based on its children’s height — which often causes the container to collapse to zero height unexpectedly.

Fix 2: Understand the Scope — @container Styles Apply to Descendants

@container rules style the children of the container, not the container element itself. This is the key mental model shift from media queries:

/* WRONG — trying to style the container based on its own size */
.sidebar {
  container-type: inline-size;
}

@container (max-width: 300px) {
  .sidebar {          /* ← This doesn't work — .sidebar IS the container */
    padding: 0.5rem;
  }
}
/* CORRECT — style descendants of the container */
.sidebar {
  container-type: inline-size;
}

@container (max-width: 300px) {
  .sidebar-nav {      /* ← child of .sidebar — this works */
    padding: 0.5rem;
  }
  .sidebar-link {     /* ← child of .sidebar — this works */
    font-size: 0.875rem;
  }
}

If you need the container element itself to change style based on its size, combine a container query on the parent with direct styling, or use a JavaScript ResizeObserver.

Fix 3: Use Named Containers to Target the Right Ancestor

Without a name, @container queries the nearest ancestor with container-type set. Naming containers lets you target a specific ancestor:

/* Unnamed — queries the nearest container ancestor */
.sidebar {
  container-type: inline-size;
}

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

/* This @container targets the nearest container ancestor of .card */
/* If .card is inside .sidebar, it queries .sidebar */
/* If .card is inside .main-content, it queries .main-content */
@container (min-width: 600px) {
  .card {
    display: grid;
  }
}
/* Named containers — explicitly target the right ancestor */
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
  /* Shorthand: container: sidebar / inline-size; */
}

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

/* This only fires when the sidebar container is ≥ 200px */
@container sidebar (min-width: 200px) {
  .card {
    padding: 1rem;
  }
}

/* This only fires when the main content container is ≥ 600px */
@container main (min-width: 600px) {
  .card {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
}

Shorthand syntax for both name and type:

/* Long form */
.wrapper {
  container-type: inline-size;
  container-name: card-container;
}

/* Shorthand: container: <name> / <type> */
.wrapper {
  container: card-container / inline-size;
}

Fix 4: Fix Layout Collapse with container-type: size

size containment prevents the element from sizing based on its children in both dimensions. If the container has no explicit height, it collapses to zero:

/* WRONG — container-type: size with no explicit height */
.card-grid {
  container-type: size;   /* height collapses to 0 */
  display: grid;
}

/* Grid items can't be seen — container has no height */
/* CORRECT option 1 — use inline-size instead of size (most cases) */
.card-grid {
  container-type: inline-size;  /* Only inline (width) containment */
  display: grid;
}

/* CORRECT option 2 — provide explicit height when using size */
.card-grid {
  container-type: size;
  height: 400px;    /* Explicit height prevents collapse */
  display: grid;
}

/* CORRECT option 3 — use size for elements with known height */
.modal {
  container-type: size;
  width: 600px;
  height: 80vh;     /* Height is known */
}

When to use container-type: size vs inline-size:

  • Use inline-size for components that reflow based on width (cards, sidebars, articles).
  • Use size for fixed-size containers where you also need to query height (modals, panels, media players).

Fix 5: Correct @container Syntax

Container query syntax has some differences from media query syntax:

/* Media query syntax */
@media (min-width: 768px) { }
@media screen and (max-width: 1200px) { }
@media (min-width: 400px) and (max-width: 800px) { }

/* Container query syntax — similar but no media type */
@container (min-width: 768px) { }
@container (max-width: 1200px) { }
@container (min-width: 400px) and (max-width: 800px) { }

/* Named container */
@container sidebar (min-width: 200px) { }
@container main (min-width: 600px) and (max-width: 1000px) { }

Available container query features (Level 1):

/* Size-based queries */
@container (min-width: 400px) { }       /* Width ≥ 400px */
@container (max-width: 600px) { }       /* Width ≤ 600px */
@container (min-height: 300px) { }      /* Height ≥ 300px (requires container-type: size) */
@container (aspect-ratio: 16/9) { }     /* Aspect ratio exactly 16:9 */
@container (min-aspect-ratio: 4/3) { }  /* Aspect ratio at least 4:3 */

/* Modern range syntax (Chrome 111+, Firefox 110+) */
@container (width >= 400px) { }         /* Equivalent to min-width: 400px */
@container (400px <= width <= 800px) { } /* Range — between 400px and 800px */

Style queries (experimental — Chrome 111+):

/* Query a CSS custom property value on the container */
@container style(--variant: card) {
  .title {
    font-size: 1.5rem;
  }
}

Fix 6: Handle Browser Support

Container queries are broadly supported in modern browsers, but older versions need a fallback or polyfill:

Browser support (as of 2025):

  • Chrome 105+ ✓
  • Firefox 110+ ✓
  • Safari 16+ ✓
  • Edge 105+ ✓
  • IE: not supported (no plans)

Check support with @supports:

/* Fallback for browsers without container query support */
.card {
  /* Default layout for all browsers */
  padding: 1rem;
}

@supports (container-type: inline-size) {
  .card-wrapper {
    container-type: inline-size;
  }

  @container (min-width: 400px) {
    .card {
      display: flex;
      gap: 1rem;
    }
  }
}

Use the container-query polyfill for older browser support:

npm install container-query-polyfill
// Load polyfill for browsers that don't support container queries
const supportsContainerQueries = 'container' in document.documentElement.style;
if (!supportsContainerQueries) {
  import('container-query-polyfill');
}

Or via CDN with conditional loading:

<script>
  if (!('container' in document.documentElement.style)) {
    const script = document.createElement('script');
    script.src = 'https://cdn.example.com/container-query-polyfill.js';
    document.head.appendChild(script);
  }
</script>

Note: The polyfill has limitations — it works for simple cases but doesn’t support all features like style queries or nested containers. Test thoroughly.

Fix 7: Debugging Container Queries

Use DevTools to inspect container boundaries. In Chrome DevTools, elements with container-type set show a “container” badge in the Elements panel. Click the badge to see the container’s size and which @container rules apply.

Add a visual outline to see the container:

/* Temporary debug styles */
.card-wrapper {
  container-type: inline-size;
  outline: 2px dashed red;  /* See the container boundary */
}

Log container size with JavaScript:

const container = document.querySelector('.card-wrapper');
const observer = new ResizeObserver(entries => {
  for (const entry of entries) {
    console.log('Container width:', entry.contentRect.width);
    // Check if this matches when you expect @container rules to fire
  }
});
observer.observe(container);

Verify the @container threshold with a computed value check:

// Test if the container query is firing correctly
const element = document.querySelector('.card');
const styles = getComputedStyle(element);
console.log('display:', styles.display);
// Should be 'flex' if the @container (min-width: 400px) rule is active

Common specificity pitfall — container query rules compete with regular CSS by specificity, not by source order alone:

/* This container rule has specificity (0, 1, 0) — one class */
@container (min-width: 400px) {
  .card {
    display: flex;
  }
}

/* This regular rule also has specificity (0, 1, 0) and comes AFTER */
/* It wins because source order breaks the tie */
.card {
  display: block;
}

/* Fix — increase specificity of the container rule */
@container (min-width: 400px) {
  .card-wrapper .card {   /* Now specificity is (0, 2, 0) */
    display: flex;
  }
}

/* Or reorder so the container rule comes last */
.card {
  display: block;
}

@container (min-width: 400px) {
  .card {
    display: flex;   /* Wins because it comes after */
  }
}

Still Not Working?

Inspect the container in DevTools. Open DevTools → Elements panel, select the container element, and check the Computed styles. Verify that container-type is applied and not overridden. Chrome shows a container icon next to elements with containment.

Check for overflow: hidden on ancestors. If an ancestor element clips the container, the container query may respond to a clipped size rather than the intended layout size.

Verify the container and queried element are in the correct DOM relationship. The element being styled by @container must be a descendant of the container — not a sibling or ancestor.

<!-- CORRECT — .card is a descendant of .wrapper -->
<div class="wrapper">       <!-- container-type: inline-size -->
  <div class="card">        <!-- @container applies here -->
    Content
  </div>
</div>

<!-- WRONG — .card is a sibling of .wrapper -->
<div class="wrapper"></div>  <!-- container-type: inline-size -->
<div class="card"></div>     <!-- @container does NOT apply here -->

In React and CSS Modules, class names may be transformed. Verify the generated class names in DevTools match what you’re querying. In Next.js, CSS Modules generate scoped class names — use the .module.css file’s class references, not raw strings:

/* styles.module.css */
.wrapper {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card {
    display: flex;
  }
}
import styles from './styles.module.css';

export function Component() {
  return (
    <div className={styles.wrapper}>
      <div className={styles.card}>Content</div>
    </div>
  );
}

For related CSS issues, see Fix: CSS Variables Not Working and Fix: CSS Flexbox Not Centering.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles