A content-visibility: auto optimization can look correct while the browser is using two materially different layout-size states for the same element. Offscreen, the user agent may skip the element’s contents and apply size containment; when that happens, contain-intrinsic-size supplies the size used for layout. Once the element becomes relevant, its contents participate in rendering again and its content-derived size can replace that provisional size. The CSS Containment specification explicitly warns that this transition can move layout or scrollbar position when an element depends on its contents for size. CSS Containment Level 2 defines the skipped-content lifecycle; CSS Box Sizing Level 4 now defines the remembered-size mechanics of contain-intrinsic-size: auto.
That makes a screenshot, a successful page load, or even a correct final getBoundingClientRect().height insufficient acceptance evidence. The operational question is narrower: what size did layout reserve while the subtree was skipped, what changed when it became relevant, what moved downstream, and what size was remembered when it became skippable again? getBoundingClientRect() reports an element’s viewport-relative border-box geometry, with scrolling reflected in its coordinates, so the fixture can record geometry without pretending that one rectangle alone proves lifecycle state. MDN documents those semantics explicitly.
Define the contract before measuring the browser
The acceptance contract is not “content-visibility works.” It is a state transition with measurable boundaries.
For the primary case, create a block whose actual child content has a deterministic 1000px block size. Give the container content-visibility: auto and contain-intrinsic-size: auto none auto 600px. The four-component shorthand means an auto none intrinsic inline size and an auto 600px intrinsic block size. MDN describes auto <length> as using a remembered normally rendered size, when one exists and the element is currently skipping its contents, otherwise falling back to the supplied length. MDN’s current contain-intrinsic-size reference and the current CSS Sizing draft agree on that central behavior.
The initial prediction is therefore conditional, not absolute. If the element is genuinely offscreen, its proximity has been determined, it is skipping its contents, and it has no remembered size, the fixture predicts an approximately 600px container height. When the element becomes relevant and is no longer skipping, the fixture predicts approximately 1000px because the child’s actual block size determines the container. After sufficient rendering opportunities have occurred to establish a remembered size, moving the element offscreen again predicts approximately 1000px even while skipped. CSS Sizing Level 4 says the remembered size is element state and describes recording it when ResizeObserver observations are determined and delivered while auto is present and the element is not size-contained.
Do not turn those predictions into fabricated observations. Every evidence row starts NOT RUN. A browser/version row becomes evidence only after an actual execution writes its result.
The authoritative lifecycle is more nuanced than “offscreen means skipped.” Under content-visibility: auto, the browser decides whether the element is relevant to the user. The current editor’s draft defines proximity to the viewport using the paint-containment box and a user-agent-defined margin; it suggests 50% as a reasonable margin but does not make that value universal. Focus, selection and top-layer state can also make an element relevant. Consequently, “6000px below the viewport” is fixture setup, not proof of skipping. The fixture must record lifecycle state independently.
There is another lifecycle detail that matters directly to this test. The current specification says an initial proximity determination takes effect in the same frame, while later proximity changes take effect at the next rendering opportunity. It also specifies that scrolling operations targeting an element that is still skipping its contents determine its size and location while size containment remains active; after it stops skipping, a size change can mean that the final alignment is no longer exactly what the scrolling operation requested. That is precisely the boundary this fixture is designed to expose.
Define four evidence quantities:
Evidence field | What it answers | Acceptance use |
target.height | What block size is layout giving the container now? | Distinguishes fallback reservation from content-derived size. |
tail.documentTop | Where did an independent element after the target end up in document coordinates? | Shows downstream layout displacement. |
document.scrollHeight | What total scroll extent does the document expose? | Shows whether the reserved-versus-actual difference propagated into scrollable geometry. |
scrollY | Did the viewport itself move while lifecycle/layout settled? | Diagnostic only; do not require one universal value because scroll behavior and anchoring can vary. |
getBoundingClientRect() is appropriate for the target and tail rectangles because MDN defines it as returning border-box size and viewport-relative position. Adding window.scrollY to rect.top converts the sampled top position to document-relative coordinates for this controlled document.
Lifecycle evidence comes from two independent browser-facing signals. First, contentvisibilityautostatechange reports transitions for elements using content-visibility: auto; its event object exposes skipped, where true means the contents are being skipped. Second, checkVisibility({contentVisibilityAuto: true}) can be used as a sample-time check that takes content-visibility: auto skipping into account. Neither should be silently replaced by “the element looked offscreen.” MDN documents the event and its skipped property.
The failure boundary is now precise. A 600px initial rectangle is not accepted unless the same sample establishes that the auto element is actually skipped. A 1000px final rectangle is not accepted unless it is associated with a relevant/non-skipped state. A changed final rectangle without a downstream geometry measurement is incomplete. And a lifecycle event without geometry is also incomplete: it proves a state transition, not what that transition did to layout.
Capture the baseline and pin fixture ownership
Use a disposable directory, synthetic markup and a browser installed by the test tool. Do not point this experiment at a production origin, application account or customer page. The code below has no network dependency after the browser package is installed and contains no credentials or real data.
The first reproducibility requirement is to capture the environment at execution, rather than hard-code an article author’s machine as though it were yours. Install Playwright as an exact dependency and keep the generated lockfile:
mkdir cv-layout-lab
cd cv-layout-lab
npm init -y
npm install --save-dev --save-exact @playwright/test
npx playwright install chromium
node --version
npm --version
npm ls @playwright/testCommit or archive package.json and package-lock.json with the evidence. The runner below additionally records process.platform, CPU architecture, Playwright package version, the browser-reported version, user agent, viewport and headless mode. That is stronger runtime version pinning than writing “tested in Chrome” in a ticket.
The baseline owner should also record the fixture digest. For example:
shasum -a 256 fixture.html run.mjs package.json package-lock.jsonOn Linux systems where sha256sum is standard instead:
sha256sum fixture.html run.mjs package.json package-lock.jsonThese commands are procedures, not execution transcripts. This article has not run them and therefore supplies no fabricated hash values.
The baseline should answer three questions before any intervention: did the intended declarations compute, did the auto case actually enter a skipped state, and did the browser reserve the predicted initial geometry? If any answer is unknown, stop interpreting downstream differences.
That principle resembles the staging discipline in Python tarfile Errors: Validate Staged Extraction: a partial result is not equivalent to a completed and independently validated state. The tarfile article applies that idea to filesystem staging, not CSS, and it is not evidence for browser behavior here. Its relevance is methodological only.
The fixture owner should keep three cases under the same markup:
Estimate case. content-visibility: auto plus a 600px intrinsic block-size fallback around 1000px actual content.
Accurate-estimate control. content-visibility: auto plus a 1000px fallback around the same 1000px actual content. It should still undergo the skip/relevance lifecycle, but the geometry should have little or no reason to move because provisional and actual sizes match.
Visible negative control. content-visibility: visible with the same intrinsic-size declaration and the same 1000px child. contain-intrinsic-size supplies an explicit intrinsic size when the element is subject to the relevant size-containment behavior; merely declaring it should not turn a normally laid-out visible subtree into a 600px placeholder. MDN describes content-visibility: visible as normal layout/rendering, while auto introduces layout, style and paint containment and can skip contents when they are not relevant.
Do not use the visible case merely to prove that a second page also loaded. Its purpose is causal discrimination. If both the auto/600 case and visible/600 case initially measure 600px, the hypothesized skip-size mechanism has not been isolated. Investigate the fixture before drawing conclusions.
Build the disposable fixture and reproduce the misleading success
Create fixture.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>content-visibility intrinsic-size fixture</title>
<script>
const requestedCase =
new URLSearchParams(location.search).get("case") || "estimate";
document.documentElement.dataset.case = requestedCase;
</script>
<style>
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
}
#lead {
height: 6000px;
}
#target {
width: 100%;
margin: 0;
padding: 0;
border: 0;
}
.payload {
height: 1000px;
}
#tail {
height: 80px;
}
#after {
height: 1200px;
}
html[data-case="estimate"] #target {
content-visibility: auto;
contain-intrinsic-size: auto none auto 600px;
}
html[data-case="accurate"] #target {
content-visibility: auto;
contain-intrinsic-size: auto none auto 1000px;
}
html[data-case="visible"] #target {
content-visibility: visible;
contain-intrinsic-size: auto none auto 600px;
}
</style>
</head>
<body>
<div id="lead"></div>
<section id="target">
<div class="payload"></div>
</section>
<div id="tail"></div>
<div id="after"></div>
<script>
const target = document.querySelector("#target");
const tail = document.querySelector("#tail");
window.lifecycle = [];
target.addEventListener("contentvisibilityautostatechange", event => {
window.lifecycle.push({
type: "contentvisibilityautostatechange",
skipped: event.skipped,
time: performance.now()
});
});
window.sample = label => {
const targetRect = target.getBoundingClientRect();
const tailRect = tail.getBoundingClientRect();
const style = getComputedStyle(target);
let contentVisibilityAware = null;
if (typeof target.checkVisibility === "function") {
contentVisibilityAware = target.checkVisibility({
contentVisibilityAuto: true
});
}
return {
label,
case: document.documentElement.dataset.case,
scrollY: window.scrollY,
scrollHeight: document.documentElement.scrollHeight,
target: {
top: targetRect.top,
bottom: targetRect.bottom,
height: targetRect.height,
documentTop: targetRect.top + window.scrollY
},
tail: {
top: tailRect.top,
documentTop: tailRect.top + window.scrollY
},
computed: {
contentVisibility: style.contentVisibility,
containIntrinsicSize: style.containIntrinsicSize,
containIntrinsicWidth: style.containIntrinsicWidth,
containIntrinsicHeight: style.containIntrinsicHeight
},
contentVisibilityAware,
lifecycle: [...window.lifecycle]
};
};
window.__fixtureReady = true;
</script>
</body>
</html>The script sets the case on <html> before the body is parsed. That detail matters. Applying content-visibility: auto only after the browser has already normally rendered the 1000px target could contaminate the experiment by giving the element an opportunity to acquire a last remembered size before the supposed “first skipped” measurement. CSS Sizing defines remembered size as element state, so an accidentally pre-rendered element can make a fallback-size test appear mysteriously accurate.
That is the false-positive hypothesis: the fixture says “600px fallback,” but the browser measures 1000px offscreen because the element already acquired a remembered size. The correct response is not to declare the specification wrong. First prove whether the intended first lifecycle state was actually reached.
Now create run.mjs:
import { chromium } from "@playwright/test";
import { createRequire } from "node:module";
import { pathToFileURL } from "node:url";
import { resolve } from "node:path";
import fs from "node:fs";
import os from "node:os";
const require = createRequire(import.meta.url);
const playwrightVersion =
require("@playwright/test/package.json").version;
const fixtureURL = pathToFileURL(resolve("fixture.html"));
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1280, height: 800 },
deviceScaleFactor: 1
});
const page = await context.newPage();
async function frames(count = 2) {
await page.evaluate(async n => {
for (let i = 0; i < n; i++) {
await new Promise(resolve =>
requestAnimationFrame(() => resolve())
);
}
}, count);
}
async function runCase(name) {
await page.goto${fixtureURL.href}?case=${name});
await page.waitForFunction(() => window.__fixtureReady === true);
await frames(2);
const baseline = await page.evaluate(() =>
window.sample("baseline-offscreen")
);
const sameTaskAfterScroll = await page.evaluate(() => {
const target = document.querySelector("#target");
const rect = target.getBoundingClientRect();
const documentTop = rect.top + window.scrollY;
window.scrollTo({
top: Math.max(0, documentTop - 100),
behavior: "instant"
});
return window.sample("scroll-issued-same-task");
});
if (name !== "visible") {
await page.waitForFunction(() => {
const target = document.querySelector("#target");
return target.checkVisibility({
contentVisibilityAuto: true
});
});
}
await frames(3);
const relevant = await page.evaluate(() =>
window.sample("relevant-settled")
);
windowResult:
if (name === "visible") {
return {
name,
baseline,
sameTaskAfterScroll,
relevant,
offscreenAgain: null
};
}
await page.evaluate(() =>
window.scrollTo({ top: 0, behavior: "instant" })
);
await page.waitForFunction(() => {
const target = document.querySelector("#target");
return !target.checkVisibility({
contentVisibilityAuto: true
});
});
await frames(3);
const offscreenAgain = await page.evaluate(() =>
window.sample("offscreen-again")
);
return {
name,
baseline,
sameTaskAfterScroll,
relevant,
offscreenAgain
};
}
const envPage = await context.newPage();
await envPage.goto${fixtureURL.href}?case=visible);
const environment = {
capturedAt: new Date().toISOString(),
platform: process.platform,
arch: process.arch,
osRelease: os.release(),
node: process.version,
playwright: playwrightVersion,
browser: await browser.version(),
browserExecutable: chromium.executablePath(),
userAgent: await envPage.evaluate(() => navigator.userAgent),
viewport: { width: 1280, height: 800 },
deviceScaleFactor: 1,
headless: true
};
await envPage.close();
const cases = [];
for (const name of ["estimate", "accurate", "visible"]) {
cases.push(await runCase(name));
}
const evidence = { environment, cases };
fs.writeFileSync(
"evidence.json",
JSON.stringify(evidence, null, 2) + "\n"
);
console.log(JSON.stringify(evidence, null, 2));
await browser.close();Run it with:
node run.mjs | tee transcript.jsonThe duplicated console output and evidence.json are useful independent artifacts: compare their hashes after execution instead of manually copying values into a ticket.
The “misleading success” is the relevant-settled sample by itself. If it reports height: 1000, somebody reviewing only that row might conclude that layout sizing was always correct. That row says nothing about whether the preceding skipped state reserved 600px and moved the tail by 400px when relevance changed.
This distinction is similar to the evidence warning in CloudFront Cache Isolation: Audit Personalized Responses: one client-visible signal is not universal proof of the underlying state. That page concerns CDN request/cache evidence, not browser layout, so use it only as an example of the observational discipline.
Run the controls and compare competing timelines
Before accepting the main case, write down the predicted timelines. That makes it harder to retrofit the explanation to whatever numbers appear.
For estimate, the prediction is:
baseline offscreen, skipped
target ≈ 600
tail based on 600 reservation
scroll issued, same JavaScript task
transition may not yet have taken effect
target can still reflect skipped sizing
next rendering opportunities
target becomes relevant / not skipped
target ≈ 1000
tail documentTop increases ≈ 400
document scrollHeight increases ≈ 400
offscreen again after normal rendering
target skipped again
remembered size may now be ≈ 1000
target should no longer need the original 600 fallbackThis ordering is grounded in the current containment draft: non-initial proximity changes take effect at a later rendering opportunity, and scrolling an element while it is still skipped uses its size-contained size for the scrolling operation. Once the element is in view, removal of skipped-size containment may alter its size.
For accurate, lifecycle state should still change, but layout geometry is predicted to remain nearly invariant because both fallback and actual block size are 1000px:
baseline skipped ≈ 1000
relevant ≈ 1000
offscreen remembered ≈ 1000
tail document delta ≈ 0
scrollHeight delta ≈ 0That case is important because it separates “a lifecycle transition occurred” from “a lifecycle transition necessarily creates a layout shift.” The specification warns that size changes can cause jumping when the skipped size differs from content-derived size; it does not say every content-visibility: auto transition must move geometry.
For visible, there should be no auto-skipping lifecycle at all:
baseline ≈ 1000
after scroll ≈ 1000
relevant-settled ≈ 1000
auto-state events none expected
tail geometry delta ≈ 0The control establishes whether the 600px result is caused by the skipped size-containment path rather than by the child, viewport, or measurement code.
A meaningful negative control must be capable of falsifying the preferred explanation. That is why simply changing the target’s background color is not useful. It changes an unrelated variable. Changing content-visibility from auto to visible removes the lifecycle mechanism under test while holding markup and actual content height constant.
The same controlled contrast is what makes HTTP Cache Revalidation: Validate Conditional Requests useful here: its test cases isolate different validation mechanisms instead of calling every successful HTTP response equivalent. It remains a separate HTTP experiment and supplies no technical authority for CSS.
Treat scrollY carefully. Window.scrollTo({behavior: "instant"}) requests immediate scrolling rather than smooth animation, according to MDN, but the fixture should not elevate one exact post-layout scrollY delta into a cross-browser specification requirement. MDN documents the scrolling API; the CSS contract here is the skipped/relevant sizing transition, not a universal scroll-anchoring algorithm.
For that reason, use the tail’s document-relative position as the principal displacement oracle:
tailDocumentTop =
tail.getBoundingClientRect().top + window.scrollY;If the target changes from 600px to 1000px and nothing else between the target and tail changes, a roughly 400px increase in tail.documentTop identifies the layout-size boundary directly. Whether the viewport also moves by 0px, 400px or another amount is useful browser evidence but not the sole acceptance condition.
This separation also protects against a common diagnostic mistake: interpreting viewport-relative rect.top as document movement. getBoundingClientRect() coordinates change when the viewport scrolls because they are viewport-relative. The document-relative reconstruction is therefore essential when comparing before/after positions around an explicit scroll.
Measure lifecycle, output and independently observable effects
The core evidence review should distinguish identity, state, geometry and effects.
Identity is the fixture hash, package lock, URL case, browser executable and runtime metadata.
State is whether content-visibility computed to the intended value and whether the auto case is being skipped at the sampled boundary.
Geometry is the target rectangle and downstream tail position.
Effect is the resulting document scroll extent and any viewport displacement.
Those categories prevent a “correct output” from swallowing the rest of the lifecycle. For example, suppose the estimate case produces this hypothetical pattern:
baseline:
skipped = true
target.height = 600
tail.documentTop = 6600
relevant-settled:
skipped = false
target.height = 1000
tail.documentTop = 7000Those numbers are predicted illustration, not an execution transcript. Their value is to show what the oracle would mean: the target’s own size increased by 400px and an independent downstream element moved by the same amount in document space. If scrollHeight also grows by roughly 400px, three separate geometry observations tell the same story.
Now consider another hypothetical result:
baseline:
skipped = false
target.height = 1000That is not successful confirmation of the remembered-size feature. The fixture never proved the first skipped state. The correct disposition is HOLD or repair the fixture placement/environment, even though the final geometry looks visually ideal.
A third hypothetical result is subtler:
baseline:
skipped = true
target.height = 1000Possible explanations include an already-established remembered size, a browser implementation difference, or contamination of the intended first-render lifecycle. Do not choose among them from one rectangle. Review event history, confirm whether the DOM node previously rendered normally, rerun in a fresh page/context, and compare against the accurate and visible controls.
The specification itself gives a reason for this caution. The auto remembered size is state attached to the element, not merely a textual interpretation of the currently generated CSS box. CSS Sizing even states that the remembered size can survive transitions such as going to and from display: none while the auto keyword remains.
Independent observation also means avoiding geometry probes inside the skipped subtree unless the test specifically studies forced rendering. The containment specification notes that when script requests layout information from within skipped contents, the user agent may have to perform previously skipped rendering work to return a correct value. A probe can therefore perturb the state it intends to measure. The fixture reads the container’s border box and an outside tail element instead of calling getBoundingClientRect() on .payload.
That distinction is fundamental for CSS failure reproduction: the diagnostic code itself must not casually cross the optimization boundary.
The browser’s contentvisibilityautostatechange event is another reason not to use visual output as the lifecycle oracle. MDN says the event fires when a content-visibility: auto element starts or stops being relevant and having its rendering work skipped, and event.skipped reports the resulting skipped state. Record those transitions beside geometry rather than reducing them to a console sentence such as “section appeared.”
When comparing browsers, preserve the raw rows rather than normalizing unexpected values away. The CSSWG draft allows a user-agent-defined proximity margin, which means the exact scroll point at which relevance flips is not necessarily portable. What should remain comparable is the relationship among observed state and geometry: when the implementation reports the element skipped, what size is the outer box using; when it reports the element relevant, what size is it using; and what moved across that transition?
Keep an auditable ledger, repair the boundary and gate the release
Convert evidence.json into an explicit case ledger. A compact record can look like this:
Case | Boundary | Expected | Actual | Status |
estimate | baseline skip | skipped; ~600px | NOT RUN | Pending |
estimate | relevant settle | not skipped; ~1000px | NOT RUN | Pending |
estimate | downstream displacement | tail Δ ~400px | NOT RUN | Pending |
estimate | second skip | skipped; remembered ~1000px | NOT RUN | Pending |
accurate | baseline/relevant | ~1000px both | NOT RUN | Pending |
accurate | downstream displacement | ~0px | NOT RUN | Pending |
visible | all sampled geometry | ~1000px | NOT RUN | Pending |
visible | auto lifecycle | no auto-skip transition | NOT RUN | Pending |
Add these fields to each completed run: fixture SHA-256, capturedAt, operating-system identifier, Node version, Playwright version, browser version, browser executable, viewport, device scale factor, headless/headed mode, exact case URL, computed content-visibility, computed intrinsic-size values, lifecycle event list and raw before/after geometry.
The ledger is not bureaucratic decoration. It is what prevents a later browser upgrade from being compared to an undocumented earlier state. Refonte Learning’s PostgreSQL SKIP LOCKED: Test Queue Fairness and Starvation uses a similarly narrow fixture/evidence approach and explicitly starts its observations as unexecuted rather than fabricating results. The database mechanism is unrelated, but the evidentiary convention is directly worth preserving.
A repair should address the violated boundary, not suppress the symptom. If the target really transitions from a 600px skipped reservation to 1000px actual content and that 400px displacement is unacceptable, candidate repairs include deriving a more representative intrinsic block-size estimate or using auto so a subsequently remembered real size can replace the first fallback. MDN explicitly describes the purpose of auto <length> as allowing the remembered normally rendered size to be used during later skipped periods.
Setting contain-intrinsic-size does not guarantee that layout shift disappears. The first skipped render still needs a fallback if no remembered size exists, and an inaccurate fallback can differ from the eventual rendered size. MDN’s own example notes that a difference between placeholder and calculated size may force new layout and accompanying positional changes.
Likewise, do not assume that remembered size remains correct after the subtree’s content changes while skipped. The non-normative containment guidance explicitly conditions exactness on the element not having changed size while skipped. When content length, responsive width, fonts, writing mode or other sizing inputs can change, extend the fixture rather than promoting the simple fixed-height case into a universal guarantee.
After a repair, rerun all three cases from fresh browser contexts. The regression check is not only that the estimate case improved. The visible control must remain content-sized, and the auto case must still demonstrably enter skipped state; otherwise a “repair” could simply have disabled the optimization being validated.
Use this decision matrix:
Decision | Required evidence | Release meaning |
ACCEPT | Required browser/version rows are pinned; baseline skip is proven; provisional and relevant geometry match the declared contract; controls discriminate the mechanism; resulting displacement is within the product’s reviewed tolerance. | The candidate behavior is understood and accepted for the tested scope. |
REPAIR | The mechanism is proven, but the intrinsic estimate, containment boundary or fixture/application CSS causes unacceptable displacement. | Change the CSS/layout contract and rerun the same ledger. |
HOLD | Skip state cannot be proved, browser/runtime is unpinned, controls contradict the hypothesis, an expected API is unsupported in a required environment, or results cannot be reconciled. | Do not infer acceptance from final appearance. Expand or repair the evidence. |
RECONCILE | Different runs, browser versions or candidate revisions produced incompatible evidence, or a prior candidate has already generated artifacts/results that cannot safely be compared with the current fixture. | Establish which fixture/runtime/result belongs to which revision before retrying or accepting. |
The distinction between a returned signal and completed effects also appears in When asyncio.gather Raises, Which Tasks Are Still Running?: an exception delivered to a caller does not, by itself, describe every sibling task’s terminal state. Again, that Python article does not prove anything about CSS; the useful analogy is the insistence on observing the lifecycle objects and effects separately.
For CSS release-gate validation, the equivalent error is “the page reached its final height, therefore the optimization was safe.” The final height is only one point in the timeline. Acceptance needs the skipped reservation, transition state, downstream geometry, second skipped state and controls.
Document limits, ownership and the Refonte Learning connection
This fixture deliberately proves less than a full application.
It uses one block-flow container with a fixed-height synthetic child. It does not establish equivalent behavior for grid intrinsic sizing, multicolumn layout, replaced elements, writing-mode changes, dynamic fonts, images whose dimensions arrive later, nested content-visibility, transforms, sticky positioning, scroll snapping, browser zoom, mobile visual viewports or application frameworks. MDN specifically notes that explicit intrinsic sizes can interact differently with grid and multicolumn layout, including a meaningful distinction between none and 0px; those cases deserve their own fixtures rather than extrapolation from this one.
It also does not assert one universal viewport distance at which content-visibility: auto must switch state. The current CSS Containment editor’s draft uses a user-agent-defined proximity margin and allows the browser to begin preparing content before it reaches the literal viewport. The fixture therefore validates the state the browser reports, not a made-up “800px from viewport” contract.
content-visibility remains defined in CSS Containment Level 2, but the detailed contain-intrinsic-* property and last-remembered-size algorithms are now found in CSS Box Sizing Level 4. MDN’s current contain-intrinsic-size page points to Box Sizing Level 4 as its formal specification. CSS Containment Level 2 alone does not provide the full specification for remembered-size behavior.
MDN currently labels content-visibility as Baseline 2024 and notes that support can vary in older browsers, while contain-intrinsic-size is listed as broadly available across browsers from September 2023. Those labels are useful compatibility context, not a substitute for the required release matrix. Pin and execute the actual browsers your release supports.
Ownership should therefore be explicit. A component or layout owner defines the acceptable displacement and intrinsic-size strategy. A test owner maintains the fixture, browser versions and ledger. A release owner decides whether required environment rows satisfy the gate. If the product adds a new layout mechanism (grid, nested scrolling, responsive height, animation or framework virtualization), the owner should extend the fixture before relying on earlier evidence.
The operational rule is compact:
Prove the element is skipped.
Measure the size reserved while skipped.
Move it across the relevance boundary.
Measure the real size and downstream geometry.
Move it offscreen again.
Check the remembered-size state.
Run controls that can falsify the explanation.
Preserve runtime, fixture and raw evidence.
Then decide ACCEPT, REPAIR, HOLD or RECONCILE.
That is the difference between a CSS demo and release acceptance. content-visibility: auto is allowed to skip rendering work; contain-intrinsic-size gives layout a substitute size under containment; auto can later reuse a remembered normally rendered size. None of those statements tells you what your particular page did unless you capture the lifecycle and geometry together.
For engineers who want broader structured frontend practice, the current Refonte Learning Frontend Development Program page lists HTML5/CSS3, responsive web design, advanced CSS techniques, JavaScript, React, Git, accessibility and performance optimization. It currently states a four-month period and a 10–12-hours-per-week dedication. The page does not establish that this specific content-visibility fixture, Playwright procedure or release-gate method is part of the curriculum. The program can be reviewed as a frontend-development learning path, while this playbook remains an independently reproducible CSS lifecycle-validation exercise.
