CSS/Custom properties (CSS-Variablen)

Aus SELFHTML-Wiki
< CSS(Weitergeleitet von Custom properties)
Wechseln zu: Navigation, Suche

Text-Info

Lesedauer
20min
Schwierigkeitsgrad
leicht → mittel
Vorausgesetztes Wissen
Einstieg in CSS

In Stylesheets finden sich oft viele sich wiederholende Werte. So kann dieselbe Farbe an Hunderten von verschiedenen Stellen verwendet werden, was ein globales Suchen und Ersetzen erfordert, wenn sich diese Farbe ändern soll.

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.

Syntax[Bearbeiten]

Custom properties (benutzerdefinierte Eigenschaften) bestehen aus zwei Bestandteilen:

  1. Einer benutzerdefinierten Eigenschaft wird ein Wert zugewiesen. Der frei gewählte Name dieser Eigenschaft beginnt mit zwei Minuszeichen. Es gibt zwei mögliche Stellen für diese Zuweisung:
    • In einem Regelsatz, der sich in einem style-Element oder einer CSS-Datei befindet
    • In einem style-Attribut
  2. Dieser Wert wird später mit der CSS-Funktion var() beliebig oft wieder aufgerufen.
  • Chrome
  • Firefox
  • Edge
  • Opera
  • Safari

Details: caniuse.com

Syntax
:root {
  --akzentfarbe: #c32e04;
}
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.
Beachten Sie:
  • 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 kann jedes Unicode-Zeichen enthalten, sogar Emojis. Vermeiden Sie Zeichen, die in Stylesheets eine Sonderbedeutung haben. Sie müssten sie mit einem vorangestellten \ Zeichen maskieren.
  • Variablen können nur Werte, aber keine Eigenschaften annehmen.
  • Beim Funktionsaufruf darf sich innerhalb der Klammer kein Leerzeichen befinden.

Mittlerweile unterstützen alle Browser außer dem IE9-11 custom properties und ein Fallback ist daher eigentlich nicht mehr nötig, kann aber als eindeutige Festlegung nach der CSS-Variable notiert werden:

Fallback mit festen Werten für custom properties
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.

Zusätzlich gibt es mit ShadyCSS einen Polyfill, der besonders für Web Components geeignet ist.

Anwendungsbeispiele[Bearbeiten]

Die folgenden Beispiele zeigen, dass man mit custom properties viel mehr als nur Farben festlegen kann:

Anwenden einer Farbpalette[Bearbeiten]

Anwenden einer Farbpalette ansehen …
: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[Bearbeiten]

Mit den custom properties können nicht nur Farben, sondern auch Farbtöne und -variationen erzeugt werden[2]:

Farbton ansehen …
:root { --akzentfarbe: 195, 46, 4 }
h1 { color: rgb(var(--akzentfarbe)) }
h2 { color: rgba(var(--akzentfarbe), 0.5) }

Für h2 wird die Akzentfarbe als rgba-Wert mit einem Transparenzwert von 0.5 (50% Deckkraft) berechnet.


Noch mehr Möglichkeiten ergeben sich bei der Verwendung von HSL-Werten[3]:

Erstellen Sie ihr eigenes Theme! ansehen …
: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), 80%, 50%);
  --backgr2: hsl(calc(var(--baseHue) - 200), 80%, 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[Bearbeiten]

Bis jetzt benötigten Sie JavaScript, um einen Countdown herunterzuzählen. Mit custom properties können Sie dies nur mit CSS erreichen.[4]

Countdown-Timer ansehen …
<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).

Countdown-Timer ansehen …
.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 Einheit s an.
Beachten Sie: Es ist performanter, die Breite des div durch transform:scaleX() anstelle von width zu animieren.

SVG und CSS[Bearbeiten]

Längeneinheiten und custom properties[Bearbeiten]

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:

Abstände berechnen ansehen …
.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[Bearbeiten]

Auch in CSS-Animationen gibt es z.B. bei Verzögerungen Werte, die aufeinander aufbauen.

gestaffelte Animation ansehen …
.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[Bearbeiten]

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:

keine doppelte Arbeit mehr:
.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[Bearbeiten]

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[Bearbeiten]

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.

Custom Properties und Vererbung ansehen …
<style>
body { --farbe: red; }
ul { color: var(--farbe); }
li { --farbe: blue; }
</style>
...
<body>
  Hallo
  <ul>
    <li>Welt</li>
    <li>Selfhtml</li>
  </ul>
</body>
  1. Der body erhält eine custom property --farbe.
  2. Das ul-Element erhält für die Eigenschaft color als Wert den berechneten Wert der custom property --farbe. Dieser wird, falls es keine spezifischeren Festlegungen gibt, an die Kindelemente weitervererbt.
  3. Für li wird der Wert für --farbe neu gesetzt.
Aufgabe: Überlegen Sie, welche Textfarbe die li-Elemente haben.

Lösung anzeigenverbergenFür li wird der Wert für --farbe neu gesetzt.
Allerdings wurde der neue Wert color-Eigenschaft noch nicht zugewiesen; deshalb wird der vorher zugewiesene Wert für ul vererbt.
Die li-Elemente werden rot dargestellt.


Vererbung unterschiedlicher Werte ansehen …
<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.

Aufgabe: Überlegen Sie, welche Textfarbe der untere Absatz hat.

Sind das jetzt Variablen?[Bearbeiten]

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.
Der durch eine Variable repräsentierte Wert und gegebenenfalls auch die Größe kann – im Unterschied zu einer Konstante – zur Laufzeit des Rechenprozesses verändert werden.[10]


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.


Css-variables-example.png


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[Bearbeiten]

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.

  • Leer
  • Leer
  • Leer
  • Leer
  • Leer

Details: caniuse.com

custom media queries(noch nicht implementiert)
@custom-media --narrow-window (max-width: 30em);

@media (--narrow-window) {
  /* narrow window styles */
}

Siehe auch[Bearbeiten]

  • 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.

Quellen[Bearbeiten]

  1. CSSWG: Using Cascading Variables: the var() notation
  2. SELF-Forum: Farben in Abhängigkeit von Grundfarbe von Gunnar Bittersmann vom 05.03.2020
  3. SELF-Forum: Farben in Abhängigkeit von Grundfarbe von Rolf B. vom 05.03.2020
  4. css-tricks.com: Timer Bars in CSS with Custom Properties von Chris Coyier, Aug 18, 2020
  5. How to append a unit to a unitless css custom property with calc() Kevin Powell, 17.03.2019
  6. github.com: [css-values] Proposal: add sibling-count() and sibling-index() Vorschlag vom 04.12.2019
  7. CSS {In Real Life}: 7 Uses for CSS Custom Properties vom 09.12.2019
  8. webplatformnews: CSS custom properties are not variables
  9. MDN:Using CSS custom properties (variables)
  10. Wikipedia: Variable (Programmierung)
  11. Stefan Judis: Can we have custom media queries, please? vom 08.08.2021

Weblinks[Bearbeiten]

Zikaden-Prinzip für unregelmäßig wirkende Strukturen