Letztes Mal haben wir uns ein paar mögliche Ansätze angesehen, um CSS Custom Properties in responsiven Designs zu deklarieren und zu verwenden. In diesem Artikel werden wir uns CSS-Variablen genauer ansehen und wie man sie in wiederverwendbaren Komponenten und Modulen verwendet. Wir lernen, wie wir unsere Variablen optional machen und Fallback-Werte festlegen können.
Als Beispiel werden wir ein einfaches Grid-System auf Basis von Flexbox aufbauen. Grid-Systeme spielen eine entscheidende Rolle in responsiven Designs. Ein Grid-System zu bauen, das gleichzeitig flexibel und leichtgewichtig ist, kann jedoch eine knifflige Aufgabe sein. Sehen wir uns die gängigen Ansätze für Grid-Systeme an und wie CSS Custom Properties uns beim Aufbau helfen können.
Artikelserie
- Variablen und Breakpoints definieren
- Ein flexibles Grid-System aufbauen (Dieser Beitrag)
Ein einfaches CSS-Grid-System
Beginnen wir mit einem 12-Spalten-Grid-System
.container {
max-width: 960px;
margin: 0 auto;
display: flex;
}
.col-1 { flex-basis: 8.333%; }
.col-2 { flex-basis: 16.666%; }
.col-3 { flex-basis: 25%; }
.col-4 { flex-basis: 33.333%; }
.col-5 { flex-basis: 41.666%; }
.col-6 { flex-basis: 50%; }
/* and so on up to 12... */
Siehe den Pen
#5 Responsive Features mit CSS Custom Properties erstellen von Mikołaj (@mikolajdobrucki)
auf CodePen.
Hier gibt es ziemlich viel Wiederholung und hartkodierte Werte. Ganz zu schweigen davon, wie viele weitere generiert werden, sobald wir mehr Breakpoints, Offset-Klassen usw. hinzufügen.
Ein Grid-System mit Sass aufbauen
Um unser Grid-Beispiel lesbarer und wartbarer zu machen, verwenden wir Sass zur Vorverarbeitung unseres CSS.
$columns: 12; // Number of columns in the grid system
.container {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
max-width: 960px;
}
@for $width from 1 through $columns {
.col-#{$width} {
flex-basis: $width / $columns * 100%;
}
}
Siehe den Pen
#6 Responsive Features mit CSS Custom Properties erstellen von Mikołaj (@mikolajdobrucki)
auf CodePen.
Das ist definitiv viel einfacher zu handhaben. Wenn wir unser Grid weiterentwickeln und es beispielsweise von 12 auf 16 Spalten ändern möchten, müssen wir nur eine einzige Variable aktualisieren (im Vergleich zu Dutzenden von Klassen und Werten). Aber... solange unser Sass jetzt kürzer und wartbarer ist, ist der kompilierte Code identisch mit dem ersten Beispiel. Wir werden am Ende immer noch mit einer riesigen Menge an Code in der endgültigen CSS-Datei dastehen. Lassen Sie uns untersuchen, was passiert, wenn wir versuchen, die Sass-Variablen stattdessen durch CSS Custom Properties zu ersetzen.
Ein Grid-System mit CSS Custom Properties aufbauen
Bevor wir mit CSS Custom Properties herumspielen, beginnen wir mit etwas HTML. Hier ist das Layout, das wir anstreben

Es besteht aus drei Elementen: einem Header, einem Inhaltsbereich und einer Seitenleiste. Lassen Sie uns Markup für diese Ansicht erstellen und jedem der Elemente eine eindeutige semantische Klasse (header, content, sidebar) und eine column-Klasse geben, die anzeigt, dass dieses Element Teil eines Grid-Systems ist.
<div class="container">
<header class="header column">
header
</header>
<main class="content column">
content
</main>
<aside class="sidebar column">
sidebar
</aside>
</div>
Unser Grid-System basiert, wie zuvor, auf einem 12-Spalten-Layout. Man kann es sich als eine Überlagerung vorstellen, die unsere Inhaltsbereiche abdeckt.

