Wir können JavaScript verwenden, um den Wert einer benutzerdefinierten CSS-Eigenschaft abzurufen. Robin hat eine detaillierte Erklärung dazu in Get a CSS Custom Property Value with JavaScript geschrieben. Zur Wiederholung, sagen wir, wir haben eine einzelne benutzerdefinierte Eigenschaft auf dem HTML-Element deklariert
html {
--color-accent: #00eb9b;
}
In JavaScript können wir mit getComputedStyle und getPropertyValue auf den Wert zugreifen
const colorAccent = getComputedStyle(document.documentElement)
.getPropertyValue('--color-accent'); // #00eb9b
Perfekt. Jetzt haben wir Zugriff auf unsere Akzentfarbe in JavaScript. Wissen Sie, was cool ist? Wenn wir diese Farbe in CSS ändern, wird sie auch in JavaScript aktualisiert! Praktisch.
Was passiert aber, wenn wir nicht nur auf eine, sondern auf viele Eigenschaften in JavaScript zugreifen müssen?
html {
--color-accent: #00eb9b;
--color-accent-secondary: #9db4ff;
--color-accent-tertiary: #f2c0ea;
--color-text: #292929;
--color-divider: #d7d7d7;
}
Am Ende haben wir JavaScript, das so aussieht
const colorAccent = getComputedStyle(document.documentElement).getPropertyValue('--color-accent'); // #00eb9b
const colorAccentSecondary = getComputedStyle(document.documentElement).getPropertyValue('--color-accent-secondary'); // #9db4ff
const colorAccentTertiary = getComputedStyle(document.documentElement).getPropertyValue('--color-accent-tertiary'); // #f2c0ea
const colorText = getComputedStyle(document.documentElement).getPropertyValue('--color-text'); // #292929
const colorDivider = getComputedStyle(document.documentElement).getPropertyValue('--color-text'); // #d7d7d7
Wir wiederholen uns viel. Wir könnten jede dieser Zeilen verkürzen, indem wir die gemeinsamen Aufgaben in eine Funktion auslagern.
const getCSSProp = (element, propName) => getComputedStyle(element).getPropertyValue(propName);
const colorAccent = getCSSProp(document.documentElement, '--color-accent'); // #00eb9b
// repeat for each custom property...
Das hilft zwar, Wiederholungen im Code zu reduzieren, aber die Situation ist immer noch nicht ideal. Jedes Mal, wenn wir eine benutzerdefinierte Eigenschaft in CSS hinzufügen, müssen wir eine weitere Zeile JavaScript schreiben, um darauf zuzugreifen. Dies kann funktionieren und tut es auch, wenn wir nur wenige benutzerdefinierte Eigenschaften haben. Ich habe dieses Setup bereits in Produktionsprojekten verwendet. Aber es ist auch möglich, dies zu automatisieren.
Gehen wir den Prozess der Automatisierung durch, indem wir ein funktionierendes Ding erstellen.
Was erstellen wir?
Wir erstellen eine Farbpalette, die ein übliches Merkmal in Musterbibliotheken ist. Wir generieren ein Raster von Farbfeldern aus unseren benutzerdefinierten CSS-Eigenschaften.
Hier ist die vollständige Demo, die wir Schritt für Schritt erstellen werden.

