CSS-Variablen: Warum ist das wichtig?

CSS-Variablen, genauer gesagt benutzerdefinierte CSS-Properties, werden in Chrome 49 eingeführt. Sie können nützlich sein, um Wiederholungen in CSS zu reduzieren, und auch für leistungsstarke Laufzeiteffekte wie den Themenwechsel und die potenzielle Erweiterung/Polyfillierung zukünftiger CSS-Funktionen.

CSS-Clash

Beim Entwerfen einer Anwendung ist es üblich, eine Reihe von Markenfarben zur Verfügung zu stellen, die wiederverwendet werden, um das Erscheinungsbild der App einheitlich zu gestalten. Leider ist es nicht nur mühsam, sondern auch fehleranfällig, diese Farbwerte immer wieder in Ihrem CSS zu wiederholen. Wenn eine der Farben irgendwann geändert werden muss, können Sie alle Elemente mit der Funktion „Suchen und ersetzen“ ändern. Bei einem ausreichend großen Projekt kann dies jedoch schnell gefährlich werden.

In letzter Zeit haben viele Entwickler auf CSS-Preprocessor wie SASS oder LESS umgestellt, die dieses Problem durch die Verwendung von Preprocessor-Variablen lösen. Diese Tools haben die Produktivität der Entwickler zwar enorm gesteigert, die verwendeten Variablen haben jedoch einen großen Nachteil: Sie sind statisch und können nicht zur Laufzeit geändert werden. Das Hinzufügen der Möglichkeit, Variablen während der Laufzeit zu ändern, öffnet nicht nur die Tür für dynamische Anwendungsdesigns, sondern hat auch erhebliche Auswirkungen auf das responsive Design und die Möglichkeit, zukünftige CSS-Funktionen mit Polyfill zu versehen. Mit der Veröffentlichung von Chrome 49 sind diese Funktionen jetzt in Form von benutzerdefinierten CSS-Properties verfügbar.

Benutzerdefinierte Properties – Kurzübersicht

Mit benutzerdefinierten Properties werden der CSS-Toolbox zwei neue Funktionen hinzugefügt:

  • Die Möglichkeit für einen Autor, einer Property mit einem vom Autor gewählten Namen beliebige Werte zuzuweisen.
  • Die Funktion var(), mit der Autoren diese Werte in anderen Properties verwenden können.

Hier ist ein kurzes Beispiel:

:root {
    --main-color: #06c;
}

#foo h1 {
    color: var(--main-color);
}

--main-color ist eine vom Autor definierte benutzerdefinierte Eigenschaft mit dem Wert #06c. Alle benutzerdefinierten Properties beginnen mit zwei Bindestriche.

Mit der Funktion var() wird der Wert der benutzerdefinierten Eigenschaft abgerufen und durch diesen ersetzt. Dies führt zu color: #06c;. Wenn die benutzerdefinierte Eigenschaft an einer beliebigen Stelle im Stylesheet definiert ist, sollte sie für die Funktion var verfügbar sein.

Die Syntax mag auf den ersten Blick etwas seltsam aussehen. Viele Entwickler fragen: „Warum verwenden wir nicht einfach $foo für Variablennamen?“ Dieser Ansatz wurde speziell ausgewählt, um so flexibel wie möglich zu sein und $foo-Makros möglicherweise in Zukunft zuzulassen. Informationen zur Hintergrundgeschichte finden Sie in diesem Beitrag von einem der Spezifikationsautoren Tab Atkins.

Syntax für benutzerdefinierte Properties

Die Syntax für eine benutzerdefinierte Eigenschaft ist einfach.

--header-color: #06c;

Bei benutzerdefinierten Properties wird zwischen Groß- und Kleinschreibung unterschieden. --header-color und --Header-Color sind also unterschiedliche benutzerdefinierte Properties. Benutzerdefinierte Eigenschaften mögen zwar simpel erscheinen, doch die zulässige Syntax für benutzerdefinierte Eigenschaften ist recht weit gefasst. Das folgende Beispiel zeigt eine gültige benutzerdefinierte Eigenschaft:

--foo: if(x > 5) this.width = 10;

Dies ist zwar nicht als Variable nützlich, da es in einer normalen Property ungültig wäre, aber es kann bei der Laufzeit mit JavaScript gelesen und verwendet werden. Benutzerdefinierte Attribute können also alle Arten interessanter Techniken nutzen, die mit den heutigen CSS-Präprozessoren nicht möglich sind. Wenn Sie also denken: Langweilig, ich habe SASS, also ist das egal…, dann sollten Sie sich das noch einmal genauer ansehen. Das sind nicht die Variablen, mit denen Sie normalerweise arbeiten.

Die Kaskade

Benutzerdefinierte Properties folgen den standardmäßigen Kaskadenregeln. Sie können dieselbe Property also mit unterschiedlicher Spezifität definieren.

:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
<p>I inherited blue from the root element!</p>
<div>I got green set directly on me!</div>
<div id="alert">
    While I got red set directly on me!
    <p>I’m red too, because of inheritance!</p>
</div>

Das bedeutet, dass Sie benutzerdefinierte Eigenschaften in Medienabfragen zur Unterstützung des responsiven Designs nutzen können. Ein Anwendungsfall könnte sein, den Rand um die wichtigsten Schnittelemente zu erweitern, wenn sich der Bildschirm vergrößert:

:root {
    --gutter: 4px;
}

section {
    margin: var(--gutter);
}

@media (min-width: 600px) {
    :root {
    --gutter: 16px;
    }
}

Beachten Sie, dass das obige Code-Snippet nicht mit den heutigen CSS-Präprozessoren möglich ist, die keine Variablen innerhalb von Medienabfragen definieren können. Diese Fähigkeit eröffnet viele Möglichkeiten.

