Debugging Container Queries in DevTools: Why a Rule Doesn't Match

Media queries fail in one way: the viewport is not the size you thought. Container queries have more moving parts — which ancestor is the container, whether it has the right type, whether its name matches, how big it actually is — and each part fails silently. The @container rule is simply never applied, with no error. The narrow problem this page solves is diagnosing that silence quickly, with a checklist that maps each symptom to its cause and the DevTools features in Chrome, Firefox and Safari that reveal it. It belongs to Container Query Syntax Basics in the Mastering Container Queries & Responsive Layouts guide.

Why container queries fail silently

A container query is evaluated in three stages. First, the browser searches ancestors of the styled element for a query container: an element with a container-type other than normal, and a container-name matching the rule's name if the rule has one. Second, it measures that container's content box. Third, it evaluates the condition against the measurement. A failure at any stage makes the rule inapplicable, and CSS has no way to report that except by not applying styles.

Where a container query can fail Stage one finds the container: fails if no ancestor has container-type or the name does not match. Stage two measures it: fails if the container collapsed to zero or lacks block-size containment for a height condition. Stage three evaluates the condition: fails if the condition is invalid or uses units unexpectedly. Three stages, three families of bug 1. Find container nearest matching ancestor 2. Measure it content-box size 3. Evaluate condition against size no container-type name mismatch querying itself wrong ancestor wins collapsed to 0px height on inline-size padding not counted and/or unparenthesised var() in condition em vs rem surprise

Knowing which stage failed narrows the search immediately. The workflow below checks them in order.

The debugging workflow

The sample below contains four deliberate bugs, one per common cause. Each fix is annotated.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Container query bugs</title>
<style>
  /* BUG 1: querying yourself. .card is the container AND the element
     being styled, so the rule looks for a container ABOVE .card and
     finds none. FIX: style a child, or make the parent the container. */
  .card { container-type: inline-size; }
  @container (width > 30rem) { .card { display: flex; } }            /* never applies */
  @container (width > 30rem) { .card__body { display: flex; } }      /* works */

  /* BUG 2: name mismatch. The rule asks for "sidebar"; the ancestor is
     named "aside". FIX: use the same name, or drop the name. */
  .aside { container: aside / inline-size; }
  @container sidebar (width > 20rem) { .nav { columns: 2; } }        /* never applies */

  /* BUG 3: height query on an inline-size container. Only size
     containers can be queried on height. FIX: container-type: size
     plus an explicit height, or query width instead. */
  .tile { container-type: inline-size; }
  @container (height > 10rem) { .tile__meta { display: block; } }    /* never applies */

  /* BUG 4: collapsed container. A container is a flex item with no
     basis, so its content-derived width is 0 and nothing matches.
     FIX: give it flex: 1, a width, or align-self: stretch. */
  .row { display: flex; }
  .row > .widget { container-type: inline-size; }                   /* 0px wide */
  .row > .widget { flex: 1; }                                        /* fixed */
</style>
</head>
<body>
  <article class="card"><div class="card__body">Body</div></article>
</body>
</html>

Step 1: confirm the rule exists and is unmatched. Select the element you expected to change. In the Styles pane, an @container rule that is not applying is listed but greyed out or absent from the matched rules, depending on the browser. If the rule is not listed at all, the problem is the selector, not the container.

Step 2: find the container the rule resolved against. In Chrome and Edge, each @container rule in the Styles pane shows the container it resolved to, such as @container (width > 30rem) followed by div.card-slot. Hovering highlights the container on the page; clicking selects it in the Elements tree. If it says no container was found, you are in stage one: check container-type on the intended ancestor, the name, and whether you are styling the container itself.

Step 3: read the container's size. With the container selected, check the Computed pane or the box model diagram. The query compares against the content box — width minus padding and border. A container that looks 400 pixels wide with 24 pixels of padding each side is 352 pixels for query purposes. A reported width of zero means the container has collapsed.

Step 4: check the condition. Copy the condition into a scratch rule on the container itself as an @supports-style sanity check: is it well-formed, are and and or grouped with parentheses, and does it use units you expect? In container conditions em resolves against the container's font size, not the styled element's, which surprises people using em-based thresholds.

The key technique: DevTools badges and overlays

Every major browser now marks containers in the Elements panel, which is the fastest way to see the containment structure of a page at a glance.

Reading the container structure from the Elements panel A DOM tree: main with a container badge named layout, section.cards without a badge, div.card-slot with a container badge, and h3.card__title. The title's container query resolves to the nearest badged ancestor, div.card-slot, not main. The nearest badged ancestor is the one measured <main> container <section class="cards"> <div class="card-slot"> container <h3 class="card__title"> @container resolves here Unnamed queries use the nearest container; named ones skip non-matching names.

