v158 · css · cssom

CSSImportRule.styleSheet

For as long as Chromium has had a CSSOM, CSSImportRule.styleSheet was typed non-nullable — so an @import that failed to load handed script a phantom empty CSSStyleSheet instead of admitting there was nothing there. Chrome 158 makes the attribute nullable, as CSSOM declares it (CSSStyleSheet?): when an @import has no associated style sheet — its supports() condition is false, it has not finished loading, or the fetch failed — styleSheet returns null, matching Firefox and Safari.

concepts

  1. The supports() gate

    Write a real @import … supports(…) into a live style element, then read the rule back out of the CSSOM: supportsText, styleSheet, and whether the imported sheet actually painted the proof card. Every condition is evaluated by this browser, and the readout tells you whether you are looking at the Chrome 158 nullable behavior or an older one.

  2. Import tree inspector

    Walk document.styleSheets recursively and table every CSSImportRule this page can reach — href, media, layerName, supportsText, and what styleSheet returned, including the cross-origin Google Fonts import whose rules this origin is not allowed to read. Add media-gated, layered, failing, and conditional imports and watch the tree change.

  3. Pending, loaded, failed

    An @import's styleSheet has a lifecycle: read it the instant the rule is inserted, watch the value settle as the fetch completes, and compare a sheet that loads against one that 404s. A millisecond-stamped timeline records exactly what this browser returned at every step — null, a real sheet, or the pre-158 phantom empty one.

  4. The null check your traversal needs

    The compat story, run live: a stylesheet auditor written before Chrome 158 chains straight into rule.styleSheet.cssRules and works by accident on the phantom sheet; on a nullable build the same code throws a real TypeError. Run the legacy auditor and the null-safe one against the same import tree and compare what actually happens in this browser.

why it shipped

CSS Cascade 5 says a conditional @import whose supports() condition is false is never fetched and has no associated style sheet, and CSSOM types the attribute CSSStyleSheet? accordingly. Chromium's IDL predated that and promised a non-null sheet, so the engine manufactured one — a detached, empty CSSStyleSheet — for imports that failed to load. Firefox has returned null for years and WebKit made its IDL nullable in March 2025, so the phantom sheet was a Chromium-only quirk that code could silently depend on. The change aligns the getter with the spec and the other engines; the one migration note is that code which chained rule.styleSheet.cssRules without a null check may now need one — that is exactly what the fourth concept demonstrates.

the API

// CSSOM: https://drafts.csswg.org/cssom/#the-cssimportrule-interface
interface CSSImportRule : CSSRule {
  readonly attribute USVString href;
  [SameObject, PutForwards=mediaText] readonly attribute MediaList media;
  [SameObject] readonly attribute CSSStyleSheet? styleSheet;  // <- nullable in 158
  readonly attribute CSSOMString? layerName;
  readonly attribute CSSOMString? supportsText;
};

const rule = sheet.cssRules[0];           // a CSSImportRule
rule.styleSheet?.cssRules.length ?? 0;    // null-safe from Chrome 158 on

references