Legen wir den Grundstein. Wir verwenden eine ungeordnete Liste, um unsere Palette anzuzeigen. Jedes Farbfeld ist ein <li>-Element, das wir mit JavaScript rendern werden.
<ul class="colors"></ul>
Die CSS für das Grid-Layout ist für die Technik in diesem Beitrag nicht relevant, daher werden wir sie nicht im Detail betrachten. Sie ist in der CodePen-Demo verfügbar.
Nachdem wir nun unser HTML und CSS haben, konzentrieren wir uns auf das JavaScript. Hier ist eine Übersicht, was wir mit unserem Code machen werden
- Alle Stylesheets auf einer Seite abrufen, sowohl externe als auch interne
- Alle Stylesheets von Drittanbieter-Domains verwerfen
- Alle Regeln für die verbleibenden Stylesheets abrufen
- Alle Regeln verwerfen, die keine einfachen Stilregeln sind
- Den Namen und Wert aller CSS-Eigenschaften abrufen
- Nicht-benutzerdefinierte CSS-Eigenschaften verwerfen
- HTML erstellen, um die Farbfelder anzuzeigen
Lasst uns loslegen.
Schritt 1: Alle Stylesheets auf einer Seite abrufen
Das Erste, was wir tun müssen, ist, alle externen und internen Stylesheets auf der aktuellen Seite abzurufen. Stylesheets sind als Member des globalen `document` verfügbar.
document.styleSheets
Das gibt ein Array-ähnliches Objekt zurück. Wir wollen Array-Methoden verwenden, also konvertieren wir es in ein Array. Packen wir das auch in eine Funktion, die wir in diesem Beitrag verwenden werden.
const getCSSCustomPropIndex = () => [...document.styleSheets];
Wenn wir `getCSSCustomPropIndex` aufrufen, sehen wir ein Array von CSSStyleSheet-Objekten, eines für jedes externe und interne Stylesheet auf der aktuellen Seite.

Schritt 2: Drittanbieter-Stylesheets verwerfen
Wenn unser Skript auf https://example.com läuft, muss jedes Stylesheet, das wir inspizieren wollen, auch auf https://example.com liegen. Dies ist eine Sicherheitsfunktion. Aus der MDN-Dokumentation für CSSStyleSheet
In einigen Browsern führt der Zugriff auf
cssRuleszu einemSecurityError, wenn ein Stylesheet von einer anderen Domain geladen wird.
Das bedeutet, dass wir, wenn die aktuelle Seite auf ein Stylesheet verweist, das auf https://some-cdn.com gehostet wird, keine benutzerdefinierten Eigenschaften – oder überhaupt keine Stile – daraus abrufen können. Der Ansatz, den wir hier verfolgen, funktioniert nur für Stylesheets, die auf der aktuellen Domain gehostet werden.
CSSStyleSheet-Objekte haben eine href-Eigenschaft. Ihr Wert ist die vollständige URL zum Stylesheet, z. B. https://example.com/styles.css. Interne Stylesheets haben eine href-Eigenschaft, aber der Wert ist null.
Schreiben wir eine Funktion, die Drittanbieter-Stylesheets verwirft. Das tun wir, indem wir den href-Wert des Stylesheets mit dem current location.origin vergleichen.
const isSameDomain = (styleSheet) => {
if (!styleSheet.href) {
return true;
}
return styleSheet.href.indexOf(window.location.origin) === 0;
};
Jetzt verwenden wir `isSameDomain` als Filter für `document.styleSheets`.
const getCSSCustomPropIndex = () => [...document.styleSheets]
.filter(isSameDomain);
Nachdem die Drittanbieter-Stylesheets verworfen wurden, können wir den Inhalt der verbleibenden inspizieren.
Schritt 3: Alle Regeln für die verbleibenden Stylesheets abrufen
Unser Ziel für `getCSSCustomPropIndex` ist es, ein Array von Arrays zu erzeugen. Um dorthin zu gelangen, werden wir eine Kombination von Array-Methoden verwenden, um zu durchlaufen, die gewünschten Werte zu finden und sie zu kombinieren. Machen wir einen ersten Schritt in diese Richtung, indem wir ein Array erzeugen, das jede Stilregel enthält.
const getCSSCustomPropIndex = () => [...document.styleSheets]
.filter(isSameDomain)
.reduce((finalArr, sheet) => finalArr.concat(...sheet.cssRules), []);
Wir verwenden `reduce` und `concat`, weil wir ein flaches Array erzeugen wollen, bei dem jedes Element erster Ebene das ist, was uns interessiert. In diesem Ausschnitt durchlaufen wir einzelne CSSStyleSheet-Objekte. Für jedes von ihnen benötigen wir dessen cssRules. Aus der MDN-Dokumentation
Die schreibgeschützte Eigenschaft `cssRules` von `CSSStyleSheet` gibt eine lebende `CSSRuleList` zurück, die eine Echtzeit-Aktualisierung aller CSS-Regeln liefert, aus denen das Stylesheet besteht. Jedes Element in der Liste ist eine `CSSRule`, die eine einzelne Regel definiert.
Jede CSS-Regel ist der Selektor, die geschweiften Klammern und die Eigenschaftsdeklarationen. Wir verwenden den Spread-Operator `...sheet.cssRules`, um jede Regel aus dem `cssRules`-Objekt zu nehmen und in `finalArr` zu platzieren. Wenn wir die Ausgabe von `getCSSCustomPropIndex` protokollieren, erhalten wir ein einstufiges Array von CSSRule-Objekten.

