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

  1. —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

see also