Detect active container query CSS

Use a temporary custom-property probe so the browser decides whether a nested style rule actually applies in the current container layout.

AI assistance disclosure: Codex helped implement and test this fix under maintainer direction.
This commit is contained in:
Paul Bakaus
2026-09-02 10:17:30 -07:00
parent eec431e778
commit 720222e1df
5 changed files with 119 additions and 25 deletions
+47 -10
View File
@@ -1306,6 +1306,45 @@ if (IS_BROWSER) {
catch { return null; }
}
let containerProbeSequence = 0;
function isContainerCssRule(rule) {
return rule?.constructor?.name === 'CSSContainerRule'
|| /^\s*@container\b/i.test(rule?.cssText || '');
}
function styleRuleAppliesToLiveMatches(rule, matches) {
const style = rule?.style;
if (!style || !matches?.length || typeof getComputedStyle !== 'function') return false;
const sequence = ++containerProbeSequence;
const property = `--impeccable-container-probe-${sequence}-${Math.random().toString(36).slice(2)}`;
const value = `impeccable-container-active-${sequence}`;
const previousValue = style.getPropertyValue(property);
const previousPriority = style.getPropertyPriority(property);
try {
style.setProperty(property, value, 'important');
} catch {
return false;
}
const pseudoElements = [...new Set(
String(rule.selectorText || '').match(/::[a-zA-Z-]+(?:\([^)]*\))?/g) || [],
)];
try {
return matches.some(el => [null, ...pseudoElements].some(pseudo => {
try {
const computed = pseudo ? getComputedStyle(el, pseudo) : getComputedStyle(el);
return computed.getPropertyValue(property).trim() === value;
} catch {
return false;
}
}));
} finally {
if (previousValue) style.setProperty(property, previousValue, previousPriority);
else style.removeProperty(property);
}
}
function conditionalCssRuleIsActive(rule) {
const type = Number(rule?.type);
const constructorName = rule?.constructor?.name || '';
@@ -1321,13 +1360,6 @@ if (IS_BROWSER) {
try { return CSS.supports(condition); }
catch { return true; }
}
if (constructorName === 'CSSContainerRule' || /^\s*@container\b/i.test(rule?.cssText || '')) {
// The platform exposes no matchMedia-equivalent for container queries,
// and selector matches do not prove that the element's query container
// satisfies the condition. Omit the group rather than report styles that
// are inactive for the current layout.
return false;
}
return true;
}
@@ -1341,7 +1373,7 @@ if (IS_BROWSER) {
function linkedStylesheetText() {
const parts = [];
const seen = new Set();
const appendRules = (rules) => {
const appendRules = (rules, requiresAppliedMatch = false) => {
for (const rule of rules) {
if (rule.styleSheet) {
appendSheet(rule.styleSheet);
@@ -1353,7 +1385,12 @@ if (IS_BROWSER) {
// Only declarations with a resolvable live host enter the corpus.
// Unresolvable selectors are uncertain, not evidence that a pattern
// rendered, and retaining them would leak unused CSS into findings.
if (matches?.length > 0) parts.push(cssText);
if (
matches?.length > 0
&& (!requiresAppliedMatch || styleRuleAppliesToLiveMatches(rule, matches))
) {
parts.push(cssText);
}
continue;
}
let nested = [];
@@ -1368,7 +1405,7 @@ if (IS_BROWSER) {
const isKeyframes = /^\s*@(?:-webkit-)?keyframes\b/i.test(cssText);
if (hasNestedRules && !isKeyframes) {
if (!conditionalCssRuleIsActive(rule)) continue;
appendRules(nested);
appendRules(nested, requiresAppliedMatch || isContainerCssRule(rule));
continue;
}
if (cssText) parts.push(cssText);
+47 -10
View File
@@ -8210,6 +8210,45 @@ if (IS_BROWSER) {
catch { return null; }
}
let containerProbeSequence = 0;
function isContainerCssRule(rule) {
return rule?.constructor?.name === 'CSSContainerRule'
|| /^\s*@container\b/i.test(rule?.cssText || '');
}
function styleRuleAppliesToLiveMatches(rule, matches) {
const style = rule?.style;
if (!style || !matches?.length || typeof getComputedStyle !== 'function') return false;
const sequence = ++containerProbeSequence;
const property = `--impeccable-container-probe-${sequence}-${Math.random().toString(36).slice(2)}`;
const value = `impeccable-container-active-${sequence}`;
const previousValue = style.getPropertyValue(property);
const previousPriority = style.getPropertyPriority(property);
try {
style.setProperty(property, value, 'important');
} catch {
return false;
}
const pseudoElements = [...new Set(
String(rule.selectorText || '').match(/::[a-zA-Z-]+(?:\([^)]*\))?/g) || [],
)];
try {
return matches.some(el => [null, ...pseudoElements].some(pseudo => {
try {
const computed = pseudo ? getComputedStyle(el, pseudo) : getComputedStyle(el);
return computed.getPropertyValue(property).trim() === value;
} catch {
return false;
}
}));
} finally {
if (previousValue) style.setProperty(property, previousValue, previousPriority);
else style.removeProperty(property);
}
}
function conditionalCssRuleIsActive(rule) {
const type = Number(rule?.type);
const constructorName = rule?.constructor?.name || '';
@@ -8225,13 +8264,6 @@ if (IS_BROWSER) {
try { return CSS.supports(condition); }
catch { return true; }
}
if (constructorName === 'CSSContainerRule' || /^\s*@container\b/i.test(rule?.cssText || '')) {
// The platform exposes no matchMedia-equivalent for container queries,
// and selector matches do not prove that the element's query container
// satisfies the condition. Omit the group rather than report styles that
// are inactive for the current layout.
return false;
}
return true;
}
@@ -8245,7 +8277,7 @@ if (IS_BROWSER) {
function linkedStylesheetText() {
const parts = [];
const seen = new Set();
const appendRules = (rules) => {
const appendRules = (rules, requiresAppliedMatch = false) => {
for (const rule of rules) {
if (rule.styleSheet) {
appendSheet(rule.styleSheet);
@@ -8257,7 +8289,12 @@ if (IS_BROWSER) {
// Only declarations with a resolvable live host enter the corpus.
// Unresolvable selectors are uncertain, not evidence that a pattern
// rendered, and retaining them would leak unused CSS into findings.
if (matches?.length > 0) parts.push(cssText);
if (
matches?.length > 0
&& (!requiresAppliedMatch || styleRuleAppliesToLiveMatches(rule, matches))
) {
parts.push(cssText);
}
continue;
}
let nested = [];
@@ -8272,7 +8309,7 @@ if (IS_BROWSER) {
const isKeyframes = /^\s*@(?:-webkit-)?keyframes\b/i.test(cssText);
if (hasNestedRules && !isKeyframes) {
if (!conditionalCssRuleIsActive(rule)) continue;
appendRules(nested);
appendRules(nested, requiresAppliedMatch || isContainerCssRule(rule));
continue;
}
if (cssText) parts.push(cssText);
+7 -5
View File
@@ -1405,11 +1405,12 @@ describe('detectUrl — browser-only fixtures', () => {
})));
const linkedFindings = await linkedPage.evaluate(() => window.impeccableDetect({ serialize: true })
.flatMap(group => group.findings || []));
const inactiveContainerBackground = await linkedPage.$eval(
'.inactive-container-stripes',
element => getComputedStyle(element).backgroundImage,
);
assert.equal(inactiveContainerBackground, 'none');
const containerBackgrounds = await linkedPage.evaluate(() => ({
inactive: getComputedStyle(document.querySelector('.inactive-container-stripes')).backgroundImage,
active: getComputedStyle(document.querySelector('.active-container-halo')).backgroundImage,
}));
assert.equal(containerBackgrounds.inactive, 'none');
assert.match(containerBackgrounds.active, /radial-gradient/i);
const grids = linkedFindings.filter(finding => finding.type === 'codex-grid-background');
assert.equal(grids.length, 1, JSON.stringify({ linkedFindings, linkedCssom }));
assert.equal(grids[0].severity, 'advisory');
@@ -1419,6 +1420,7 @@ describe('detectUrl — browser-only fixtures', () => {
true,
JSON.stringify({ linkedFindings, linkedCssom }),
);
assert.equal(linkedFindings.some(finding => finding.type === 'radial-halo'), true);
assert.equal(linkedFindings.some(finding => finding.type === 'repeating-stripes-gradient'), false);
assert.equal(linkedFindings.some(finding => finding.type === 'layout-transition'), false);
await linkedPage.close();
+15
View File
@@ -7,6 +7,11 @@
}
}
body {
background: #111;
color: #fff;
}
/* A valid hostless pseudo-element selector cannot be queried through the DOM
selector API. It must remain in the corpus rather than count as unused. */
main > ::before {
@@ -45,10 +50,20 @@ main > ::before {
width: 240px;
}
.container-query-host-active {
width: 960px;
}
@container (width > 900px) {
.inactive-container-stripes {
background: repeating-linear-gradient(45deg, #eee, #eee 10px, #fafafa 10px, #fafafa 20px);
}
.active-container-halo {
width: 640px;
height: 400px;
background: radial-gradient(circle, rgba(80, 111, 255, 0.85), transparent 70%);
}
}
.unused-linked-transition {
+3
View File
@@ -14,6 +14,9 @@
<div class="container-query-host">
<div class="inactive-container-stripes">Inactive container-query stripes</div>
</div>
<div class="container-query-host container-query-host-active">
<div class="active-container-halo">Active container-query halo</div>
</div>
</main>
</body>
</html>