CSS Container Queries Edge Cases: When @container Silently Fails
Container queries are here, but containment types and the ancestor lookup algorithm create subtle traps. Here's what breaks and how to fix it.
Container queries shipped. You're finally styling components based on their container,
not the viewport. But then things break: nested containers ignore your queries, elements
collapse to zero height, or query units resolve to viewport sizes instead of container
sizes. The culprit? Containment types and the lookup algorithm—here's what trips up
most teams adopting @container.
The Containment Type Trap
The first gotcha: container-type determines which dimensions
you can query. Pick wrong, and your queries silently fail:
/* ❌ This query NEVER matches */
.sidebar {
container-type: inline-size; /* Only inline (width) queryable */
}
@container (height > 500px) {
.card { padding: 2rem; } /* Ignored—height not tracked */
}
/* ✅ Use 'size' to query both axes */
.sidebar {
container-type: size; /* Both width AND height queryable */
}
@container (height > 500px) {
.card { padding: 2rem; } /* Now this works */
}
inline-size only establishes containment on the inline axis (width in
horizontal writing modes). Height queries require container-type: size.
Why inline-size Is the Default
Most examples use inline-size because size containment has
a critical side effect—it breaks intrinsic height:
/* container-type: size creates height containment */
.container {
container-type: size;
}
/* Problem: Children no longer contribute to parent height */
<div class="container">
<p>This text doesn't affect container height</p>
<p>Container collapses to 0px unless you set explicit height</p>
</div>
/* You MUST set an explicit height */
.container {
container-type: size;
height: 400px; /* Required for size containment */
}
Size containment means the element's size is determined independently
of its contents. If you don't set a height, it collapses. Use inline-size
unless you specifically need to query height.
The Nearest Ancestor Rule
Container queries target the nearest ancestor with containment—not necessarily the one you intended:
<section class="page"> <!-- container-type: inline-size -->
<div class="sidebar"> <!-- container-type: inline-size -->
<article class="card"> <!-- Queries target .sidebar, not .page! -->
</article>
</div>
</section>
/* This queries .sidebar, the nearest container */
@container (width > 800px) {
.card { grid-template-columns: 1fr 1fr; }
}
/* .sidebar is 300px, so this never matches—
even though .page is 1200px */The fix: use named containers to target specific ancestors:
.page {
container-type: inline-size;
container-name: page;
}
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
/* Explicitly query the page container */
@container page (width > 800px) {
.card { /* Styles for when PAGE is wide */ }
}
/* Or query the sidebar */
@container sidebar (width > 200px) {
.card { /* Styles for when SIDEBAR is wide */ }
}Container Query Units Fallback
Container query units (cqw, cqh, cqi, cqb)
are powerful—but they have a surprising fallback:
.element {
font-size: 5cqi; /* 5% of container inline size */
}
/* If NO container exists up the tree...
cqi falls back to svw (small viewport width)
cqb falls back to svh (small viewport height) */This means query units work outside container contexts—but they become viewport-relative. If your component is supposed to be self-contained, this fallback can break layouts:
/* Defensive pattern: ensure a container exists */
.component-wrapper {
container-type: inline-size;
}
.component-content {
/* Now cqi definitely refers to .component-wrapper */
padding: 2cqi;
}Can't Query Your Own Container
A common mistake: trying to apply container-queried styles to the container itself:
.card {
container-type: inline-size;
}
/* ❌ This doesn't work */
@container (width > 400px) {
.card { /* Styles for the container itself */ }
}
/* The query selects .card's CHILDREN based on .card's size
You cannot style .card itself with its own container query */
/* ✅ Fix: Add a wrapper or style children */
<div class="card-container"> <!-- container-type here -->
<div class="card"> <!-- style this -->
</div>
</div>
.card-container { container-type: inline-size; }
@container (width > 400px) {
.card { /* Now this works */ }
}Containment Breaks Certain Layouts
Container queries require CSS containment, which isolates the element from layout calculations. This breaks:
- Sticky positioning inside containers —
position: stickymay not work as expected - Percentage heights — Height containment (
size) breaksheight: 100%chains - Margin collapse — Containment creates a new block formatting context, preventing margin collapse
- Baseline alignment — Grid/flex baseline alignment can break with contained children
/* ❌ Sticky header inside container may fail */
.container {
container-type: inline-size;
}
.sticky-header {
position: sticky;
top: 0;
/* May not stick correctly depending on containment */
}
/* Test sticky behavior after adding container-type */Style Queries: The Forgotten Sibling
Container queries aren't just for size. Style queries let you respond to custom property values:
.theme-provider {
--theme: dark;
}
/* Query the computed custom property value */
@container style(--theme: dark) {
.card {
background: #1a1a1a;
color: white;
}
}
/* No container-type needed for style queries—
any element can be queried for style values */
Style queries don't require container-type declarations. Every element
is implicitly a style query container. This is useful for theme switching without
adding class names everywhere.
The Shorthand Syntax
Use the container shorthand to set both name and type:
/* Longhand */
.element {
container-name: sidebar;
container-type: inline-size;
}
/* Shorthand: name / type */
.element {
container: sidebar / inline-size;
}
/* Type-only (anonymous container) */
.element {
container-type: inline-size;
}Debugging Container Queries
Chrome DevTools has container query debugging:
- Elements panel shows container badges on elements with
container-type - Click the badge to see container dimensions
- Styles panel shows which
@containerrules match/don't match - Hover over
@containerrules to highlight the matched container
If your query isn't matching, check:
- Is there a container ancestor with the correct
container-type? - Are you querying the right axis (width vs height)?
- Is another container closer in the ancestor chain intercepting?
- If using a name, does it match exactly?
Key Takeaways
inline-sizeonly queries width; usesizefor height queriessizecontainment collapses height—set explicit dimensions- Queries target the nearest container ancestor; use names to be explicit
- Query units (
cqi, etc.) fall back to viewport units without a container - You can't style a container based on its own size—only its children
- Containment affects sticky positioning, margins, and baseline alignment
- Style queries work without
container-type
Advertisement
Explore these curated resources to deepen your understanding
Official Documentation
Tools & Utilities
Related Insights
Explore related edge cases and patterns
Advertisement