In Chrome and Edge, elements that establish a container show a container badge in the Elements tree; clicking it toggles an overlay that outlines the container and labels its size. Firefox's inspector shows the container type in the rules view and marks container query rules with the condition. Safari's Web Inspector lists @container rules in the Styles sidebar and lets you inspect ancestors for container-type. In all three, the fastest test of a hypothesis is to edit the container's width in the Styles pane and watch the rule switch on and off.

Subtler failures: the rule matches, but the wrong way

Not every container bug is a rule that never applies. Some rules apply at the wrong time, which is harder to notice.

The wrong ancestor answered. An unnamed @container rule uses the nearest container of any matching type. When a component is dropped inside another component that also establishes a container, the inner container becomes the one measured, and the thresholds suddenly apply to a much narrower box. Naming the containers each component depends on — container-name: card — makes the relationship explicit and immune to nesting.

Units resolve somewhere unexpected. Container conditions written in em resolve against the container's computed font size, which may differ from the styled element's. Container query units such as cqi inside the rule's declarations resolve against the nearest container for that element, which is not always the container the condition queried. When sizes look off by a consistent ratio, check which container each unit is measuring.

The container changes size because of its own children. An inline-size container ignores its children for its inline size, but a size container with a percentage height, or one inside a grid track sized by content, can change size when the query's styles change what is inside the track. The result is flicker at the threshold as the layout alternates. DevTools shows this as a rule that toggles on every frame; the fix is to give the container a size that does not depend on the queried content.

Styles transition across the threshold. If a transition is declared on a property that the query changes, crossing the threshold animates — which may be the intention, or may look like lag. Temporarily disabling transitions in DevTools distinguishes a slow query from an animation.

Variation: a debugging stylesheet

For a component library, a small opt-in debugging stylesheet makes containers visible without opening DevTools, which helps designers and reviewers see what each component is responding to.

/* Include only in development, or behind a query parameter. */
[data-debug-containers] :where([style*="container"], .cq, [class*="-slot"]) {
  outline: 1px dashed #f59e0b;
  outline-offset: -1px;
}

/* Label each band the component is in by setting a token per band... */
@container (width < 20rem)          { .cq-label::after { content: "compact"; } }
@container (20rem <= width < 40rem) { .cq-label::after { content: "regular"; } }
@container (width >= 40rem)         { .cq-label::after { content: "wide"; } }

/* ...and printing it in a corner of the component. */
[data-debug-containers] .cq-label::after {
  position: absolute;
  inset: 0 0 auto auto;
  padding: 0 0.3rem;
  font: 10px/1.4 ui-monospace, monospace;
  background: #fef3c7;
  color: #78350f;
}

The demo shows the idea on a single card: resize the frame and the corner tag updates as the container crosses each band boundary.

Live demoDebug labels that print each container band
Drag the frame narrower and wider: the amber tag in the corner names the band the component is currently in. Drag the bottom-right corner to resize the frame in either direction.

Printing the active band in each component turns a silent mechanism into a visible one, which is invaluable when tuning thresholds. The thresholds themselves are best written with the range syntax covered in Container Query Range Syntax and Logic, so bands never overlap. For the collapsed-container problem in depth, see container-type: size vs inline-size. The same DevTools discipline applies to motion: Profiling Animations in DevTools covers the animation side of the panels.

Browser support

Container queries are supported in Chrome and Edge 105+, Firefox 110+ and Safari 16+. DevTools support for inspecting them is present in current versions of all three browsers: Chromium's Elements panel shows container badges and the resolved container for each @container rule, Firefox's inspector shows container query rules and container types, and Safari's Web Inspector lists @container rules in its Styles sidebar. Exact panel names and affordances change between releases, but the resolved-container information is available in all of them.

FAQ

Why does my @container rule never apply? The most common causes are no ancestor with container-type set, a container name that does not match, a condition on height against an inline-size container, or a container that has collapsed to zero width. DevTools shows which container a rule resolved against, which usually identifies the cause within seconds.

How do I see which element a container query is measuring? In Chromium DevTools, the Styles pane shows each @container rule with the container it resolved to; hovering it highlights the container on the page, and clicking it selects the container in the Elements tree. Firefox and Safari show the container query condition and let you inspect ancestors to find the container.

Why is my container zero pixels wide? An element with container-type: inline-size does not use its content to determine its inline size. If it sits in a shrink-to-fit context, such as a flex item without a basis or an absolutely positioned element without a width, it collapses to zero. Give it a width or let it stretch.

Can I test container queries without resizing the browser? Yes. Change the container's width directly in the Styles pane, or add resize: horizontal with overflow: auto to the container temporarily and drag its corner. Container queries respond to the container, so the viewport does not need to change.

Related articles

More pages in the same section.