Ein Strategieleitfaden für benutzerdefinierte CSS-Eigenschaften

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Dynamische Eigenschaften bieten Möglichkeiten für neue kreative Ideen, aber auch das Potenzial, CSS komplexer zu machen. Um das Beste aus ihnen herauszuholen, benötigen wir möglicherweise eine Strategie, wie wir CSS mit benutzerdefinierten Eigenschaften schreiben und strukturieren.

Benutzerdefinierte CSS-Eigenschaften (manchmal auch als „CSS-Variablen“ bezeichnet) werden jetzt in allen modernen Browsern unterstützt, und die Leute beginnen, sie in der Produktion zu verwenden. Das ist großartig, aber sie unterscheiden sich von Variablen in Präprozessoren, und ich habe bereits viele Beispiele von Leuten gesehen, die sie verwenden, ohne darüber nachzudenken, welche Vorteile sie bieten.

Benutzerdefinierte Eigenschaften haben ein enormes Potenzial, die Art und Weise zu ändern, wie wir CSS schreiben und strukturieren, und in geringerem Maße, wie wir JavaScript verwenden, um mit UI-Komponenten zu interagieren. Ich werde mich nicht auf die Syntax und ihre Funktionsweise konzentrieren (dafür empfehle ich Ihnen, „Es ist Zeit, mit der Verwendung benutzerdefinierter Eigenschaften zu beginnen“). Stattdessen möchte ich einen tieferen Blick auf Strategien werfen, um das Beste aus benutzerdefinierten CSS-Eigenschaften herauszuholen.

Wie ähneln sie Variablen in Präprozessoren?

Benutzerdefinierte Eigenschaften sind ein bisschen wie Variablen in Präprozessoren, weisen aber einige wichtige Unterschiede auf. Der erste und offensichtlichste Unterschied ist die Syntax.

Bei SCSS verwenden wir ein Dollarzeichen, um eine Variable zu bezeichnen:

 $smashing-red: #d33a2c;

In Less verwenden wir ein @ -Symbol:

 @smashing-red: #d33a2c;

