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.
.scope-card-stage — has its own --card-bg, --card-pad, --card-accent on top of the inherited globals.
for…of over the CSSStyleDeclaration
| token | resolved 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.