CSS/Custom properties (CSS-Variablen)
- 20min
- leicht → mittel
- Einstieg in CSS
Mit custom properties (benutzerdefinierten Eigenschaften) können Sie einen Wert an einer Stelle festlegen, auf den dann an mehreren anderen Stellen verwiesen wird. Ein weiterer Vorteil sind semantische Bezeichner. So ist beispielsweise --main-text-color
leichter zu verstehen als #00ff00
, vor allem, wenn dieselbe Farbe auch in anderen Kontexten verwendet wird.
Vorläufer für den Einsatz von Variablen in CSS waren Präprozessoren wie SASS, die aber einige Nachteile haben:
- Sie werden vor dem Laden des Dokuments zusammengestellt und können deshalb nicht auf Änderungen durch media queries reagieren
- Sie können nicht von JavaScript ausgelesen oder verändert werden.
Custom Properties dagegen …
- reagieren unmittelbar auf Änderungen im DOM und auf media queries
- reagieren auf die Elementschachtelungen im DOM
- ermöglichen es, mit setProperty() ein individuelles Theme anzubieten
Oft als CSS-Variablen bezeichnet, sind custom properties doch keine Variablen im eigentlichen Sinne.
Inhaltsverzeichnis
Syntax
Die Kernsyntax von custom properties besteht aus zwei Teilen.
Erzeugen
Ein custom property wird genau wie die Deklaration einer regulären CSS-Eigenschaft erzeugt:
name: wert;
Der Name eines Custom Property muss mit zwei Minuszeichen (Zeichencode \x2D) beginnen.
Der Wert eines Custom Property ist - mit gewissen Einschränkungen - eine beliebige Zeichenfolge und wird durch ein Semikolon abgeschlossen.
<div style="--größe: 10px">
</div>
body {
--größe: 20em;
}
Im Beispiel wurde einerseits im style-Attribut des div-Elements, sowie im Stylesheet im Regelsatz eine custom property namens --größe
angelegt.
Die Buchstaben ö und ß haben einen Zeichencode über 127 und können deshalb problemlos im Namen verwendet werden.
- Im Gegensatz zu regulären Eigenschaften ist der Name eines Custom Property case-sensitive, d. h. die Namen
--self
und--Self
bezeichnen unterschiedliche Eigenschaften. - Der Name eines Custom Property beginnt mit
--
. Die darauf folgenden Zeichen sind weitgehend frei wählbar, vor allem Buchstaben, Ziffern, das Minuszeichen und alle Unicode-Zeichen ab \x80, sogar Emojis.- Vermeiden Sie Zeichen, die in Stylesheets eine Sonderbedeutung haben. Sie müssten sie mit einem vorangestellten \ Zeichen maskieren.
- Innerhalb des Variablennamens darf sich kein Leerzeichen befinden.
- Variablen können nur Werte, aber keine Eigenschaften annehmen.
Die Einschränkungen entstehen dadurch, dass an der Stelle, wo Sie das custom property verwenden, vor und nach Einsetzen des Wertes gültige CSS Syntax stehen muss. Sie können deshalb im Wert eines Custom Property nicht einfach eine runde oder geschweifte Klammer verwenden. Das ist anders, wenn der zugewiesene Wert in Anführungszeichen steht, aber in diesem Fall ist der Wert auch nur dort nutzbar, wo eine CSS Zeichenkette erwartet wird (z.B. als font-family oder content).
Verwenden
Um den Wert eines Custom Property zu nutzen, verwenden Sie die CSS-Funktion var().
var(--name) var(--name, defaultwert)
Sie können var()
überall dort verwenden, wo im CSS ein Wert notiert werden darf. Wie schon erwähnt, muss an der Stelle, wo var()
notiert wird, gültige CSS-Syntax stehen. Die Klammer einer CSS-Funktion kann nicht aus dem custom property kommen, und die Anführungszeichen einer CSS Zeichenkette müssen im Wert des custom property enthalten sein. Wert und Einheit einer CSS-Wertangabe müssen ebenfalls vollständig in einer Variablen stehen.
Sie dürfen var()
aber verwenden, um einen oder auch mehrere Bestandteile einer CSS Sammeleigenschaft beizusteuern.
:root {
--akzentfarbe: #c32e04;
--akzentrahmen: solid
}
h1 {
border: thin solid var(--akzentfarbe);
}
h1::before {
color: var(--akzentfarbe);
content: "Wichtig: ";
}
Im Beispiel wird im root für alle Elemente des Dokuments eine Farbe festgelegt. Diese erhält den Namen --akzentfarbe
und ist durch die zwei vorangestellten Minuszeichen als custom property erkennbar.
Anschließend kann diese Variable im gesamtem Stylesheet mit der Funktion var()
wieder aufgerufen werden.[1].
- So bestimmt sie die Rahmenfarbe des h1-Elements.
- Das Pseudoelement vor der h1-Überschrift erhält eine rote Textfarbe.
Mittlerweile unterstützen alle Browser custom properties und ein Fallback ist daher eigentlich nicht mehr nötig, kann aber als eindeutige Festlegung nach der CSS-Variable notiert werden:
h1 {
color: var(--akzentfarbe, red);
}
Die CSS-Funktion var()
enthält neben dem Variablennamen noch einen durch Komma getrennten festen Wert.
Falls die Variable nicht vorhanden sein sollte, wird dieser verwendet. Falls der fallback-Wert nicht gültig ist, wird der Default-Wert unset verwendet.
Ältere Browser, die var()
nicht verstehen, ignorieren auch diese Angabe und rendern dafür die Elemente in den Standardeinstellungen.
in JavaScript
Sie finden den Wert von Custom Propertys in der CSSStyleDeclaration, die Sie in der style-Eigenschaft eines DOM Elements vorfinden, sowie in dem StylePropertyMapReadOnly-Objekt, das sie von der computedStyleMap()-Methode des Elements bekommen. Wenn Sie mit registrierten Custom Propertys (siehe den Abschnitt Deklaration arbeiten, erhalten Sie den deklarierten Datentyp nur über die Stylemap.
Den aktuellen Wert können Sie für ein Element mittels window.getComputedStyle() ermitteln. Hierbei handelt es sich immer um die Zeichenkettendarstellung, und Längenangaben sind in px umgerechnet.
Wenn Sie einen Wert setzen möchten, dann benötigen Sie dazu die Methode setProperty des style-Objektes.
Deklarieren
Die Deklaration eines Custom Property ist optional. Ohne eine Deklaration ist sein Wertetyp unspezifiziert, mit anderen Worten: alles ist erlaubt, soweit es die Grundregeln von CSS erfüllt (paarige Klammern, paarige Anführungzeichen, etc), es wird an Kindelemente vererbt und sein Anfangswert ist undefiniert.
Zur Deklaration eines Custom Property kann man die JavaScript-Methode CSS.registerProperty verwenden, oder die @-Regel @property. Beide Wege bieten die gleichen Möglichkeiten.
Durch eine Registrierung werden drei Eigenschaften für das Custom Property festgelegt:
- Syntax
- CSS definiert von sich aus etliche Syntaxregeln für Werte. Diese finden sich in den jeweiligen Spezifikationen und können exakt so in der Registrierung angegeben werden. Also "<string>" für Zeichenketten, "<length>" für eine Längenangabe, "<color>" für eine Farbangabe, und so weiter. Eine vollständige Aufstellung der Syntaxwerte finden Sie auf der Referenzseite der
@property
-Regel. - Vererbungsverhalten
- Sie können festlegen, ob das Custom Property auf Kindelemente vererbt wird oder nicht.
- Anfangswert
- Sie können festlegen, dass für den Fall, dass das Custom Property auf einem Element nicht gesetzt wurde, bei seinem Abruf ein bestimmter Wert geliefert wird.
Die Registrierung bietet folgende Vorteile:
- Das Custom Property wird animierbar - ohne Registrierung wird hart zwischen den Keyframe-Werten (bzw. Anfangs- und Endwert) umgeschaltet.
- Die Zuweisung eines ungültigen Wertes (z.B. Farbe statt Zeichenkette) wird beim Zuweisen ignoriert. Ohne Registrierung wird der ungültige Wert im Custom Property gespeichert und überschreibt damit einen eventuell geerbten Wert. Erst beim Verwenden des Custom Property merkt der Browser, dass der Wert ungültig ist und ignoriert das Property.
- Vererbung kann gesteuert werden
- Es kann ein Defaultwert vergeben werden, ohne dass bei jeder Verwendung der var()-Funktion ein Defaultwert hinzugefügt werden muss.
@property --textcolor { syntax: "<color>"; inherits: true; initial-value: white; }
@property --background { syntax: "<color>"; inherits: true; initial-value: #112; }
@media (prefers-color-scheme: light) {
:root {
--textcolor: #112;
--background: white;
}
}
body {
color: var(--textcolor);
background-color: var(--background);
}
Anwendungsbeispiele
Die folgenden Beispiele zeigen, dass man mit custom properties viel mehr als nur Farben festlegen kann:
Anwenden einer Farbpalette
:root {
--primary: #666;
--hintergrund: #ccc;
--akzent: #c32e04;
--linkfarbe: #09c;
}
h2 {
color: var(--primary);
border-bottom: medium solid var(--akzent);
}
aside {
color: var(--akzent);
background-color: var(--primary);
border: medium solid var(--akzent);
}
aside > h2 {
color: var(--hintergrund);
border-bottom: medium solid transparent;
}
aside > label {
color: black;
background-color: var(--akzent);
}
Im Beispiel wird eine Farbpalette festgelegt. Durch die strukturelle Pseudoklasse root wird ein Gültigkeitsbereich für das gesamte HTML-Dokument festgelegt.
Diese Farbwerte werden dann im Stylesheet mehrfach verwendet.
ein maßgeschneidertes Theme
Mit den custom properties können nicht nur Farben, sondern auch Farbtöne und -variationen erzeugt werden[2]:
:root { --akzentfarbe: 195 46 4 }
h1 { color: rgb(var(--akzentfarbe)) }
h2 { color: rgb(var(--akzentfarbe) / 0.5) }
Für h2 wird die Akzentfarbe als rgb-Wert mit einem Transparenzwert von 0.5 (50% Deckkraft) berechnet.
Noch mehr Möglichkeiten ergeben sich bei der Verwendung von HSL-Werten[3]:
:root {
--baseHue: 240;
--primary: hsl(var(--baseHue) 80% 40%);
--accent1: hsl(calc(var(--baseHue) - 231) 80% 40%);
--backgr1: hsl(calc(var(--baseHue) - 231) 80% 40% / .15);
--accent2: hsl(calc(var(--baseHue) - 200) 90% 50%);
--backgr2: hsl(calc(var(--baseHue) - 200) 90% 50% / .15);
}
Für das Dokument werden über den :root-Selektor eine Hauptfarbe und zwei Akzentfarben festgelegt. Dabei wird der Farbton über baseHue
festgelegt und für die Akzentfarben mit der calc()-Funktion entsprechend verändert. Für die Akzentfarben gibt es noch hellere Töne für den Hintergrund. Insgesamt ist die Farbpalette an die SELFHTML-Farbtabelle angelehnt.
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('#huePicker').addEventListener('input', setHue);
});
function setHue() {
const root = document.querySelector(':root');
var huePicker = document.querySelector('#huePicker');
root.style.setProperty('--baseHue', huePicker.value);
};
Beim Laden des Dokuments wird an den Schieberegler ein EventListener angehängt. Sobald er geändert wird, feuert das input-Event und der Wert wird mit setProperty der custom property baseHue
zugewiesen.
Das Beispiel von Keith Clark verwendet Farbwähler (input type="color"), mit denen eine Hauptfarbe und eine Kontrastfarbe ausgewählt werden.
Countdown
Bis jetzt benötigten Sie JavaScript, um einen Countdown herunterzuzählen. Mit custom properties können Sie dies nur mit CSS erreichen.[4]
<div class="time-bar" style="--duration: 9;">
<div></div>
</div>
Als Zeitleiste wird ein div verwendet (ein meter-Element wäre evtl. semantisch passender, bringt jedoch bereits viele eigene Formatierungen mit).
.time-bar div {
height: 1em;
background: linear-gradient(to bottom, red, #c32e04);
animation: roundtime calc(var(--duration) * 1s) steps(var(--duration)) forwards;
transform-origin: left center;
}
@keyframes roundtime {
to {
/* More performant than animating `width` */
transform: scaleX(0);
}
}
Bereits im HTML wurde im style-Attribut des Container-divs eine CSS-Variable --duration
festgelegt.
Sie wird nun 2x verwendet:
- steps zerlegt die Animation in einzelne Schritte, ohne extra Wegpunkte festlegen zu müssen.
- animation-duration erfordert einen Wert mit Zeiteinheit:
calc(var(--duration) * 1s)
fügt an die Zahl die Einheits
an.
SVG und CSS
Längeneinheiten und custom properties
In HTML benötigen Elemente normalerweise keine Positionierung, da sie nacheinander im Elementfluss liegen. SVG-Objekte werden jedes für sich gezeichnet und positioniert. Bei mehreren Objekten mit jeweils gleichem Abstand zueinander müssen die Koordinaten immer wieder neu berechnet werden. Dies geht mit CSS einfacher:
.ball {
--distance: calc(var(--d, 1) * (2.5 * var(--radius)));
--radius: 20px;
cx: var(--distance);
cy: var(--radius);
r: var(--radius);
}
.ball:nth-child(1) {
fill: var(--color-1);
}
.ball:nth-child(2) {
fill: var(--color-2);
--d: 2;
}
.ball:nth-child(3) {
fill: var(--color-3);
--d: 3;
}
...
Das Beispiel enthält drei custom properties:
-
--radius
erhält einen festen Pixelwert von 20px. (Eigentlich besteht das Koordinatensystem von SVG aus dimensionslosen Einheiten; Firefox benötigt bei Geometrieattributen in CSS eine Angabe in Pixeln.) -
--var-distance
besteht aus einer längeren Berechnung mit calc():-
(2.5 * var(--radius)
verwendet den eben erwähnten Radius und multipliziert ihn mit einem Faktor 2.5. Dieser Wert wird z.B. für den Abstand des ersten Balls zum linken Rand benötigt. -
calc(var(--d, 1) * ...)
multipliziert das obere Zwischenergebnis mit einer Variable--d
-
- Die Variable
--d
wird per CSS über den nth-child immer wieder neu gesetzt.
Die Berechnung von --distance
löst ein vermeintliches Problem von custom properties, dass das Hinzufügen von Einheiten wie in JavaScript nicht möglich wäre:[5]
.icon { --scale: 1; /* this doesn't work */ font-size: var(--scale) + 'px'; }
Mit Hilfe der calc()-Funktion gelingt dies problemlos:
.icon { --scale: 1; /* this works */ font-size: calc(var(--scale) * 1rem); }
SVG mit CSS-Animationen
Auch in CSS-Animationen gibt es z.B. bei Verzögerungen Werte, die aufeinander aufbauen.
.ball {
--delay: calc(var(--i, 1) * 100ms);
--distance: calc(var(--d, 1) * (2.5 * var(--radius)));
--radius: 20px;
cx: var(--distance);
cy: var(--radius);
r: var(--radius);
animation: moveCircle 1200ms var(--delay) ease-in-out infinite;
}
.ball:nth-child(1) {
fill: var(--color-1);
}
.ball:nth-child(2) {
fill: var(--color-2);
--d: 2;
--i: 2;
}
.ball:nth-child(3) {
fill: var(--color-3);
--d: 3;
--i: 3;
}
Wie im oberen Beispiel setzt sich die Variable --delay
aus einer Berechnung zusammen: Die Zählvariable --i
wird über den nth-child-Selektor immer wieder neu gesetzt und mit einem gegebenen Zeitwert (von hier 100ms) multipliziert.
Leider muss diese Variable --i
wie auch --d
immer wieder explizit gesetzt werden. Um die Anzahl der Objekte automatisch zu erfassen, müsste man JavaScript einsetzen. Deshalb gibt es einen Vorschlag eine sibling-count()-Funktion einzuführen, die dies bereits im CSS erledigen könnte.[6]
clip-path und vendor-prefixes
Eine der Begleiterscheinungen von CSS3 waren die Browserpräfixe, die den damals neuen Eigenschaften vorangestellt wurden. Heute gibt es nur noch wenige Eigenschaften, die z. B. ein zusätzlichen Regelsatz mit -webkit-
benötigen.
Bei Beschneidungen erfordert ein Einsatz der clip-path-Eigenschaft solch einen doppelten Regelsatz. Damit bei einer späteren Anpassung der Wert nur einmal geändert werden muss, können auch hier Custom properties helfen:
.img {
--clip: polygon(0 0, 100% 0, 50% 100%, 0 100%);
-webkit-clip-path: var(--clip);
clip-path: var(--clip);
}
Der Wert wird als CSS-Variable --clip
festgelegt und beiden Regelsätze zugewiesen.
Noch besser wäre es, wenn man den Beschneidungspfad selbst durch custom properties vereinfachen könnte. Michelle Barker zeigte das in diesem CodePen.[7]
Weiterführendes
Benutzerdefinierte Eigenschaften verhalten sich genau wie normale, vererbbare CSS-Eigenschaften: Sie sind auch auf Kind-Elementen verfügbar und sie beachten die Regeln für Kaskade und Spezifität, d.h. benutzerdefinierte Eigenschaften, die an einer Stelle festgelegt werden, können durch eine spezifischere Regel überschrieben werden.
Zusammenspiel der Regeln
Eine wichtige Eigenschaft von custom properties ist, dass var()
aufgelöst wird, wenn die Eigenschaft, die die var()
-Angabe enthält, auf ein HTML- oder SVG-Element angewendet wird. Wird die so gesetzte Eigenschaft weiter vererbt, ist der Wert festgelegt und nicht mehr variabel.
<style>
body { --farbe: red; }
ul { color: var(--farbe); }
li { --farbe: blue; }
</style>
<body>
Hallo
<ul>
<li>Welt</li>
<li>Selfhtml</li>
</ul>
</body>
- Der body erhält ein custom property
--farbe
mit dem Wertred
. - Für das
ul
-Element wird für die Eigenschaftcolor
der berechnete Wert des custom property--farbe
festgelegt. Dieser wird, falls es keine spezifischeren Festlegungen gibt, an die Kindelemente weitervererbt. - Für
li
wird der Wert für--farbe
neu gesetzt.
Lösung anzeigenverbergenFür li
wird der Wert für --farbe
neu gesetzt.
Allerdings wurde der neue Wert nicht der color
-Eigenschaft zugewiesen; deshalb wird der vorher zugewiesene Wert für ul
vererbt.
→ Die li-Elemente werden rot dargestellt.
<style>
p { color: var(--farbe); }
section.a { --farbe:red; }
section.b { --farbe:blue; }
</style>
<body>
<section class="a">
<p>Hallo Welt</p>
</section>
<section class="b">
<p>Hallo Welt</p>
</section>
<p>Absatz außerhalb einer section</p>
Hier ist es so, dass die erste CSS Regel auf beide p
Elemente innerhalb der section-Elemente angewendet wird, aber jeweils im Moment der Anwendung unterschiedliche Werte für --farbe
vom section
-Element vererbt werden. Deswegen wird der erste Abschnitt rot dargestellt und der zweite Abschnitt blau.
Sind das jetzt Variablen?
Auch wenn es immer wieder heißt, dass Custom properties keine Variablen wären, werden die beiden Begriffe oft nebeneinander benutzt:[8]
Custom properties (sometimes referred to as CSS variables or cascading variables) … You can define multiple fallback values when the given variable is not yet defined.
MDN: Using CSS custom properties (variables)[9]
Information: Variablen
In Programmiersprachen sind Variablen abstrakte Behälter für eine Größe, welche im Verlauf eines Rechenprozesses auftritt. Im Normalfall wird eine Variable durch einen Namen bezeichnet und hat eine Adresse im Speicher einer Maschine.
Auch die Spezifikation verwendet den Begriff Variable, unterscheidet aber: Eine benutzerdefinierte Eigenschaft ist keine Variable, ermöglicht aber die Festlegung einer Variable.
Jede Eigenschaft kann mit der var()-Funktion Variablen verwenden, deren Werte durch die zugehörigen benutzerdefinierten Eigenschaften definiert sind.
Die benutzerdefinierte Eigenschaft --accent-background
verwendet die Variable var(--main-color)
, deren Wert durch die benutzerdefinierte Eigenschaft --main-color
definiert ist.
Zukunftsmusik: custom media queries
Wenn Sie benutzerdefinierte CSS-Eigenschaften (CSS-Variablen) verwenden und sie in einer Medienabfrage verwenden wollten, haben Sie wahrscheinlich festgestellt, dass Sie benutzerdefinierte Eigenschaften in diesem Zusammenhang nicht verwenden können.[11]
@media (max-width: var(--mobile-breakpoint)) {...}
Benutzerdefinierte Media-Queries sind seit Jahren in der Spezifikation enthalten. Allerdings scheint es kein großes Interesse an der Implementierung zu geben. Weder die MDN noch caniuse.com kennen das Feature bisher.
@custom-media --narrow-window (max-width: 30em);
@media (--narrow-window) {
/* narrow window styles */
}
Siehe auch
- fertige Layouts/Design01
ein Beispiel-Layout mit einem „normalen“ und einem dunklen Theme im Dark Mode - die SVG-Uhr, die die Zeit mit CSS-Variablen setzt
- Tabellen/Responsive Gestaltung – responsive Tabellen erhalten mit custom properties benutzerfreundliche Spaltenüberschriften
- CSS-Formatierung des Shadow DOM
Das SVG-Use-Element kann aus beliebig vielen Teil-Elementen bestehen, die im Shadow DOM vorhanden sind, aber nicht durch CSS formatiert werden können. MIt Custom properties können auch einzelne Bestandteile des use-Elements formatiert werden.
→ SVG/Tutorials/Gruppierungen#Styling des use-Elements
Quellen
- ↑ CSSWG: Using Cascading Variables: the var() notation
- ↑ SELF-Forum: Farben in Abhängigkeit von Grundfarbe von Gunnar Bittersmann vom 05.03.2020
- ↑ SELF-Forum: Farben in Abhängigkeit von Grundfarbe von Rolf B. vom 05.03.2020
- ↑ css-tricks.com: Timer Bars in CSS with Custom Properties von Chris Coyier, Aug 18, 2020
- ↑ How to append a unit to a unitless css custom property with calc() Kevin Powell, 17.03.2019
- ↑ github.com: [css-values] Proposal: add sibling-count() and sibling-index() Vorschlag vom 04.12.2019
- ↑ CSS {In Real Life}: 7 Uses for CSS Custom Properties vom 09.12.2019
- ↑ webplatformnews: CSS custom properties are not variables
- ↑ MDN:Using CSS custom properties (variables)
- ↑ Wikipedia: Variable (Programmierung)
- ↑ Stefan Judis: Can we have custom media queries, please? vom 08.08.2021
Weblinks
- csswg: Defining Custom Properties: the --* family of properties (Entwurf)
- MDN: Using CSS custom properties (variables)
- css-tricks: Simplifying CSS Cubes with Custom Properties von Ana Tudor
- sitepoint: A Practical Guide to CSS Variables (Custom Properties) (15.06.2017)
Zikaden-Prinzip für unregelmäßig wirkende Strukturen
- The Cicada Principle, revisited with CSS variables Lea Verou, 07.07.2020