Damit erhalten wir alle CSS-Regeln für alle Stylesheets. Wir wollen einige davon verwerfen, also machen wir weiter.
Schritt 4: Alle Regeln verwerfen, die keine einfachen Stilregeln sind
CSS-Regeln gibt es in verschiedenen Typen. CSS-Spezifikationen definieren jeden Typ mit einem konstanten Namen und einer Ganzzahl. Der häufigste Regeltyp ist `CSSStyleRule`. Ein weiterer Regeltyp ist `CSSMediaRule`. Wir verwenden diese, um Media Queries zu definieren, wie z.B. `@media (min-width: 400px) {}`. Andere Typen umfassen `CSSSupportsRule`, `CSSFontFaceRule` und `CSSKeyframesRule`. Sehen Sie sich die Typ-Konstanten-Sektion der MDN-Dokumentation für CSSRule an, um die vollständige Liste zu erhalten.
Wir interessieren uns nur für Regeln, bei denen wir benutzerdefinierte Eigenschaften definieren, und für die Zwecke dieses Beitrags konzentrieren wir uns auf CSSStyleRule. Das schließt den Regeltyp CSSMediaRule aus, bei dem es gültig ist, benutzerdefinierte Eigenschaften zu definieren. Wir könnten einen ähnlichen Ansatz wie den, den wir hier verwenden, um benutzerdefinierte Eigenschaften zu extrahieren, aber wir werden diesen spezifischen Regeltyp ausschließen, um den Umfang der Demo zu begrenzen.
Um unseren Fokus auf Stilregeln zu beschränken, schreiben wir einen weiteren Array-Filter
const isStyleRule = (rule) => rule.type === 1;
Jede `CSSRule` hat eine `type`-Eigenschaft, die die Ganzzahl für diesen Typkonstanten zurückgibt. Wir verwenden `isStyleRule`, um `sheet.cssRules` zu filtern.
const getCSSCustomPropIndex = () => [...document.styleSheets]
.filter(isSameDomain)
.reduce((finalArr, sheet) => finalArr.concat(
[...sheet.cssRules].filter(isStyleRule)
), []);
Eine Sache, die zu beachten ist, ist, dass wir `...sheet.cssRules` in Klammern einschließen, damit wir die Array-Methode `filter` verwenden können.
Unser Stylesheet enthielt nur `CSSStyleRules`, daher sind die Demoergebnisse die gleichen wie zuvor. Wenn unser Stylesheet Media Queries oder `font-face`-Deklarationen enthielte, würde `isStyleRule` diese verwerfen.
Schritt 5: Name und Wert aller Eigenschaften abrufen
Nachdem wir nun die gewünschten Regeln haben, können wir die Eigenschaften abrufen, aus denen sie bestehen. `CSSStyleRule`-Objekte haben eine `style`-Eigenschaft, die ein CSSStyleDeclaration-Objekt ist. Es besteht aus Standard-CSS-Eigenschaften wie `color`, `font-family` und `border-radius` sowie benutzerdefinierten Eigenschaften. Fügen wir das unserer `getCSSCustomPropIndex`-Funktion hinzu, damit sie jede Regel betrachtet und dabei ein Array von Arrays aufbaut
const getCSSCustomPropIndex = () => [...document.styleSheets]
.filter(isSameDomain)
.reduce((finalArr, sheet) => finalArr.concat(
[...sheet.cssRules]
.filter(isStyleRule)
.reduce((propValArr, rule) => {
const props = []; /* TODO: more work needed here */
return [...propValArr, ...props];
}, [])
), []);
Wenn wir dies jetzt aufrufen, erhalten wir ein leeres Array. Wir haben noch mehr Arbeit vor uns, aber das legt das Fundament. Da wir mit einem Array enden wollen, beginnen wir mit einem leeren Array, indem wir den Akkumulator verwenden, der der zweite Parameter von `reduce` ist. Im Körper der `reduce`-Callback-Funktion haben wir eine Platzhaltervariable, `props`, wo wir die Eigenschaften sammeln werden. Die `return`-Anweisung kombiniert das Array aus der vorherigen Iteration – den Akkumulator – mit dem aktuellen `props`-Array.
Im Moment sind beide leere Arrays. Wir müssen `rule.style` verwenden, um `props` mit einem Array für jede Eigenschaft/Wert in der aktuellen Regel zu füllen
const getCSSCustomPropIndex = () => [...document.styleSheets]
.filter(isSameDomain)
.reduce((finalArr, sheet) => finalArr.concat(
[...sheet.cssRules]
.filter(isStyleRule)
.reduce((propValArr, rule) => {
const props = [...rule.style].map((propName) => [
propName.trim(),
rule.style.getPropertyValue(propName).trim()
]);
return [...propValArr, ...props];
}, [])
), []);
`rule.style` ist Array-ähnlich, also verwenden wir erneut den Spread-Operator, um jedes Element daraus in ein Array zu legen, das wir mit `map` durchlaufen. Im `map`-Callback geben wir ein Array mit zwei Elementen zurück. Das erste Element ist `propName` (das `color`, `font-family`, `--color-accent` usw. enthält). Das zweite Element ist der Wert jeder Eigenschaft. Um diesen zu erhalten, verwenden wir die getPropertyValue-Methode von CSSStyleDeclaration. Sie nimmt einen einzelnen Parameter, den String-Namen der CSS-Eigenschaft.
Wir verwenden `trim` für sowohl den Namen als auch den Wert, um sicherzustellen, dass wir keine führenden oder nachfolgenden Leerzeichen einschließen, die manchmal zurückbleiben.
Jetzt, da wir `getCSSCustomPropIndex` aufrufen, erhalten wir ein Array von Arrays. Jedes Kind-Array enthält einen CSS-Eigenschaftsnamen und einen Wert.

