Prerequisites
- Basic familiarity with CSS container queries and the container-type and container-name properties.
- Ability to inspect DOM and computed styles in the browser devtools.
Implementation decision
- If your rules include block-axis queries (height, block-size), keep the container that those elements rely on set to container-type: size.
- If you only need inline-axis checks (width), prefer container-type: inline-size to limit containment cost.
- Use container-name to ensure the browser targets the intended ancestor when nested containers could confuse matching.
Expected result
- When the container remains container-type: size, both width- and height-based @container queries will find a valid ancestor and evaluate normally.
- If you switch the same container to container-type: inline-size at runtime, inline-axis (width) queries will still match, but block-axis (height) queries will report no eligible container and stop matching.
- A named container still resolves by name, but a name does not grant missing axis support; axis-based queries still need the right container-type.
Limitation
- The guidance isolates containment eligibility. It does not cover cases where a query fails due to selector specificity, layout overrides, or DOM moves that change ancestors. Always verify the actual DOM path and computed container-type in the browser where the issue appears.
Working example
export function createElement({ tag = 'div', width = 0, height = 0 } = {}) {
return {
tag,
width,
height,
parent: null,
children: [],
containerType: 'normal',
containerName: undefined,
};
}
export function appendChild(parent, child) {
child.parent = parent;
parent.children.push(child);
}
export function setContainerType(el, type) {
el.containerType = type;
}
export function setContainerName(el, name) {
el.containerName = name;
}
export function toggleContainerType(el, nextType) {
setContainerType(el, nextType);
}
function compare(op, left, right) {
switch (op) {
case '>': return left > right;
case '>=': return left >= right;
case '<': return left < right;
case '<=': return left <= right;
default: throw new Error(`unsupported compare operator: ${op}`);
}
}
function queryAxis(query) {
if (query.kind === 'name-only') return null;
if (query.axis) return query.axis;
if (query.feature === 'width') return 'inline';
if (query.feature === 'height') return 'block';
return null;
}
export function findEligibleContainer(startEl, query) {
let node = startEl.parent;
const axis = queryAxis(query);
while (node) {
if (query.kind === 'name-only') {
if (node.containerName === query.name) return node;
} else if (axis) {
if (node.containerType === 'size') return node;
if (node.containerType === 'inline-size' && axis === 'inline') return node;
}
node = node.parent;
}
return null;
}
export function evaluateQuery(targetEl, query) {
const container = findEligibleContainer(targetEl, query);
if (!container) {
return { matched: false, reason: 'no-eligible-container', container: null };
}
if (query.kind === 'name-only') {
return { matched: true, container };
}
const axis = queryAxis(query);
const size = axis === 'inline' ? container.width : container.height;
return {
matched: compare(query.compare, size, query.value),
container,
containerValue: size,
};
}
Read the full article: https://chriseugenerodriguez.com/blog/why-do-container-queries-stop-matching-when-you-switch-container-type-dynamically