demo · v141

Design Token Extractor

Before Chrome 141, harvesting all design tokens off an element meant crawling document.styleSheets, filtering same-origin sheets, walking CSSStyleRule lists, and matching --* by hand. Now getComputedStyle(el) just iterates them — with values fully cascaded and resolved for the actual element you're standing on.

checking support…

.scope-card-stage — has its own --card-bg, --card-pad, --card-accent on top of the inherited globals.

— tokens discovered via for…of over the CSSStyleDeclaration
tokenresolved value

the old way

// Pre-Chrome 141: hand-roll the crawl
const tokens = new Set();
for (const sheet of document.styleSheets) {
  let rules;
  try { rules = sheet.cssRules; } catch { continue; }  // CORS
  for (const rule of rules) {
    if (!(rule instanceof CSSStyleRule)) continue;
    for (let i = 0; i < rule.style.length; i++) {
      const prop = rule.style[i];
      if (prop.startsWith("--")) tokens.add(prop);
    }
  }
}
// …and you still don't have the cascaded value for any given element.

the new way

// Chrome 141 onwards: getComputedStyle enumerates --* too
const cs = getComputedStyle(element);
const tokens = [];
for (const prop of cs) {
  if (prop.startsWith("--")) tokens.push([prop, cs.getPropertyValue(prop).trim()]);
}
// values are fully resolved against the cascade for `element`.

why this angle

The Tyler Gaw "all custom properties on a page" post is the canonical pre-141 workaround — and it can't reach across origins, doesn't pick up runtime style.setProperty() mutations, and most importantly can't tell you the cascaded value for a specific element. Design-token tools (Style Dictionary inspectors, theme editors, Figma sync plugins) all want the cascaded value for the element they're inspecting. That's exactly what this fix gives them.

see also