Fix: CSS Container Query Not Working — @container and container-type Issues
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. Withoutcontainer-type: inline-size(orsize), an element is not a container and@containerrules targeting it never fire. - Querying the container itself —
@containerstyles 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-typeset, which may not be the element you intended. container-type: sizewithout explicit dimensions —sizecontainment requires both inline and block sizes to be defined. If the container’s height isauto, usingcontainer-type: sizemay 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
@containercompete with other CSS rules by specificity. An@containerrule 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:
| Value | Containment | Use when |
|---|---|---|
inline-size | Horizontal width only | Querying width (most common) |
size | Both width and height | Querying width or height — requires explicit height |
normal | No size containment (default) | Resets; only name is set |
Common Mistake: Using
container-type: sizewhen you only needinline-size.sizecontainment 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-sizefor components that reflow based on width (cards, sidebars, articles). - Use
sizefor 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 activeCommon 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.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Panda CSS Not Working — Styles Not Applying, Tokens Not Resolving, or Build Errors
How to fix Panda CSS issues — PostCSS setup, panda.config.ts token system, recipe and pattern definitions, conditional styles, responsive design, and integration with Next.js and Vite.
Fix: UnoCSS Not Working — Classes Not Generating, Presets Missing, or Attributify Mode Broken
How to fix UnoCSS issues — Vite plugin setup, preset configuration, attributify mode, icons preset, shortcuts, custom rules, and integration with Next.js, Nuxt, and Astro.
Fix: CodeMirror Not Working — Editor Not Rendering, Extensions Not Loading, or React State Out of Sync
How to fix CodeMirror 6 issues — basic setup, language and theme extensions, React integration, vim mode, collaborative editing, custom keybindings, and read-only mode.
Fix: GSAP Not Working — Animations Not Playing, ScrollTrigger Not Firing, or React Cleanup Issues
How to fix GSAP animation issues — timeline and tween basics, ScrollTrigger setup, React useGSAP hook, cleanup and context, SplitText, stagger animations, and Next.js integration.