Benutzerdefinierte Eigenschaften folgen ähnlichen Konventionen und verwenden ein -- :

 :root { --smashing-red: #d33a2c; } .smashing-text { color: var(--smashing-red); }

Ein wichtiger Unterschied zwischen benutzerdefinierten Eigenschaften und Variablen in Präprozessoren besteht darin, dass benutzerdefinierte Eigenschaften eine andere Syntax zum Zuweisen eines Werts und zum Abrufen dieses Werts haben. Beim Abrufen des Werts einer benutzerdefinierten Eigenschaft verwenden wir die Funktion var() .

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Der nächst offensichtlichste Unterschied liegt im Namen. Sie werden „benutzerdefinierte Eigenschaften“ genannt, weil sie wirklich CSS-Eigenschaften sind. In Präprozessoren können Sie Variablen fast überall deklarieren und verwenden, einschließlich außerhalb von Deklarationsblöcken, in Medienregeln oder sogar als Teil eines Selektors.

 $breakpoint: 800px; $smashing-red: #d33a2c; $smashing-things: ".smashing-text, .cats"; @media screen and (min-width: $breakpoint) { #{$smashing-things} { color: $smashing-red; } }

Die meisten der obigen Beispiele wären mit benutzerdefinierten Eigenschaften ungültig.

Benutzerdefinierte Eigenschaften haben die gleichen Regeln darüber, wo sie verwendet werden können wie normale CSS-Eigenschaften. Es ist viel besser, sie als dynamische Eigenschaften als als Variablen zu betrachten. Das bedeutet, dass sie nur innerhalb eines Deklarationsblocks verwendet werden können, oder mit anderen Worten, benutzerdefinierte Eigenschaften sind an einen Selektor gebunden. Dies kann der :root Selektor oder jeder andere gültige Selektor sein.

 :root { --smashing-red: #d33a2c; } @media screen and (min-width: 800px) { .smashing-text, .cats { --margin-left: 1em; } }

Sie können den Wert einer benutzerdefinierten Eigenschaft überall dort abrufen, wo Sie sonst einen Wert in einer Eigenschaftsdeklaration verwenden würden. Das bedeutet, dass sie als einzelner Wert, als Teil einer Kurzformanweisung oder sogar innerhalb von calc() Gleichungen verwendet werden können.

 .smashing-text, .cats { color: var(--smashing-red); margin: 0 var(--margin-horizontal); padding: calc(var(--margin-horizontal) / 2) }

Sie können jedoch nicht in Medienabfragen oder Selektoren einschließlich :nth-child() verwendet werden.

Es gibt wahrscheinlich noch viel mehr, was Sie über die Syntax und die Funktionsweise benutzerdefinierter Eigenschaften wissen möchten, z. B. wie Fallback-Werte verwendet werden und ob Sie Variablen anderen Variablen zuweisen können (ja), aber diese grundlegende Einführung sollte ausreichen, um den Rest zu verstehen die Konzepte in diesem Artikel. Weitere Informationen zu den Besonderheiten der Funktionsweise von benutzerdefinierten Eigenschaften finden Sie in „Es ist an der Zeit, mit der Verwendung benutzerdefinierter Eigenschaften zu beginnen“, geschrieben von Serg Hospodarets.

Dynamisch vs. statisch

Abgesehen von kosmetischen Unterschieden besteht der bedeutendste Unterschied zwischen Variablen in Präprozessoren und benutzerdefinierten Eigenschaften in ihrem Geltungsbereich. Wir können auf Variablen entweder mit statischem oder dynamischem Gültigkeitsbereich verweisen. Variablen in Präprozessoren sind statisch, während benutzerdefinierte Eigenschaften dynamisch sind.

Statisch bedeutet für CSS, dass Sie den Wert einer Variablen an verschiedenen Stellen im Kompilierungsprozess aktualisieren können, aber den Wert des davor liegenden Codes nicht ändern können.

 $background: blue; .blue { background: $background; } $background: red; .red { background: $background; }

ergibt:

 .blue { background: blue; } .red { background: red; }

Sobald dies in CSS gerendert wird, sind die Variablen weg. Das bedeutet, dass wir möglicherweise eine .scss -Datei lesen und ihre Ausgabe bestimmen könnten, ohne etwas über HTML, Browser oder andere Eingaben zu wissen. Dies ist bei benutzerdefinierten Eigenschaften nicht der Fall.

Präprozessoren haben eine Art „Blockbereich“, in dem Variablen innerhalb eines Selektors, einer Funktion oder eines Mixins vorübergehend geändert werden können. Dies ändert den Wert einer Variablen innerhalb des Blocks, ist aber immer noch statisch. Dies ist an den Block gebunden, nicht an den Selektor. Im Beispiel unten wird die Variable $background innerhalb des .example -Blocks geändert. Außerhalb des Blocks wechselt er wieder auf den Anfangswert, auch wenn wir denselben Selektor verwenden.

 $background: red; .example { $background: blue; background: $background; } .example { background: $background; }

Dies führt zu:

 .example { background: blue; } .example { background: red; }

Benutzerdefinierte Eigenschaften funktionieren anders. Bei benutzerdefinierten Eigenschaften bedeutet dynamischer Geltungsbereich, dass sie der Vererbung und der Kaskade unterliegen. Die Eigenschaft ist an einen Selektor gebunden und wenn sich der Wert ändert, wirkt sich dies wie jede andere CSS-Eigenschaft auf alle passenden DOM-Elemente aus.

Das ist großartig, weil Sie den Wert einer benutzerdefinierten Eigenschaft innerhalb einer Medienabfrage mit einem Pseudoselektor wie Hover oder sogar mit JavaScript ändern können.

 a { --link-color: black; } a:hover, a:focus { --link-color: tomato; } @media screen and (min-width: 600px) { a { --link-color: blue; } } a { color: var(--link-color); }

Wir müssen nicht ändern, wo die benutzerdefinierte Eigenschaft verwendet wird – wir ändern den Wert der benutzerdefinierten Eigenschaft mit CSS. Das bedeutet, dass wir bei Verwendung derselben benutzerdefinierten Eigenschaft unterschiedliche Werte an verschiedenen Stellen oder im Kontext auf derselben Seite haben können.

Global vs. Lokal

Variablen können nicht nur statisch oder dynamisch sein, sondern auch entweder global oder lokal. Wenn Sie JavaScript schreiben, werden Sie damit vertraut sein. Variablen können entweder auf alles innerhalb einer Anwendung angewendet werden, oder ihr Geltungsbereich kann auf bestimmte Funktionen oder Codeblöcke beschränkt werden.

CSS ist ähnlich. Wir haben einige Dinge, die global angewendet werden, und einige Dinge, die eher lokal sind. Markenfarben, vertikale Abstände und Typografie sind alles Beispiele für Dinge, die Sie möglicherweise global und konsistent auf Ihrer Website oder Anwendung anwenden möchten. Wir haben auch lokale Sachen. Beispielsweise kann eine Schaltflächenkomponente eine kleine und eine große Variante haben. Sie möchten nicht, dass die Größen dieser Schaltflächen auf alle Eingabeelemente oder sogar auf alle Elemente auf der Seite angewendet werden.

Das kennen wir von CSS. Wir haben Designsysteme, Namenskonventionen und JavaScript-Bibliotheken entwickelt, die alle dabei helfen, lokale Komponenten und globale Designelemente zu isolieren. Benutzerdefinierte Eigenschaften bieten neue Optionen für den Umgang mit diesem alten Problem.

Benutzerdefinierte CSS-Eigenschaften sind standardmäßig lokal auf die spezifischen Selektoren beschränkt, auf die wir sie anwenden. Sie sind also wie lokale Variablen. Benutzerdefinierte Eigenschaften werden jedoch auch vererbt, sodass sie sich in vielen Situationen wie globale Variablen verhalten – insbesondere, wenn sie auf den :root Selektor angewendet werden. Das bedeutet, dass wir uns Gedanken darüber machen müssen, wie wir sie verwenden.

So viele Beispiele zeigen, dass benutzerdefinierte Eigenschaften auf das :root Element angewendet werden, und obwohl dies für eine Demo in Ordnung ist, kann es zu einem chaotischen globalen Bereich und unbeabsichtigten Problemen mit der Vererbung führen. Glücklicherweise haben wir diese Lektionen bereits gelernt.

Globale Variablen neigen dazu, statisch zu sein

Es gibt ein paar kleine Ausnahmen, aber im Allgemeinen sind die meisten globalen Dinge in CSS auch statisch.

Globale Variablen wie Markenfarben, Typografie und Abstände ändern sich in der Regel nicht wesentlich von einer Komponente zur nächsten. Wenn sie sich ändern, handelt es sich in der Regel um ein globales Rebranding oder eine andere bedeutende Änderung, die bei einem ausgereiften Produkt selten vorkommt. Es macht immer noch Sinn, dass diese Dinge Variablen sind, sie werden an vielen Stellen verwendet, und Variablen helfen bei der Konsistenz. Aber es macht keinen Sinn, dass sie dynamisch sind. Der Wert dieser Variablen ändert sich nicht dynamisch.

Aus diesem Grund empfehle ich dringend, Präprozessoren für globale (statische) Variablen zu verwenden. Dies stellt nicht nur sicher, dass sie immer statisch sind, sondern kennzeichnet sie auch visuell innerhalb des Codes. Dadurch kann CSS viel besser lesbar und einfacher zu warten sein.

Lokale statische Variablen sind in Ordnung (manchmal)

Angesichts der starken Haltung, dass globale Variablen statisch sind, könnten Sie denken, dass alle lokalen Variablen möglicherweise dynamisch sein müssen. Es stimmt zwar, dass lokale Variablen dazu neigen, dynamisch zu sein, aber dies ist bei weitem nicht so stark wie die Tendenz, dass eine globale Variable statisch ist.

Lokal statische Variablen sind in vielen Situationen völlig in Ordnung. Ich verwende Präprozessorvariablen in Komponentendateien hauptsächlich aus Gründen der Entwicklerfreundlichkeit.

Betrachten Sie das klassische Beispiel einer Schaltflächenkomponente mit mehreren Größenvariationen.

Tasten

Mein scss könnte in etwa so aussehen:

 $button-sml: 1em; $button-med: 1.5em; $button-lrg: 2em; .btn { // Visual styles } .btn-sml { font-size: $button-sml; } .btn-med { font-size: $button-med; } .btn-lrg { font-size: $button-lrg; }

Offensichtlich wäre dieses Beispiel sinnvoller, wenn ich die Variablen mehrfach verwenden oder Werte für Rand und Polsterung aus den Größenvariablen ableiten würde. Die Fähigkeit, schnell Prototypen verschiedener Größen zu erstellen, könnte jedoch ein ausreichender Grund sein.

Da die meisten statischen Variablen global sind, unterscheide ich gerne statische Variablen, die nur innerhalb einer Komponente verwendet werden. Dazu können Sie diesen Variablen den Komponentennamen voranstellen oder ein anderes Präfix verwenden, z. B. c-variable-name für Komponente oder l-variable-name für lokal. Sie können ein beliebiges Präfix verwenden oder globalen Variablen ein Präfix voranstellen. Was auch immer Sie wählen, es ist hilfreich, zu differenzieren, insbesondere wenn Sie eine vorhandene Codebasis konvertieren, um benutzerdefinierte Eigenschaften zu verwenden.

Wann Sie benutzerdefinierte Eigenschaften verwenden sollten

Wenn es in Ordnung ist, statische Variablen innerhalb von Komponenten zu verwenden, wann sollten wir benutzerdefinierte Eigenschaften verwenden? Das Konvertieren vorhandener Präprozessorvariablen in benutzerdefinierte Eigenschaften ist normalerweise wenig sinnvoll. Der Grund für benutzerdefinierte Eigenschaften ist schließlich ein ganz anderer. Benutzerdefinierte Eigenschaften sind sinnvoll, wenn wir CSS-Eigenschaften haben, die sich relativ zu einer Bedingung im DOM ändern – insbesondere einer dynamischen Bedingung wie :focus , :hover , Medienabfragen oder mit JavaScript.

Ich vermute, dass wir immer irgendeine Form von statischen Variablen verwenden werden, obwohl wir in Zukunft möglicherweise weniger benötigen werden, da benutzerdefinierte Eigenschaften neue Möglichkeiten bieten, Logik und Code zu organisieren. Bis dahin denke ich, dass wir in den meisten Situationen mit einer Kombination aus Präprozessorvariablen und benutzerdefinierten Eigenschaften arbeiten werden.

Es ist hilfreich zu wissen, dass wir benutzerdefinierten Eigenschaften statische Variablen zuweisen können. Unabhängig davon, ob sie global oder lokal sind, ist es in vielen Situationen sinnvoll, statische Variablen in lokal dynamische benutzerdefinierte Eigenschaften umzuwandeln.

Hinweis : Wussten Sie, dass $var ein gültiger Wert für eine benutzerdefinierte Eigenschaft ist? Neuere Versionen von Sass erkennen dies, und deshalb müssen wir Variablen, die benutzerdefinierten Eigenschaften zugewiesen sind, wie folgt interpolieren: #{$var} . Dies teilt Sass mit, dass Sie den Wert der Variablen ausgeben möchten und nicht nur $var im Stylesheet. Dies ist nur für Situationen wie benutzerdefinierte Eigenschaften erforderlich, in denen Variablennamen auch ein gültiges CSS sein können.

Wenn wir das obige Schaltflächenbeispiel nehmen und entscheiden, dass alle Schaltflächen die kleine Variante auf mobilen Geräten verwenden sollten, unabhängig von der im HTML angewendeten Klasse, ist dies jetzt eine dynamischere Situation. Dafür sollten wir benutzerdefinierte Eigenschaften verwenden.

 $button-sml: 1em; $button-med: 1.5em; $button-lrg: 2em; .btn { --button-size: #{$button-sml}; } @media screen and (min-width: 600px) { .btn-med { --button-size: #{$button-med}; } .btn-lrg { --button-size: #{$button-lrg}; } } .btn { font-size: var(--button-size); }

Hier erstelle ich eine einzelne benutzerdefinierte Eigenschaft: --button-size . Diese benutzerdefinierte Eigenschaft ist zunächst auf alle Schaltflächenelemente beschränkt, die die btn -Klasse verwenden. Ich ändere dann den Wert von --button-size über 600px für die Klassen btn-med und btn-lrg . Schließlich wende ich diese benutzerdefinierte Eigenschaft auf alle Schaltflächenelemente an einer Stelle an.

Seien Sie nicht zu schlau

Die dynamische Natur benutzerdefinierter Eigenschaften ermöglicht es uns, einige clevere und komplizierte Komponenten zu erstellen.

Mit der Einführung von Präprozessoren haben viele von uns mithilfe von Mixins und benutzerdefinierten Funktionen Bibliotheken mit cleveren Abstraktionen erstellt. In begrenzten Fällen sind solche Beispiele auch heute noch nützlich, aber je länger ich mit Präprozessoren arbeite, desto weniger Funktionen verwende ich meistens. Heute verwende ich Präprozessoren fast ausschließlich für statische Variablen.

Benutzerdefinierte Eigenschaften werden (und sollten) nicht immun gegen diese Art von Experimenten sein, und ich freue mich darauf, viele clevere Beispiele zu sehen. Aber auf lange Sicht wird lesbarer und wartbarer Code immer gegen clevere Abstraktionen gewinnen (zumindest in der Produktion).

Ich habe kürzlich einen ausgezeichneten Artikel zu diesem Thema auf dem Free Code Camp Medium gelesen. Es wurde von Bill Sourour geschrieben und heißt „Don't Do It At Runtime. Machen Sie es zur Entwurfszeit.“ Anstatt seine Argumente zu paraphrasieren, lasse ich Sie sie lesen.

Ein wesentlicher Unterschied zwischen Präprozessorvariablen und benutzerdefinierten Eigenschaften besteht darin, dass benutzerdefinierte Eigenschaften zur Laufzeit funktionieren. Dies bedeutet, dass Dinge, die in Bezug auf die Komplexität mit Präprozessoren vielleicht grenzwertig akzeptabel waren, mit benutzerdefinierten Eigenschaften möglicherweise keine gute Idee sind.

Ein Beispiel, das mir das kürzlich verdeutlicht hat, war dieses:

 :root { --font-scale: 1.2; --font-size-1: calc(var(--font-scale) * var(--font-size-2)); --font-size-2: calc(var(--font-scale) * var(--font-size-3)); --font-size-3: calc(var(--font-scale) * var(--font-size-4)); --font-size-4: 1rem; }

Dadurch entsteht eine modulare Skala. Eine modulare Skala ist eine Reihe von Zahlen, die über ein Verhältnis zueinander in Beziehung stehen. Sie werden häufig im Webdesign und in der Entwicklung verwendet, um Schriftgrößen oder Abstände festzulegen.

In diesem Beispiel wird jede benutzerdefinierte Eigenschaft mit calc() bestimmt, indem der Wert der vorherigen benutzerdefinierten Eigenschaft genommen und mit dem Verhältnis multipliziert wird. Dadurch können wir die nächste Zahl in der Skala erhalten.

Das bedeutet, dass die Verhältnisse zur Laufzeit berechnet werden und Sie sie ändern können, indem Sie nur den Wert der Eigenschaft --font-scale aktualisieren. Zum Beispiel:

 @media screen and (min-width: 800px) { :root { --font-scale: 1.33; } }

Das ist clever, übersichtlich und viel schneller, als alle Werte neu zu berechnen, wenn Sie die Skala ändern möchten. Es ist auch etwas, was ich nicht im Produktionscode tun würde.

Obwohl das obige Beispiel für das Prototyping nützlich ist, würde ich in der Produktion viel lieber so etwas sehen:

 :root { --font-size-1: 1.728rem; --font-size-2: 1.44rem; --font-size-3: 1.2em; --font-size-4: 1em; } @media screen and (min-width: 800px) { :root { --font-size-1: 2.369rem; --font-size-2: 1.777rem; --font-size-3: 1.333rem; --font-size-4: 1rem; } }

Ähnlich wie im Beispiel in Bills Artikel finde ich es hilfreich zu sehen, was die tatsächlichen Werte sind. Wir lesen Code viel häufiger als wir ihn schreiben, und globale Werte wie Schriftskalen ändern sich in der Produktion nur selten.

Das obige Beispiel ist immer noch nicht perfekt. Es verstößt gegen die frühere Regel, dass globale Werte statisch sein sollten . Ich würde es vorziehen, Präprozessorvariablen zu verwenden und sie mit den zuvor gezeigten Techniken in lokal dynamische benutzerdefinierte Eigenschaften umzuwandeln.

Es ist auch wichtig, Situationen zu vermeiden, in denen wir von der Verwendung einer benutzerdefinierten Eigenschaft zu einer anderen benutzerdefinierten Eigenschaft übergehen. Dies kann passieren, wenn wir Eigenschaften wie diese benennen.

Ändern Sie den Wert, nicht die Variable

Das Ändern des Werts und nicht der Variablen ist eine der wichtigsten Strategien zur effektiven Verwendung benutzerdefinierter Eigenschaften.

Als allgemeine Regel sollten Sie niemals ändern, welche benutzerdefinierte Eigenschaft für einen bestimmten Zweck verwendet wird. Es ist einfach, weil wir die Dinge mit Präprozessoren genau so machen, aber es macht wenig Sinn mit benutzerdefinierten Eigenschaften.

In diesem Beispiel haben wir zwei benutzerdefinierte Eigenschaften, die für eine Beispielkomponente verwendet werden. Ich wechsle je nach Bildschirmgröße von der Verwendung des Werts von --font-size-small zu --font-size-large .

 :root { --font-size-small: 1.2em; --font-size-large: 2em; } .example { font-size: var(--font-size-small); } @media screen and (min-width: 800px) { .example { font-size: var(--font-size-large); } }

Ein besserer Weg, dies zu tun, wäre, eine einzelne benutzerdefinierte Eigenschaft zu definieren, die auf die Komponente beschränkt ist. Ändern Sie dann mit einer Medienabfrage oder einem anderen Selektor seinen Wert.

 .example { --example-font-size: 1.2em; } @media screen and (min-width: 800px) { .example { --example-font-size: 2em; } }

Schließlich verwende ich an einer einzigen Stelle den Wert dieser benutzerdefinierten Eigenschaft:

 .example { font-size: var(--example-font-size); }

In diesem und anderen Beispielen davor wurden Medienabfragen nur verwendet, um den Wert von benutzerdefinierten Eigenschaften zu ändern. Möglicherweise stellen Sie auch fest, dass die var() Anweisung nur an einer Stelle verwendet wird und reguläre CSS-Eigenschaften aktualisiert werden.

Diese Trennung zwischen Variablendeklarationen und Eigenschaftsdeklarationen ist beabsichtigt. Dafür gibt es viele Gründe, aber die Vorteile werden am deutlichsten, wenn man an Responsive Design denkt.

Responsive Design mit benutzerdefinierten Eigenschaften

Eine der Schwierigkeiten beim Responsive Design, wenn es sich stark auf Medienabfragen stützt, besteht darin, dass unabhängig davon, wie Sie Ihr CSS organisieren, Stile, die sich auf eine bestimmte Komponente beziehen, über das Stylesheet fragmentiert werden.

Es kann sehr schwierig sein zu wissen, welche CSS-Eigenschaften sich ändern werden. Dennoch können benutzerdefinierte CSS-Eigenschaften uns dabei helfen, einen Teil der Logik im Zusammenhang mit responsivem Design zu organisieren und die Arbeit mit Medienabfragen erheblich zu vereinfachen.

Wenn es sich ändert, ist es eine Variable

Eigenschaften, die sich mithilfe von Medienabfragen ändern, sind von Natur aus dynamisch, und benutzerdefinierte Eigenschaften bieten die Möglichkeit, dynamische Werte in CSS auszudrücken. Wenn Sie also eine Medienabfrage verwenden, um eine CSS-Eigenschaft zu ändern, sollten Sie diesen Wert in einer benutzerdefinierten Eigenschaft platzieren.

Sie können dies dann zusammen mit allen Medienregeln, Hover-Status oder dynamischen Selektoren, die definieren, wie sich der Wert ändert, an den Anfang des Dokuments verschieben.

Logik vom Design trennen

Bei richtiger Ausführung bedeutet die Trennung von Logik und Design, dass Medienabfragen nur verwendet werden, um den Wert von benutzerdefinierten Eigenschaften zu ändern . Das bedeutet, dass die gesamte Logik im Zusammenhang mit responsivem Design am Anfang des Dokuments stehen sollte, und wo immer wir eine var() Anweisung in unserem CSS sehen, wissen wir sofort, dass sich diese Eigenschaft ändert. Bei herkömmlichen Methoden zum Schreiben von CSS war dies nicht auf einen Blick erkennbar.

Viele von uns wurden sehr gut darin, CSS auf einen Blick zu lesen und zu interpretieren, während sie im Kopf verfolgten, welche Eigenschaften sich in verschiedenen Situationen geändert haben. Ich bin es leid und ich will das nicht mehr tun! Benutzerdefinierte Eigenschaften stellen jetzt eine Verbindung zwischen Logik und ihrer Implementierung her, sodass wir dies nicht nachverfolgen müssen, und das ist unglaublich nützlich!

Die logische Falte

Die Idee, Variablen am Anfang eines Dokuments oder einer Funktion zu deklarieren, ist keine neue Idee. Das machen wir in den meisten Sprachen, und jetzt können wir es auch in CSS machen. Das Schreiben von CSS auf diese Weise schafft eine klare visuelle Unterscheidung zwischen CSS am Anfang des Dokuments und darunter. Ich brauche eine Möglichkeit, diese Abschnitte zu unterscheiden, wenn ich darüber spreche, und die Idee einer „logischen Faltung“ ist eine Metapher, die ich zu verwenden begonnen habe.
Above the fold enthält alle Präprozessorvariablen und benutzerdefinierten Eigenschaften. Dazu gehören alle unterschiedlichen Werte, die eine benutzerdefinierte Eigenschaft haben kann. Es sollte einfach sein, nachzuvollziehen, wie sich eine benutzerdefinierte Eigenschaft ändert.

CSS „below the fold“ ist unkompliziert und sehr aussagekräftig und leicht zu lesen. Es fühlt sich an wie CSS vor Medienabfragen und anderen notwendigen Komplexitäten des modernen CSS.

Schauen Sie sich ein wirklich einfaches Beispiel für ein sechsspaltiges Flexbox-Rastersystem an:

 .row { --row-display: block; } @media screen and (min-width: 600px) { .row { --row-display: flex; } }

Die benutzerdefinierte Eigenschaft --row-display ist anfänglich auf block gesetzt. Oberhalb von 600 Pixeln ist der Anzeigemodus auf Flex eingestellt.

Below the fold könnte so aussehen:

 .row { display: var(--row-display); flex-direction: row; flex-wrap: nowrap; } .col-1, .col-2, .col-3, .col-4, .col-5, .col-6 { flex-grow: 0; flex-shrink: 0; } .col-1 { flex-basis: 16.66%; } .col-2 { flex-basis: 33.33%; } .col-3 { flex-basis: 50%; } .col-4 { flex-basis: 66.66%; } .col-5 { flex-basis: 83.33%; } .col-6 { flex-basis: 100%; }

Wir wissen sofort, dass --row-display ein Wert ist, der sich ändert. Anfangs ist es block , also werden die Flex-Werte ignoriert.

Dieses Beispiel ist ziemlich einfach, aber wenn wir es um eine Spalte mit flexibler Breite erweitern würden, die den verbleibenden Platz ausfüllt, müssten wahrscheinlich die Werte flex-grow , flex-shrink und flex-basis in benutzerdefinierte Eigenschaften konvertiert werden. Sie können dies ausprobieren oder sich hier ein ausführlicheres Beispiel ansehen.

Benutzerdefinierte Eigenschaften für Themen

Ich habe mich hauptsächlich gegen die Verwendung benutzerdefinierter Eigenschaften für globale dynamische Variablen ausgesprochen und hoffentlich impliziert, dass das Anhängen benutzerdefinierter Eigenschaften an den :root Selektor in vielen Fällen als schädlich angesehen wird. Aber jede Regel hat eine Ausnahme, und für benutzerdefinierte Eigenschaften ist es das Thema.

Die eingeschränkte Verwendung globaler benutzerdefinierter Eigenschaften kann die Gestaltung erheblich vereinfachen.

Theming bezieht sich im Allgemeinen darauf, Benutzern die Möglichkeit zu geben, die Benutzeroberfläche auf irgendeine Weise anzupassen. Dies könnte so etwas wie das Ändern von Farben auf einer Profilseite sein. Oder es könnte etwas Lokaleres sein. Sie können beispielsweise die Farbe einer Notiz in der Google Keep-Anwendung auswählen.

Google Keep-App

Das Thematisieren beinhaltet normalerweise das Kompilieren eines separaten Stylesheets, um einen Standardwert mit Benutzereinstellungen zu überschreiben, oder das Kompilieren eines anderen Stylesheets für jeden Benutzer. Beides kann schwierig sein und sich auf die Leistung auswirken.

Mit benutzerdefinierten Eigenschaften müssen wir kein anderes Stylesheet kompilieren; Wir müssen nur den Wert der Eigenschaften gemäß den Präferenzen des Benutzers aktualisieren. Da es sich um geerbte Werte handelt, können sie überall in unserer Anwendung verwendet werden, wenn wir dies für das Stammelement tun.

Kapitalisieren Sie globale dynamische Eigenschaften

Bei benutzerdefinierten Eigenschaften wird zwischen Groß- und Kleinschreibung unterschieden, und da die meisten benutzerdefinierten Eigenschaften lokal sind, kann es sinnvoll sein, sie bei Verwendung globaler dynamischer Eigenschaften großzuschreiben.

 :root { --THEME-COLOR: var(--user-theme-color, #d33a2c); }

Die Großschreibung von Variablen bedeutet oft globale Konstanten. Für uns bedeutet dies, dass die Eigenschaft an anderer Stelle in der Anwendung festgelegt ist und wir sie wahrscheinlich nicht lokal ändern sollten.

Vermeiden Sie das direkte Festlegen globaler dynamischer Eigenschaften

Benutzerdefinierte Eigenschaften akzeptieren einen Fallback-Wert. Es kann hilfreich sein, das direkte Überschreiben des Werts einer globalen benutzerdefinierten Eigenschaft zu vermeiden und Benutzerwerte getrennt zu halten. Dazu können wir den Fallback-Wert verwenden.

Das obige Beispiel setzt den Wert von --THEME-COLOR auf den Wert von --user-theme-color , falls vorhanden. Wenn --user-theme-color nicht gesetzt ist, wird der Wert von #d33a2c verwendet. Auf diese Weise müssen wir nicht jedes Mal einen Fallback bereitstellen, wenn wir --THEME-COLOR verwenden.

Im Beispiel unten erwarten Sie vielleicht, dass der Hintergrund auf green gesetzt wird. Der Wert von --user-theme-color wurde jedoch nicht für das Stammelement festgelegt, sodass sich der Wert von --THEME-COLOR nicht geändert hat.

 :root { --THEME-COLOR: var(--user-theme-color, #d33a2c); } body { --user-theme-color: green; background: var(--THEME-COLOR); }

Das indirekte Festlegen globaler dynamischer Eigenschaften auf diese Weise schützt sie davor, lokal überschrieben zu werden, und stellt sicher, dass Benutzereinstellungen immer vom Stammelement geerbt werden. Dies ist eine nützliche Konvention, um Ihre Themenwerte zu schützen und eine unbeabsichtigte Vererbung zu vermeiden.

Wenn wir bestimmte Eigenschaften der Vererbung aussetzen möchten, können wir den :root Selektor durch einen * -Selektor ersetzen:

 * { --THEME-COLOR: var(--user-theme-color, #d33a2c); } body { --user-theme-color: green; background: var(--THEME-COLOR); }

Nun wird der Wert von --THEME-COLOR für jedes Element neu berechnet und somit kann der lokale Wert von --user-theme-color verwendet werden. Mit anderen Worten, die Hintergrundfarbe in diesem Beispiel ist green .

Sie können einige detailliertere Beispiele dieses Musters im Abschnitt über das Manipulieren von Farben mit benutzerdefinierten Eigenschaften sehen.

Aktualisieren benutzerdefinierter Eigenschaften mit JavaScript

Wenn Sie benutzerdefinierte Eigenschaften mit JavaScript festlegen möchten, gibt es eine ziemlich einfache API, die so aussieht:

 const elm = document.documentElement; elm.style.setProperty('--USER-THEME-COLOR', 'tomato');

Hier setze ich den Wert von --USER-THEME-COLOR auf das Dokumentelement, oder mit anderen Worten, das :root Element, wo es von allen Elementen geerbt wird.

Dies ist keine neue API; Es ist dieselbe JavaScript-Methode zum Aktualisieren von Stilen für ein Element. Dies sind Inline-Stile, daher haben sie eine höhere Spezifität als normales CSS.

Dies bedeutet, dass es einfach ist, lokale Anpassungen anzuwenden:

 .note { --note-color: #eaeaea; } .note { background: var(--note-color); }

Hier setze ich einen Standardwert für --note-color und beziehe diesen auf die .note -Komponente. Auch in diesem einfachen Beispiel halte ich die Variablendeklaration von der Eigenschaftsdeklaration getrennt.

 const elm = document.querySelector('#note-uid'); elm.style.setProperty('--note-color', 'yellow');

Ich ziele dann auf eine bestimmte Instanz eines .note -Elements ab und ändere den Wert der benutzerdefinierten Eigenschaft --note-color nur für dieses Element. Dies hat jetzt eine höhere Spezifität als der Standardwert.

Wie das funktioniert, sehen Sie an diesem Beispiel mit React. Diese Benutzereinstellungen könnten in einem lokalen Speicher oder, vielleicht im Fall einer größeren Anwendung, in einer Datenbank gespeichert werden.

Manipulieren von Farbe mit benutzerdefinierten Eigenschaften

Zusätzlich zu Hex-Werten und benannten Farben verfügt CSS über Farbfunktionen wie rgb() und hsl() . Diese erlauben es uns, einzelne Bestandteile einer Farbe wie den Farbton oder die Helligkeit zu spezifizieren. Benutzerdefinierte Eigenschaften können in Verbindung mit Farbfunktionen verwendet werden.

 :root { --hue: 25; } body { background: hsl(var(--hue), 80%, 50%); }

Dies ist nützlich, aber einige der am häufigsten verwendeten Funktionen von Präprozessoren sind erweiterte Farbfunktionen, die es uns ermöglichen, Farben mit Funktionen wie Aufhellen, Abdunkeln oder Entsättigen zu manipulieren:

 darken($base-color, 10%); lighten($base-color, 10%); desaturate($base-color, 20%);

Es wäre nützlich, einige dieser Funktionen in Browsern zu haben. Sie kommen, aber bis wir native Funktionen zur Farbänderung in CSS haben, könnten benutzerdefinierte Eigenschaften einen Teil dieser Lücke füllen.

Wir haben gesehen, dass benutzerdefinierte Eigenschaften in bestehenden Farbfunktionen wie rgb() und hsl() verwendet werden können, aber sie können auch in calc() verwendet werden. Das bedeutet, dass wir eine reelle Zahl in eine Prozentzahl umwandeln können, indem wir sie multiplizieren, zB calc(50 * 1%) = 50% .

 :root { --lightness: 50; } body { background: hsl(25, 80%, calc(var(--lightness) * 1%)); }

Der Grund, warum wir den Helligkeitswert als reelle Zahl speichern möchten, ist, dass wir ihn mit calc manipulieren können, bevor wir ihn in einen Prozentsatz umwandeln. Wenn ich beispielsweise eine Farbe um 20% abdunkeln möchte, kann ich ihre Helligkeit mit 0.8 multiplizieren. Wir können dies etwas leichter lesbar machen, indem wir die Helligkeitsberechnung in eine lokal begrenzte benutzerdefinierte Eigenschaft unterteilen:

 :root { --lightness: 50; } body { --lightness: calc(var(--lightness * 0.8)); background: hsl(25, 80%, calc(var(--lightness) * 1%)); }

Wir könnten sogar mehr von den Berechnungen abstrahieren und mithilfe benutzerdefinierter Eigenschaften so etwas wie Farbmodifikationsfunktionen in CSS erstellen. Dieses Beispiel ist wahrscheinlich zu komplex für die meisten praktischen Fälle von Themen, aber es demonstriert die volle Leistungsfähigkeit dynamischer benutzerdefinierter Eigenschaften.

Vereinfachen Sie die Thematisierung

Einer der Vorteile der Verwendung benutzerdefinierter Eigenschaften ist die Möglichkeit, die Gestaltung zu vereinfachen. Die Anwendung muss nicht wissen, wie benutzerdefinierte Eigenschaften verwendet werden. Stattdessen verwenden wir JavaScript oder serverseitigen Code, um den Wert benutzerdefinierter Eigenschaften festzulegen. Wie diese Werte verwendet werden, wird durch die Stylesheets bestimmt.

Das bedeutet noch einmal, dass wir in der Lage sind, Logik von Design zu trennen. Wenn Sie ein technisches Designteam haben, können Autoren Stylesheets aktualisieren und entscheiden, wie benutzerdefinierte Eigenschaften angewendet werden, ohne eine einzige Zeile JavaScript oder Backend-Code zu ändern.

Benutzerdefinierte Eigenschaften ermöglichen es auch, einen Teil der Komplexität des Themas in das CSS zu verschieben, und diese Komplexität kann sich negativ auf die Wartbarkeit Ihres CSS auswirken. Denken Sie also daran, es so einfach wie möglich zu halten.

Benutzerdefinierte Eigenschaften heute verwenden

Auch wenn Sie IE10 und 11 unterstützen, können Sie heute mit der Verwendung benutzerdefinierter Eigenschaften beginnen. Die meisten Beispiele in diesem Artikel haben damit zu tun, wie wir CSS schreiben und strukturieren. Die Vorteile sind in Bezug auf die Wartbarkeit erheblich, die meisten Beispiele reduzieren jedoch nur das, was sonst mit komplexerem Code möglich wäre.

Ich verwende ein Tool namens postcss-css-variables, um die meisten Funktionen benutzerdefinierter Eigenschaften in eine statische Darstellung desselben Codes umzuwandeln. Andere ähnliche Tools ignorieren benutzerdefinierte Eigenschaften in Medienabfragen oder komplexen Selektoren und behandeln benutzerdefinierte Eigenschaften ähnlich wie Präprozessorvariablen.

Was diese Tools nicht können, ist die Laufzeitfunktionen von benutzerdefinierten Eigenschaften zu emulieren. Das bedeutet, dass keine dynamischen Funktionen wie Theming oder das Ändern von Eigenschaften mit JavaScript vorhanden sind. Dies mag in vielen Situationen in Ordnung sein. Je nach Situation kann die Anpassung der Benutzeroberfläche als progressive Verbesserung angesehen werden, und das Standarddesign kann für ältere Browser durchaus akzeptabel sein.

Laden des richtigen Stylesheets

Es gibt viele Möglichkeiten, wie Sie postCSS verwenden können. Ich verwende einen gulp Prozess, um separate Stylesheets für neuere und ältere Browser zu kompilieren. Eine vereinfachte Version meiner gulp Aufgabe sieht so aus:

 import gulp from "gulp"; import sass from "gulp-sass"; import postcss from "gulp-postcss"; import rename from "gulp-rename"; import cssvariables from "postcss-css-variables"; import autoprefixer from "autoprefixer"; import cssnano from "cssnano"; gulp.task("css-no-vars", () => gulp .src("./src/css/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(postcss([cssvariables(), cssnano()])) .pipe(rename({ extname: ".no-vars.css" })) .pipe(gulp.dest("./dist/css")) ); gulp.task("css", () => gulp .src("./src/css/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(postcss([cssnano()])) .pipe(rename({ extname: ".css" })) .pipe(gulp.dest("./dist/css")) );

Dies führt zu zwei CSS-Dateien: eine normale mit benutzerdefinierten Eigenschaften ( styles.css ) und eine für ältere Browser ( styles.no-vars.css ). Ich möchte, dass IE10 und 11 styles.no-vars.css und andere Browser bereitgestellt werden, um die reguläre CSS-Datei zu erhalten.

Normalerweise würde ich die Verwendung von Funktionsabfragen empfehlen, aber IE11 unterstützt keine Funktionsabfragen, und wir haben benutzerdefinierte Eigenschaften so umfassend verwendet, dass die Bereitstellung eines anderen Stylesheets in diesem Fall sinnvoll ist.

Es ist keine einfache Aufgabe, ein anderes Stylesheet intelligent bereitzustellen und ein Aufblitzen von ungestylten Inhalten zu vermeiden. Wenn Sie die dynamischen Funktionen benutzerdefinierter Eigenschaften nicht benötigen, können Sie erwägen, alle Browser styles.no-vars.css und benutzerdefinierte Eigenschaften einfach als Entwicklungstool zu verwenden.

Wenn Sie alle dynamischen Funktionen benutzerdefinierter Eigenschaften voll ausnutzen möchten, empfehle ich die Verwendung einer kritischen CSS-Technik. Nach diesen Techniken wird das Haupt-Stylesheet asynchron geladen, während das kritische CSS inline gerendert wird. Ihr Seitenkopf könnte in etwa so aussehen:

 <head> <style> /* inlined critical CSS */ </style> <script> loadCSS('non-critical.css'); </script> </head>

Wir können dies erweitern, um entweder styles.css oder styles.no-vars.css zu laden, je nachdem, ob der Browser benutzerdefinierte Eigenschaften unterstützt. Wir können Unterstützung wie folgt erkennen:

 if ( window.CSS && CSS.supports('color', 'var(--test)') ) { loadCSS('styles.css'); } else { loadCSS('styles.no-vars.css'); }

Fazit

Wenn Sie Schwierigkeiten haben, CSS effizient zu organisieren, Schwierigkeiten mit reaktionsschnellen Komponenten haben, clientseitiges Design implementieren möchten oder einfach nur mit benutzerdefinierten Eigenschaften auf dem richtigen Fuß beginnen möchten, sollte Ihnen dieser Leitfaden alles geben, was Sie wissen müssen.

Es kommt darauf an, den Unterschied zwischen dynamischen und statischen Variablen in CSS sowie ein paar einfache Regeln zu verstehen:

  1. Logik vom Design trennen;
  2. Wenn sich eine CSS-Eigenschaft ändert, ziehen Sie die Verwendung einer benutzerdefinierten Eigenschaft in Betracht;
  3. Ändern Sie den Wert von benutzerdefinierten Eigenschaften, nicht welche benutzerdefinierte Eigenschaft verwendet wird;
  4. Globale Variablen sind normalerweise statisch.

If you follow these conventions, you will find that working with custom properties is a whole lot easier than you think. This might even change how you approach CSS in general.

Weiterführende Lektüre

  • “It's Time To Start Using Custom Properties,” Serg Hospodarets
    A general introduction to the syntax and the features of custom properties.
  • “Pragmatic, Practical, And Progressive Theming With Custom Properties,” Harry Roberts
    More useful information on theming.
  • Custom Properties Collection, Mike Riethmuller on CodePen
    A number of different examples you can experiment with.