v158 · cssom · lifecycle
Pending, loaded, failed
An @import's styleSheet is not one value — it is a lifecycle. The instant the rule is inserted the fetch has not finished; later it either loads or fails. This page inserts real imports, reads styleSheet synchronously in the same task, then keeps reading on a timer and timestamps every transition — alongside the actual HTTP status of the same URL, fetched in parallel, so you can see what the network really said.
CSSOM types the attribute CSSStyleSheet?: while nothing is loaded — pending, failed, or never fetched — the conforming value is null. Chromium already returns null while a fetch is pending; the Chrome 158 change (CSSImportRuleStyleSheetNullable) extends that honesty to failed imports, which older builds report as a phantom empty CSSStyleSheet. Run the failed case below and the verdict tells you which build you are on. No flag exists on pre-158 builds — the runtime feature lands with the change itself.
Run a lifecycle
- —Press a button; every reading below is live, none are scripted outcomes.
No run yet.
what to look for
Pending: the very first reading, taken synchronously in the task that inserted the rule, should be null — the fetch cannot have completed yet. Loaded: the value flips to a real CSSStyleSheet whose cssRules you can count. Failed: the spec keeps it null forever; pre-158 Chromium instead flips it to an empty phantom sheet at the moment the 404 lands — an object with cssRules.length === 0 that never corresponded to any fetched bytes. The parallel fetch() row shows the genuine HTTP status so the failure is never taken on faith.
code path
const style = document.createElement("style");
style.textContent = `@import url("./missing.css");`;
document.head.append(style);
style.sheet.cssRules[0].styleSheet; // null — still pending (same task)
// ... after the fetch 404s:
style.sheet.cssRules[0].styleSheet; // Chrome 158+: still null
// pre-158: CSSStyleSheet with 0 rules