Also belegt .header alle 12 Spalten, .content belegt acht Spalten (66,6 % der Gesamtbreite) und .sidebar belegt vier Spalten (33,3 % der Gesamtbreite). In unserem CSS möchten wir die Breite jedes Abschnitts steuern können, indem wir eine einzige Custom Property ändern.
.header {
--width: 12;
}
.content {
--width: 8;
}
.sidebar {
--width: 4;
}
Damit das funktioniert, müssen wir nur eine Regel für die Klasse .column schreiben. Glücklicherweise ist die meiste Arbeit bereits erledigt! Wir können den Sass aus dem vorherigen Kapitel wiederverwenden und die Sass-Variablen durch CSS Custom Properties ersetzen.
.container {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
max-width: 960px;
}
.column {
--columns: 12; /* Number of columns in the grid system */
--width: 0; /* Default width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
Beachten Sie hier zwei wichtige Änderungen
- Die Variable
--columnswird jetzt *innerhalb* der Regel.columndeklariert. Der Grund dafür ist, dass diese Variable nicht außerhalb des Geltungsbereichs dieser Klasse verwendet werden soll. - Die mathematische Gleichung, die wir in der Eigenschaft
flex-basisdurchführen, ist jetzt in einecalc()-Funktion eingeschlossen. Mathematische Berechnungen, die in Sass geschrieben sind, werden vom Präprozessor kompiliert und benötigen keine zusätzliche Syntax.calc()hingegen ermöglicht es uns, mathematische Berechnungen in live CSS durchzuführen. Die Gleichung muss immer in einecalc()-Funktion eingeschlossen werden.
Auf einer ganz grundlegenden Ebene ist das alles! Wir haben gerade ein 12-Spalten-Grid-System mit CSS Custom Properties aufgebaut. Herzlichen Glückwunsch! Wir könnten jetzt Feierabend machen und diesen Artikel glücklich abschließen, aber... normalerweise brauchen wir ein Grid-System, das etwas anspruchsvoller ist. Und hier wird es wirklich interessant.
Siehe den Pen
#8 Responsive Features mit CSS Custom Properties erstellen von Mikołaj (@mikolajdobrucki)
auf CodePen.
Einen Breakpoint zum Grid hinzufügen
Meistens benötigen wir Layouts, die auf verschiedenen Bildschirmgrößen unterschiedlich aussehen. Nehmen wir an, in unserem Fall soll das Layout auf großen Ansichtsfenstern (z. B. Desktop) so bleiben, wie es ist, aber auf kleineren Bildschirmen (z. B. Mobilgeräte) sollen alle drei Elemente die volle Breite einnehmen.

In diesem Fall möchten wir also, dass unsere Variablen wie folgt aussehen
.header {
--width-mobile: 12;
}
.content {
--width-mobile: 12;
--width-tablet: 8; /* Tablet and larger */
}
.sidebar {
--width-mobile: 12;
--width-tablet: 4; /* Tablet and larger */
}
.content und .sidebar enthalten jetzt jeweils zwei Variablen. Die erste Variable (--width-mobile) ist die Anzahl der Spalten, die ein Element standardmäßig einnehmen soll, und die zweite Variable (--width-tablet) ist die Anzahl der Spalten, die ein Element auf größeren Bildschirmen einnehmen soll. Das .header-Element ändert sich nicht; es nimmt immer die volle Breite ein. Auf größeren Bildschirmen soll der Header einfach die Breite erben, die er auf Mobilgeräten hat.
Aktualisieren wir nun unsere .column-Klasse.
CSS-Variablen und Fallback
Damit die mobile Version wie erwartet funktioniert, müssen wir die .column-Klasse wie folgt ändern
.column {
--columns: 12; /* Number of columns in the grid system */
--width: var(--width-mobile, 0); /* Default width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
Im Grunde ersetzen wir den Wert der Variable --width durch --width-mobile. Beachten Sie, dass die Funktion var() jetzt zwei Argumente hat. Das erste ist ein Standardwert. Es besagt: "Wenn im gegebenen Geltungsbereich eine Variable --width-mobile existiert, weisen Sie ihren Wert der Variable --width zu." Das zweite Argument ist ein Fallback. Mit anderen Worten: "Wenn im gegebenen Geltungsbereich keine Variable --width-mobile deklariert ist, weisen Sie diesen Fallback-Wert der Variable --width zu." Diesen Fallback setzen wir, um ein Szenario vorzubereiten, in dem einige Grid-Elemente keine definierte Breite haben.
Zum Beispiel hat unser .header-Element eine deklarierte Variable --width-mobile, was bedeutet, dass die Variable --width ihr gleich sein wird und die Eigenschaft flex-basis dieses Elements zu 100% berechnet wird.
.header {
--width-mobile: 12;
}
.column {
--columns: 12;
--width: var(--width-mobile, 0); /* 12, takes the value of --width-mobile */
flex-basis: calc(var(--width) / var(--columns) * 100%); /* 12 ÷ 12 × 100% = 100% */
}
Wenn wir die Variable --width-mobile aus der Regel .header entfernen, verwendet die Variable --width stattdessen einen Fallback-Wert.
.header {
/* Nothing here... */
}
.column {
--columns: 12;
--width: var(--width-mobile, 0); /* 0, takes the the fallback value */
flex-basis: calc(var(--width) / var(--columns) * 100%); /* 0 ÷ 12 × 100% = 0% */
}
Nachdem wir nun verstehen, wie man Fallbacks für CSS Custom Properties setzt, können wir einen Breakpoint erstellen, indem wir eine Media Query zu unserem Code hinzufügen.
.column {
--columns: 12; /* Number of columns in the grid system */
--width: var(--width-mobile, 0); /* Default width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
@media (min-width: 576px) {
.column {
--width: var(--width-tablet); /* Width of the element on tablet and up */
}
}
Das funktioniert genau wie erwartet, aber nur für den Inhalt und die Seitenleiste, d. h. für die Elemente, bei denen sowohl --width-mobile als auch --width-tablet angegeben sind. Warum?
Die von uns erstellte Media Query gilt für alle .column-Elemente, auch für diejenigen, bei denen in ihrem Geltungsbereich keine Variable --width-tablet deklariert ist. Was passiert, wenn wir eine nicht deklarierte Variable verwenden? Die Referenz auf die nicht deklarierte Variable in einer var()-Funktion wird dann zur Laufzeit des berechneten Werts als ungültig betrachtet, d. h. ungültig zu dem Zeitpunkt, an dem ein User-Agent versucht, sie im Kontext einer gegebenen Deklaration zu berechnen.
Idealerweise möchten wir in einem solchen Fall, dass die Deklaration --width: var(--width-tablet); ignoriert wird und stattdessen die vorherige Deklaration --width: var(--width-mobile, 0); verwendet wird. Aber so funktionieren Custom Properties nicht! Tatsächlich wird die ungültige Variable --width-tablet immer noch in der Deklaration flex-basis verwendet. Eine Eigenschaft, die eine ungültige var()-Funktion enthält, wird immer zu ihrem Anfangswert berechnet. Da flex-basis: calc(var(--width) / var(--columns) * 100%); eine ungültige var()-Funktion enthält, wird die gesamte Eigenschaft zu auto (dem Anfangswert für flex-basis) berechnet.
Was können wir also sonst noch tun? Einen Fallback setzen! Wie wir zuvor gelernt haben, wird eine var()-Funktion, die eine Referenz auf die nicht deklarierte Variable enthält, zu ihrem Fallback-Wert berechnet, solange dieser angegeben ist. In diesem Fall können wir also einfach einen Fallback für die Variable --width-tablet setzen.
.column {
--columns: 12; /* Number of columns in the grid system */
--width: var(--width-mobile, 0); /* Default width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
@media (min-width: 576px) {
.column {
--width: var(--width-tablet, var(--width-mobile, 0));
}
}
Siehe den Pen
#9 Responsive Features mit CSS Custom Properties erstellen von Mikołaj (@mikolajdobrucki)
auf CodePen.
Dies erstellt eine Kette von Fallback-Werten, wodurch die Eigenschaft --width --width-tablet verwendet, wenn verfügbar, dann --width-mobile, wenn --width-tablet nicht deklariert ist, und schließlich 0, wenn keine der Variablen deklariert ist. Dieser Ansatz ermöglicht es uns, zahlreiche Kombinationen durchzuführen.
.section-1 {
/* Flexible on all resolutions */
}
.section-2 {
/* Full-width on mobile, half of the container's width on tablet and up */
--width-mobile: 12;
--width-tablet: 6;
}
.section-3 {
/* Full-width on all resolutions */
--width-mobile: 12;
}
.section-4 {
/* Flexible on mobile, 25% of the container's width on tablet and up */
--width-tablet: 3;
}
Eine weitere Sache, die wir hier tun können, ist, den Standardwert 0 in eine weitere Variable umzuwandeln, um Wiederholungen zu vermeiden. Das macht den Code zwar etwas länger, aber einfacher zu aktualisieren.
.column {
--columns: 12; /* Number of columns in the grid system */
--width-default: 0; /* Default width, makes it flexible */
--width: var(--width-mobile, var(--width-default)); /* Width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
@media (min-width: 576px) {
.column {
--width: var(--width-tablet, var(--width-mobile, var(--width-default)));
}
}
Siehe den Pen
#10 Responsive Features mit CSS Custom Properties erstellen von Mikołaj (@mikolajdobrucki)
auf CodePen.
Jetzt haben wir ein voll funktionsfähiges, flexibles Grid! Wie wäre es, weitere Breakpoints hinzuzufügen?
Weitere Breakpoints hinzufügen
Unser Grid ist bereits ziemlich mächtig, aber wir brauchen oft mehr als einen Breakpoint. Glücklicherweise ist das Hinzufügen weiterer Breakpoints zu unserem Code nicht einfacher möglich. Alles, was wir tun müssen, ist den bereits vorhandenen Code wiederzuverwenden und eine weitere Variable hinzuzufügen.
.column {
--columns: 12; /* Number of columns in the grid system */
--width-default: 0; /* Default width, makes it flexible */
--width: var(--width-mobile, var(--width-default)); /* Width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
@media (min-width: 576px) {
.column {
--width: var(--width-tablet, var(--width-mobile, var(--width-default)));
}
}
@media (min-width: 768px) {
.column {
--width: var(--width-desktop, var(--width-tablet, var(--width-mobile, var(--width-default))));
}
}
Siehe den Pen
#11 Responsive Features mit CSS Custom Properties erstellen von Mikołaj (@mikolajdobrucki)
auf CodePen.
Fallback-Ketten reduzieren
Eine Sache, die in unserem Code nicht so toll aussieht, ist, dass die Fallback-Ketten mit jedem Breakpoint länger und länger werden. Wenn wir dieses Problem angehen wollen, können wir unseren Ansatz ändern zu etwas wie diesem
.column {
--columns: 12; /* Number of columns in the grid system */
--width: var(--width-mobile, 0); /* Width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
@media (min-width: 576px) {
.column {
--width-tablet: var(--width-mobile);
--width: var(--width-tablet);
}
}
@media (min-width: 768px) {
.column {
--width-desktop: var(--width-tablet);
--width: var(--width-desktop);
}
}
Siehe den Pen
#12 Responsive Features mit CSS Custom Properties erstellen von Mikołaj (@mikolajdobrucki)
auf CodePen.
Dieser Code leistet genau die gleiche Arbeit, aber auf eine etwas andere Weise. Anstatt für jeden Breakpoint eine vollständige Fallback-Kette zu erstellen, setzen wir den Wert jeder Variablen auf die Variable des vorherigen Breakpoints als Standardwert.
Warum so kompliziert?
Es scheint, als hätten wir viel Arbeit investiert, um eine relativ einfache Aufgabe zu erledigen. Warum? Die Hauptantwort ist: um den Rest unseres Codes einfacher und wartbarer zu machen. Tatsächlich könnten wir das gleiche Layout mit den Techniken erstellen, die im vorherigen Teil dieses Artikels beschrieben wurden.
.container {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
max-width: 960px;
}
.column {
--columns: 12; /* Number of columns in the grid system */
--width: 0; /* Default width of the element */
flex-basis: calc(var(--width) / var(--columns) * 100%);
}
.header {
--width: 12;
}
.content {
--width: 12;
}
.sidebar {
--width: 12;
}
@media (min-width: 576px) {
.content {
--width: 6;
}
.sidebar {
--width: 6;
}
}
@media (min-width: 768px) {
.content {
--width: 8;
}
.sidebar {
--width: 4;
}
}
In einem kleinen Projekt könnte dieser Ansatz perfekt funktionieren. Für komplexere Lösungen würde ich jedoch eher eine skalierbarere Lösung in Betracht ziehen.
Warum sollte ich mich überhaupt darum kümmern?
Wenn der vorgestellte Code eine sehr ähnliche Arbeit leistet wie das, was wir mit Präprozessoren wie Sass erreichen können, warum sollten wir uns überhaupt darum kümmern? Sind Custom Properties besser? Die Antwort ist, wie üblich: es kommt darauf an. Ein Vorteil der Verwendung von Sass ist die bessere Browserunterstützung. Die Verwendung von Custom Properties hat jedoch auch ein paar Vorteile.
- Es ist reines CSS. Mit anderen Worten, es ist eine standardisiertere, verlässlichere Lösung, unabhängig von Drittanbietern. Kein Kompilieren, keine Paketversionen, keine seltsamen Probleme. Es funktioniert einfach (abgesehen von den Browsern, in denen es einfach nicht funktioniert).
- Es ist einfacher zu debuggen. Das ist eine fragwürdige Aussage, da man argumentieren könnte, dass Sass Feedback durch Konsolenmeldungen gibt und CSS nicht. Sie können jedoch vorverarbeiteten Code nicht direkt im Browser anzeigen und debuggen, während Sie mit CSS-Variablen arbeiten, ist der gesamte Code direkt in den DevTools verfügbar (und live!).
- Es ist wartbarer. Custom Properties ermöglichen es uns, Dinge zu tun, die mit keinem Präprozessor möglich sind. Sie ermöglichen es uns, unsere Variablen kontextbezogener und damit wartbarer zu machen. Außerdem sind sie mit JavaScript auswählbar, was bei Sass-Variablen nicht der Fall ist.
- Es ist flexibler. Beachten Sie, dass das von uns erstellte Grid-System extrem flexibel ist. Möchten Sie auf einer Seite ein 12-Spalten-Grid und auf einer anderen eine 15-Spalten-Grid verwenden? Nur zu – es ist eine Frage einer einzigen Variablen. Der gleiche Code kann auf beiden Seiten verwendet werden. Ein Präprozessor würde erfordern, Code für zwei separate Grid-Systeme zu generieren.
- Es nimmt weniger Platz ein. Obwohl das Gewicht von CSS-Dateien normalerweise nicht der Hauptengpass für die Seitenladeleistung ist, versteht es sich von selbst, dass wir CSS-Dateien wann immer möglich optimieren sollten. Um ein besseres Bild davon zu geben, wie viel gespart werden kann, habe ich ein kleines Experiment durchgeführt. Ich habe das Grid-System von Bootstrap genommen und es mit Custom Properties von Grund auf neu aufgebaut. Die Ergebnisse sind wie folgt: Die Grundkonfiguration des Bootstrap-Grids erzeugt über 54 KB CSS, während ein ähnliches Grid mit Custom Properties nur 3 KB groß ist. Das ist ein Unterschied von 94 %! Darüber hinaus wird die Datei größer, wenn wir dem Bootstrap-Grid weitere Spalten hinzufügen. Mit CSS-Variablen können wir beliebig viele Spalten verwenden, ohne die Dateigröße zu beeinträchtigen.
Die Dateien können komprimiert werden, um den Unterschied etwas zu minimieren. Das gzippte Bootstrap-Grid ist 6,4 KB groß, im Vergleich zu 0,9 KB für das Custom-Properties-Grid. Das ist immer noch ein Unterschied von 86 %!
Performance von CSS-Variablen
Zusammenfassend lässt sich sagen, dass die Verwendung von CSS Custom Properties viele Vorteile hat. Aber wenn wir den Browser all die Berechnungen durchführen lassen, die von Präprozessoren durchgeführt wurden, beeinträchtigen wir dann die Performance unserer Website negativ? Es stimmt, dass die Verwendung von Custom Properties und calc()-Funktionen mehr Rechenleistung beansprucht. In Fällen wie den Beispielen, die wir in diesem Artikel besprochen haben, wird der Unterschied jedoch normalerweise nicht bemerkbar sein. Wenn Sie mehr über dieses Thema erfahren möchten, empfehle ich Ihnen, diesen exzellenten Artikel von Lisi Linhart zu lesen.
Nicht nur Grid-Systeme
Schließlich ist das Verständnis der Feinheiten von Custom Properties vielleicht nicht so einfach, wie es scheint. Es wird definitiv Zeit brauchen, aber es lohnt sich. CSS-Variablen können eine große Hilfe sein, wenn man mit wiederverwendbaren Komponenten, Design-Systemen, Theming und anpassbaren Lösungen arbeitet. Zu wissen, wie man mit Fallback-Werten und nicht deklarierten Variablen umgeht, kann sich dann als sehr nützlich erweisen.
Danke fürs Lesen und viel Erfolg auf Ihrer eigenen Reise mit CSS Custom Properties!
Vielen Dank, das war großartig!
Wenn Sie einen Vorher/Nachher-Schnappschuss von Code bereitstellen könnten, wenn Sie erwähnen
Würde dieser Artikel noch besser werden, als ob das möglich wäre!
Das ist definitiv ein Thema für einen ganz neuen Artikel :) aber auch einige Beispiele aus diesem hier haben damit zu tun.
z.B. das Setzen einer --columns-Variable innerhalb einer .column-Klasse erlaubt es, sie in diesen speziellen Kontext zu setzen und den globalen Namespace nicht zu verunreinigen.
Warum rechnet man immer mit * 100%? Ich meine, das ist * (100 / 100), was * 1 ist, das neutrale Element der Multiplikation, was bedeutet, dass es nichts an diesem Produkt ändert. Können Sie es nicht einfach weglassen und noch einen .x% Ihrer Dateigröße sparen? Oder ist das eine Art notwendiger Browser-Hack? Ich habe etwas Ähnliches gemacht, aber dabei Grid-Gap und potenziell Paddings bei Border-Boxed-Elternteilen berücksichtigt.
Ich bin mir nicht sicher, auf welches Beispiel Sie sich genau beziehen, aber wenn ich Sie richtig verstehe: Ich multipliziere mit 100%, um die Einheiten des Wertes auf Prozent zu setzen. z.B. "calc(var(–width) / var(–columns)" würde nur eine einfache Zahl ergeben, sagen wir 0,75. Ich multipliziere es mit 100%, um es auf 75% zu bringen.