Das ist es, wonach wir suchen! Nun, fast. Wir erhalten jede Eigenschaft zusätzlich zu den benutzerdefinierten Eigenschaften. Wir brauchen noch einen Filter, um diese Standardeigenschaften zu entfernen, denn wir wollen nur die benutzerdefinierten Eigenschaften.
Schritt 6: Nicht-benutzerdefinierte Eigenschaften verwerfen
Um festzustellen, ob eine Eigenschaft benutzerdefiniert ist, können wir uns den Namen ansehen. Wir wissen, dass benutzerdefinierte Eigenschaften mit zwei Bindestrichen (`--`) beginnen müssen. Das ist im CSS-Bereich einzigartig, daher können wir es verwenden, um eine Filterfunktion zu schreiben
([propName]) => propName.indexOf("--") === 0)
Dann verwenden wir sie als Filter für das `props`-Array
const getCSSCustomPropIndex = () =>
[...document.styleSheets].filter(isSameDomain).reduce(
(finalArr, sheet) =>
finalArr.concat(
[...sheet.cssRules].filter(isStyleRule).reduce((propValArr, rule) => {
const props = [...rule.style]
.map((propName) => [
propName.trim(),
rule.style.getPropertyValue(propName).trim()
])
.filter(([propName]) => propName.indexOf("--") === 0);
return [...propValArr, ...props];
}, [])
),
[]
);
In der Funktionssignatur haben wir `([propName])`. Dort verwenden wir Array-Destrukturierung, um auf das erste Element jedes Kind-Arrays in `props` zuzugreifen. Von dort aus führen wir eine `indexOf`-Prüfung für den Namen der Eigenschaft durch. Wenn `--` nicht am Anfang des Eigenschaftsnamens steht, dann nehmen wir ihn nicht in das `props`-Array auf.
Wenn wir das Ergebnis protokollieren, erhalten wir die exakte Ausgabe, die wir suchen: Ein Array von Arrays für jede benutzerdefinierte Eigenschaft und ihren Wert ohne andere Eigenschaften.