Es ist auch möglich, benutzerdefinierte Properties zu haben, deren Wert von anderen benutzerdefinierten Properties abgeleitet wird. Dies kann für die Themes sehr nützlich sein:

:root {
    --primary-color: red;
    --logo-text: var(--primary-color);
}

Die Funktion var()

Wenn Sie den Wert einer benutzerdefinierten Eigenschaft abrufen und verwenden möchten, müssen Sie die Funktion var() verwenden. Die Syntax für die Funktion var() sieht so aus:

var(<custom-property-name> [, <declaration-value> ]? )

Dabei ist <custom-property-name> der Name einer benutzerdefinierten Property, die vom Ersteller festgelegt wurde, z. B. --foo, und <declaration-value> ist ein Fallback-Wert, der verwendet wird, wenn die referenzierte benutzerdefinierte Property ungültig ist. Fallback-Werte können eine durch Kommas getrennte Liste sein, die zu einem einzigen Wert kombiniert wird. Beispiel: var(--font-stack, "Roboto", "Helvetica"); definiert einen Fallback von "Roboto", "Helvetica". Kurze Werte, wie sie für den Rand und den Abstand verwendet werden, sind nicht durch Kommas getrennt. Ein geeignetes Fallback für den Abstand würde also so aussehen.

p {
    padding: var(--pad, 10px 15px 20px);
}

Mit diesen Fallback-Werten können Komponentenautoren defensive Stile für ihr Element schreiben:

/* In the component’s style: */
.component .header {
    color: var(--header-color, blue);
}
.component .text {
    color: var(--text-color, black);
}

/* In the larger application’s style: */
.component {
    --text-color: #080;
    /* header-color isn’t set,
        and so remains blue,
        the fallback value */
}

Diese Technik ist besonders nützlich für die thematische Erstellung von Webkomponenten, die ShadowDOM verwenden, da benutzerdefinierte Eigenschaften Schattengrenzen überqueren können. Der Autor einer Webkomponente kann ein erstes Design mithilfe von Fallback-Werten erstellen und Design-„Hooks“ in Form von benutzerdefinierten Eigenschaften freigeben.

<!-- In the web component's definition: -->
<x-foo>
    #shadow
    <style>
        p {
        background-color: var(--text-background, blue);
        }
    </style>
    <p>
        This text has a yellow background because the document styled me! Otherwise it
        would be blue.
    </p>
</x-foo>
/* In the larger application's style: */
x-foo {
    --text-background: yellow;
}

Bei der Verwendung von var() gibt es einige Dinge zu beachten. Variablen können keine Attributnamen sein. Beispiele:

.foo {
    --side: margin-top;
    var(--side): 20px;
}

Dies entspricht jedoch nicht der Einstellung margin-top: 20px;. Stattdessen ist die zweite Deklaration ungültig und wird als Fehler ausgegeben.

Ebenso können Sie (naiv) keinen Wert erstellen, wenn ein Teil davon durch eine Variable bereitgestellt wird:

.foo {
    --gap: 20;
    margin-top: var(--gap)px;
}

Auch dies entspricht nicht der Einstellung von margin-top: 20px;. Zum Erstellen eines Werts benötigen Sie etwas anderes: die Funktion calc().

Erstellen von Werten mit calc()

Die Funktion calc() ist ein praktisches Tool, mit dem Sie Berechnungen durchführen können, um CSS-Werte zu ermitteln. Sie wird von allen modernen Browsern unterstützt und kann mit benutzerdefinierten Properties kombiniert werden, um neue Werte zu erstellen. Beispiel:

.foo {
    --gap: 20;
    margin-top: calc(var(--gap) * 1px); /* niiiiice */
}

Mit benutzerdefinierten Eigenschaften in JavaScript arbeiten

Verwenden Sie die Methode getPropertyValue() des berechneten CSSStyleDeclaration-Objekts, um den Wert einer benutzerdefinierten Eigenschaft zur Laufzeit abzurufen.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>I’m a red paragraph!</p>
/* JS */
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
// value = 'red'

Wenn Sie den Wert eines benutzerdefinierten Attributs zur Laufzeit festlegen möchten, verwenden Sie entsprechend die Methode setProperty() des CSSStyleDeclaration-Objekts.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>Now I’m a green paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'green');

Sie können den Wert der benutzerdefinierten Property auch so festlegen, dass er bei der Laufzeit auf eine andere benutzerdefinierte Property verweist. Verwenden Sie dazu die Funktion var() in Ihrem Aufruf von setProperty().

/* CSS */
:root {
    --primary-color: red;
    --secondary-color: blue;
}
<!-- HTML -->
<p>Sweet! I’m a blue paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'var(--secondary-color)');

Da benutzerdefinierte Eigenschaften auf andere benutzerdefinierte Eigenschaften in Ihren Style Sheets verweisen können, können Sie sich vorstellen, wie dies zu verschiedenen interessanten Laufzeiteffekten führen kann.

Unterstützte Browser

Derzeit werden benutzerdefinierte Properties von Chrome 49, Firefox 42, Safari 9.1 und iOS Safari 9.3 unterstützt.

Demo

Probieren Sie das Beispiel aus, um einen Eindruck von allen interessanten Techniken zu erhalten, die Sie jetzt dank benutzerdefinierter Eigenschaften nutzen können.

Weitere Informationen

Wenn Sie mehr über benutzerdefinierte Properties erfahren möchten, hat Philip Walton vom Google Analytics-Team einen Artikel darüber verfasst, warum er von benutzerdefinierten Properties begeistert ist. Auf chromestatus.com können Sie den Fortschritt bei der Einführung in anderen Browsern verfolgen.