Wenn wir weiter in die Zukunft blicken, muss das Erstellen der Eigenschaft/Wert-Zuordnung nicht so viel Code erfordern. Es gibt eine Alternative im Entwurf der CSS Typed Object Model Level 1, der CSSStyleRule.styleMap verwendet. Die Eigenschaft `styleMap` ist ein Array-ähnliches Objekt jeder Eigenschaft/jedes Werts einer CSS-Regel. Wir haben sie noch nicht, aber wenn wir sie hätten, könnten wir unseren obigen Code verkürzen, indem wir `map` entfernen
// ...
const props = [...rule.styleMap.entries()].filter(/*same filter*/);
// ...
Zum Zeitpunkt des Schreibens haben Chrome und Edge Implementierungen von `styleMap`, aber keine anderen großen Browser. Da `styleMap` in einem Entwurf vorliegt, gibt es keine Garantie, dass wir sie tatsächlich erhalten werden, und es hat keinen Sinn, sie für diese Demo zu verwenden. Dennoch ist es schön zu wissen, dass es eine zukünftige Möglichkeit ist!
Wir haben die gewünschte Datenstruktur. Jetzt verwenden wir die Daten, um Farbfelder anzuzeigen.
Schritt 7: HTML zum Anzeigen der Farbfelder erstellen
Die Daten in die exakt benötigte Form zu bringen, war die harte Arbeit. Wir brauchen noch ein weiteres Stück JavaScript, um unsere schönen Farbfelder zu rendern. Anstatt die Ausgabe von `getCSSCustomPropIndex` zu protokollieren, speichern wir sie in einer Variablen.
const cssCustomPropIndex = getCSSCustomPropIndex();
Hier ist das HTML, das wir verwendet haben, um unser Farbfeld zu Beginn dieses Beitrags zu erstellen
<ul class="colors"></ul>
Wir werden `innerHTML` verwenden, um diese Liste mit einem Listenelement für jede Farbe zu füllen
document.querySelector(".colors").innerHTML = cssCustomPropIndex.reduce(
(str, [prop, val]) => `${str}<li class="color">
<b class="color__swatch" style="--color: ${val}"></b>
<div class="color__details">
<input value="${prop}" readonly />
<input value="${val}" readonly />
</div>
</li>`,
"");
Wir verwenden `reduce`, um über den benutzerdefinierten Eigenschaftsindex zu iterieren und einen einzigen HTML-ähnlichen String für `innerHTML` zu erstellen. Aber `reduce` ist nicht die einzige Möglichkeit, dies zu tun. Wir könnten `map` und `join` oder `forEach` verwenden. Jede Methode zum Erstellen des Strings funktioniert hier. Dies ist einfach meine bevorzugte Methode.
Ich möchte ein paar spezifische Codeabschnitte hervorheben. In der `reduce`-Callback-Signatur verwenden wir erneut Array-Destrukturierung mit `[prop, val]`, diesmal um auf beide Elemente jedes Kind-Arrays zuzugreifen. Wir verwenden dann die Variablen `prop` und `val` im Körper der Funktion.
Um das Beispiel jeder Farbe anzuzeigen, verwenden wir ein `b`-Element mit einem Inline-Stil
<b class="color__swatch" style="--color: ${val}"></b>
Das bedeutet, dass wir am Ende HTML erhalten, das so aussieht
<b class="color__swatch" style="--color: #00eb9b"></b>
Aber wie setzt das eine Hintergrundfarbe? In dem vollständigen CSS verwenden wir die benutzerdefinierte Eigenschaft `--color` als Wert von `background-color` für jedes `.color__swatch`. Da externe CSS-Regeln von Inline-Stilen erben, ist `--color` der Wert, den wir auf dem `b`-Element setzen.
.color__swatch {
background-color: var(--color);
/* other properties */
}
Wir haben jetzt eine HTML-Anzeige von Farbfeldern, die unsere benutzerdefinierten CSS-Eigenschaften darstellen!
Diese Demo konzentriert sich auf Farben, aber die Technik beschränkt sich nicht auf benutzerdefinierte Farb-Props. Es gibt keinen Grund, warum wir diesen Ansatz nicht erweitern könnten, um andere Abschnitte einer Musterbibliothek zu generieren, wie z.B. Schriften, Abstände, Gittereinstellungen usw. Alles, was als benutzerdefinierte Eigenschaft gespeichert werden kann, kann mit dieser Technik automatisch auf einer Seite angezeigt werden.
Danke für diesen Beitrag, Tyler! Irgendwie verwandt, ich habe versucht, CSS Custom Props von einer Komponente (in unserem Fall eine Web Component, aber es könnte auf einen bestimmten Selektor, die gesamte Seite usw. zutreffen) abzurufen. Diese benutzerdefinierten Prop-Namen wären nicht im Voraus bekannt. Überraschenderweise funktioniert die Verwendung von `getComputedStyles()` und das Zurückgeben von CSS-Eigenschaften, die zwei Bindestriche enthalten, wirklich gut!…außer in Chrome.
Diese spezifische Funktionalität wurde zu den relevanten W3C-Spezifikationen hinzugefügt, aber es gab wenig Bewegung bei dem Fehler, der gegen Chrome eingereicht wurde. Wenn die Community diese Art von Funktionalität einfacher verwalten möchte, schauen Sie sich den Fehler an https://bugs.chromium.org/p/chromium/issues/detail?id=949807
Danke, Alexis, definitiv verwandt. Ich hatte das während der Recherche für diesen Beitrag überall als nicht funktionierend gesehen und konnte den Grund nicht finden. Der Fehlerbericht erklärt es perfekt. Es wäre großartig, das in Chrome zu sehen.
Das ist so cool!
Hallo Tyler, dieser Beitrag war inspirierend, danke!
Ich habe versucht, das von Ihnen oben geteilte Skriptbeispiel in einem meiner eigenen Projekte zu verwenden und bin auf eine kleine Hürde gestoßen und dachte, Sie sollten davon wissen.
`document.styleSheets` ruft nur Top-Level-Stylesheets ab, d.h. solche, die über
link-Elemente hinzugefügt wurden. Die Methode schließt daher keine Stylesheets ein, die über@importhinzugefügt wurden.Ich denke, die per
@importimportierten Stylesheets und ihre Regeln *sind* im resultierenden Array von Stilen verfügbar, so dass sie durch Filtern des Arrays nach importierten Regeln, die mitCSSImportRuleübereinstimmen, extrahiert und dann ihre verschachteltenrulesbetrachtet werden könnten.„Ugh“, oder? Wieder ein Warnsignal für die Verwendung von
@import:(Danke für diesen Beitrag, Tyler.
Ich habe das in TypeScript ausprobiert und festgestellt, dass die Eigenschaft `CSSRule.type` veraltet ist.
Eine Alternative ist die Verwendung von `constructor.name`