Benutzerdefinierte CSS-Eigenschaften in der Kaskade

Veröffentlicht: 2022-03-10
Kurzzusammenfassung ↬ In diesem Artikel taucht Miriam tiefer in die Spezifikation „CSS Custom Properties for Cascading Variables“ ein und fragt: „Warum werden sie Custom Properties genannt, wie funktionieren sie in der Kaskade und was können wir sonst noch damit machen ?” Über die „Variablen“-Metapher hinaus können benutzerdefinierte Eigenschaften neue Möglichkeiten bieten, Kontext und Isolation in CSS-Mustern und -Komponenten auszugleichen.

Letzten Monat hatte ich ein Gespräch auf Twitter über den Unterschied zwischen „bereichsbezogenen“ Stilen (die in einem Build-Prozess generiert werden) und „verschachtelten“ Stilen, die in CSS nativ sind. Ich habe gefragt, warum Entwickler anekdotisch die Besonderheit von ID-Selektoren vermeiden, während sie von JavaScript generierte „bereichsbezogene Stile“ annehmen? Keith Grant schlug vor, dass der Unterschied darin liegt, die Kaskade* und die Vererbung auszugleichen, dh der Nähe der Spezifität den Vorzug zu geben. Lass uns einen Blick darauf werfen.

Die Kaskade

Die CSS-Kaskade basiert auf drei Faktoren:

  1. Wichtigkeit definiert durch das !important -Flag und Stilherkunft (Benutzer > Autor > Browser)
  2. Spezifität der verwendeten Selektoren (Inline > ID > Klasse > Element)
  3. Quellreihenfolge des Codes selbst (neueste hat Vorrang)

Die Nähe wird nirgendwo erwähnt – die DOM-Baum-Beziehung zwischen Teilen eines Selektors. Die folgenden Absätze werden beide rot sein, obwohl #inner p eine engere Beziehung beschreibt als #outer p für den zweiten Absatz:

Siehe den Stift [Cascade: Specificity vs Proximity](https://codepen.io/smashingmag/pen/OexweJ/) von Miriam Suzanne.

Siehe Pen Cascade: Specificity vs Proximity von Miriam Suzanne.
 <section> <p>This text is red</p> <div> <p>This text is also red!</p> </div> </section>
 #inner p { color: green; } #outer p { color: red; }

Beide Selektoren haben die gleiche Spezifität, sie beschreiben beide das gleiche p -Element und keiner ist als !important gekennzeichnet – das Ergebnis basiert also allein auf der Reihenfolge der Quellen.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

BEM und Scoped Styles

Namenskonventionen wie BEM („Block__Element—Modifier“) werden verwendet, um sicherzustellen, dass jeder Absatz nur auf einen übergeordneten Abschnitt „beschränkt“ ist, wodurch die Kaskade vollständig vermieden wird. Absatz-„Elemente“ erhalten eindeutige Klassen, die für ihren „Block“-Kontext spezifisch sind:

Siehe Pen [BEM Selectors & Proximity](https://codepen.io/smashingmag/pen/qzPyeM/) von Miriam Suzanne.

Siehe Pen BEM Selectors & Proximity von Miriam Suzanne.
 <section class="outer"> <p class="outer__p">This text is red</p> <div class="inner"> <p class="inner__p">This text is green!</p> </div> </section>
 .inner__p { color: green; } .outer__p { color: red; }

Diese Selektoren haben immer noch dieselbe relative Wichtigkeit, Spezifität und Quellenreihenfolge – aber die Ergebnisse sind unterschiedlich. „Scoped“- oder „modulare“ CSS-Tools automatisieren diesen Prozess, indem sie unser CSS für uns basierend auf dem HTML neu schreiben. Im folgenden Code bezieht sich jeder Absatz auf sein direktes übergeordnetes Element:

Siehe Pen [Scoped Style Proximity](https://codepen.io/smashingmag/pen/NZaLWN/) von Miriam Suzanne.

Siehe Pen Scoped Style Proximity von Miriam Suzanne.
 <section outer-scope> <p outer-scope>This text is red</p> <div outer-scope inner-scope> <p inner-scope>This text is green!</p> </div> </section>
 p[inner-scope] { color: green } p[outer-scope] { color: red; }

Nachlass

Proximity ist kein Teil der Kaskade, aber es ist Teil von CSS. Das ist, wo Vererbung wichtig wird. Wenn wir das p aus unseren Selektoren entfernen, erbt jeder Absatz eine Farbe von seinem nächsten Vorfahren:

Siehe Pen [Inheritance: Specificity vs Proximity](https://codepen.io/smashingmag/pen/mZBGyN/) von Miriam Suzanne.

Siehe Pen Inheritance: Specificity vs Proximity von Miriam Suzanne.
 #inner { color: green; } #outer { color: red; }

Da #inner und #outer unterschiedliche Elemente beschreiben, unser div bzw. section , werden beide Farbeigenschaften konfliktfrei angewendet. Für das verschachtelte p -Element ist keine Farbe angegeben, sodass die Ergebnisse durch Vererbung (die Farbe des direkten übergeordneten Elements) und nicht durch cascade bestimmt werden. Nähe hat Vorrang, und der Wert #inner überschreibt den Wert #outer .

Aber es gibt ein Problem: Um die Vererbung zu verwenden, gestalten wir alles in unserem section und div . Wir wollen gezielt auf die Absatzfarbe abzielen.

(Wieder-)Einführung benutzerdefinierter Eigenschaften

Benutzerdefinierte Eigenschaften bieten eine neue, browsernative Lösung; Sie erben wie jede andere Eigenschaft, müssen aber nicht dort verwendet werden, wo sie definiert sind . Mit einfachem CSS, ohne Namenskonventionen oder Build-Tools, können wir einen Stil erstellen, der sowohl zielgerichtet als auch kontextbezogen ist, wobei die Nähe Vorrang vor der Kaskade hat:

Siehe Pen [Custom Props: Specificity vs Proximity](https://codepen.io/smashingmag/pen/gNGdaO/) von Miriam Suzanne.

Siehe Pen Custom Props: Specificity vs Proximity von Miriam Suzanne.
 p { color: var(--paragraph); } #inner { --paragraph: green; } #outer { --paragraph: red; }

Die benutzerdefinierte Eigenschaft --paragraph erbt genauso wie die Eigenschaft color , aber jetzt haben wir die Kontrolle darüber, wie und wo dieser Wert angewendet wird. Die Eigenschaft --paragraph sich ähnlich wie ein Parameter, der an die p -Komponente übergeben werden kann, entweder durch direkte Auswahl (Spezifitätsregeln) oder Kontext (Näherungsregeln).

Ich denke, dies offenbart ein Potenzial für benutzerdefinierte Eigenschaften, die wir oft mit Funktionen, Mixins oder Komponenten in Verbindung bringen.

Benutzerdefinierte „Funktionen“ und Parameter

Funktionen, Mixins und Komponenten basieren alle auf derselben Idee: wiederverwendbarer Code, der mit verschiedenen Eingabeparametern ausgeführt werden kann, um konsistente, aber konfigurierbare Ergebnisse zu erhalten. Der Unterschied besteht darin, was sie mit den Ergebnissen machen. Wir beginnen mit einer gestreiften Gradientenvariablen und können sie dann auf andere Formen erweitern:

 html { --stripes: linear-gradient( to right, powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); }

Diese Variable ist im Root- html -Element definiert (könnte auch :root verwenden, aber das fügt unnötige Spezifität hinzu), sodass unsere Striped-Variable überall im Dokument verfügbar ist. Wir können es überall dort anwenden, wo Farbverläufe unterstützt werden:

Siehe Pen [Custom Props: Variable](https://codepen.io/smashingmag/pen/NZwrrm/) von Miriam Suzanne.

Siehe Pen Custom Props: Variable von Miriam Suzanne.
 body { background-image: var(--stripes); }

Parameter hinzufügen

Funktionen werden wie Variablen verwendet, definieren aber Parameter zur Änderung der Ausgabe. Wir können unsere Variable --stripes so aktualisieren, dass sie funktionsähnlicher ist, indem wir einige parameterähnliche Variablen darin definieren. Ich beginne mit dem Ersetzen von to right durch var(--stripes-angle) , um einen Winkeländerungsparameter zu erstellen:

 html { --stripes: linear-gradient( var(--stripes-angle), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); }

Es gibt andere Parameter, die wir erstellen könnten, je nachdem, welchem ​​Zweck die Funktion dienen soll. Sollten wir Benutzern erlauben, ihre eigenen Streifenfarben auszuwählen? Wenn ja, akzeptiert unsere Funktion 5 verschiedene Farbparameter oder nur 3, die wie jetzt von außen nach innen gehen? Wollen wir auch Parameter für Farbstopps erstellen? Jeder Parameter, den wir hinzufügen, bietet mehr Anpassungsmöglichkeiten auf Kosten von Einfachheit und Konsistenz.

Es gibt keine universell richtige Antwort auf dieses Gleichgewicht – einige Funktionen müssen flexibler sein und andere müssen eigensinniger sein. Abstraktionen dienen dazu, Konsistenz und Lesbarkeit in Ihrem Code zu gewährleisten, also treten Sie einen Schritt zurück und fragen Sie nach Ihren Zielen. Was muss wirklich anpassbar sein und wo sollte Konsistenz erzwungen werden? In einigen Fällen kann es hilfreicher sein, zwei eigenständige Funktionen zu haben, anstatt eine vollständig anpassbare Funktion.

Um die obige Funktion zu verwenden, müssen wir einen Wert für den Parameter --stripes-angle und die Ausgabe auf eine CSS-Ausgabeeigenschaft wie background-image anwenden:

 /* in addition to the code above… */ html { --stripes-angle: 75deg; background-image: var(--stripes); } 

Siehe Stift [Benutzerdefinierte Requisiten: Funktion](https://codepen.io/smashingmag/pen/BgwOjj/) von Miriam Suzanne.

Siehe Pen Custom Props: Function von Miriam Suzanne.

Vererbt versus universell

Aus Gewohnheit habe ich die Funktion --stripes für das html -Element definiert. Benutzerdefinierte Eigenschaften erben, und ich möchte, dass meine Funktion überall verfügbar ist, daher ist es sinnvoll, sie auf das Stammelement zu setzen. Das funktioniert gut zum Vererben von Variablen wie --brand-color: blue , also könnten wir auch erwarten, dass es auch für unsere „Funktion“ funktioniert. Aber wenn wir versuchen, diese Funktion erneut für einen verschachtelten Selektor zu verwenden, funktioniert es nicht:

Siehe Pen [Custom Props: Function Inheritance Fail](https://codepen.io/smashingmag/pen/RzjRrM/) von Miriam Suzanne.

Siehe Pen Custom Props: Function Inheritance Fail von Miriam Suzanne.
 div { --stripes-angle: 90deg; background-image: var(--stripes); }

Der neue --stripes-angle wird vollständig ignoriert. Es stellt sich heraus, dass wir uns bei Funktionen, die neu berechnet werden müssen, nicht auf die Vererbung verlassen können. Das liegt daran, dass jeder Eigenschaftswert einmal pro Element (in unserem Fall das html -Stammelement) berechnet wird und der berechnete Wert dann vererbt wird . Indem wir unsere Funktion im Dokumentstamm definieren, stellen wir den Nachkommen nicht die gesamte Funktion zur Verfügung – nur das berechnete Ergebnis unserer Funktion.

Das macht Sinn, wenn Sie es in Bezug auf den kaskadierenden Parameter --stripes-angle . Wie jede geerbte CSS-Eigenschaft ist sie für Nachkommen, aber nicht für Vorfahren verfügbar. Der Wert, den wir für ein verschachteltes div festlegen, ist für eine Funktion, die wir für den html -Root-Vorfahren definiert haben, nicht verfügbar. Um eine universell verfügbare Funktion zu erstellen, die für jedes Element neu berechnet wird, müssen wir sie für jedes Element definieren:

Siehe Pen [Custom Props: Universal Function](https://codepen.io/smashingmag/pen/agLaNj/) von Miriam Suzanne.

Siehe Pen Custom Props: Universal Function von Miriam Suzanne.
 * { --stripes: linear-gradient( var(--stripes-angle), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); }

Der universelle Selektor macht unsere Funktion überall verfügbar, aber wir können sie enger definieren, wenn wir wollen. Wichtig ist, dass es nur dort nachrechnen kann, wo es explizit definiert ist. Hier sind ein paar alternativen:

 /* make the function available to elements with a given selector */ .stripes { --stripes: /* etc… */; } /* make the function available to elements nested inside a given selector */ .stripes * { --stripes: /* etc… */; } /* make the function available to siblings following a given selector */ .stripes ~ * { --stripes: /* etc… */; } 

Siehe Pen [Custom Props: Scoped Function](https://codepen.io/smashingmag/pen/JQMvGM/) von Miriam Suzanne.

Siehe Pen Custom Props: Scoped Function von Miriam Suzanne.

Dies kann mit jeder Auswahllogik erweitert werden, die nicht auf Vererbung angewiesen ist.

Freie Parameter und Fallback-Werte

In unserem obigen Beispiel hat var(--stripes-angle) keinen Wert und kein Fallback. Im Gegensatz zu Sass- oder JS-Variablen, die definiert oder instanziiert werden müssen, bevor sie aufgerufen werden, können benutzerdefinierte CSS-Eigenschaften aufgerufen werden, ohne jemals definiert zu werden. Dadurch entsteht eine „freie“ Variable, ähnlich einem Funktionsparameter, der aus dem Kontext geerbt werden kann.

Wir können die Variable schließlich in html oder :root (oder einem anderen Vorfahren) definieren, um einen geerbten Wert festzulegen, aber zuerst müssen wir den Fallback berücksichtigen, wenn kein Wert definiert ist. Es gibt mehrere Optionen, je nachdem, welches Verhalten wir genau wollen

  1. Für „erforderliche“ Parameter wollen wir keinen Fallback. So wie sie ist, wird die Funktion nichts tun, bis --stripes-angle definiert ist.
  2. Für „optionale“ Parameter können wir einen Fallback-Wert in der Funktion var() bereitstellen. Nach dem Variablennamen fügen wir ein Komma hinzu, gefolgt vom Standardwert:
 var(--stripes-angle, 90deg)

Jede var() -Funktion kann nur einen Fallback haben – daher sind alle zusätzlichen Kommas Teil dieses Werts. Dadurch ist es möglich, komplexe Vorgaben mit internen Kommas zu versehen:

 html { /* Computed: Hevetica, Ariel, sans-serif */ font-family: var(--sans-family, Hevetica, Ariel, sans-serif); /* Computed: 0 -1px 0 white, 0 1px 0 black */ test-shadow: var(--shadow, 0 -1px 0 white, 0 1px 0 black); }

Wir können auch verschachtelte Variablen verwenden, um unsere eigenen Kaskadenregeln zu erstellen und den verschiedenen Werten unterschiedliche Prioritäten zuzuweisen:

 var(--stripes-angle, var(--global-default-angle, 90deg))
  1. Versuchen Sie zuerst unseren expliziten Parameter ( --stripes-angle );
  2. Fallback auf einen globalen „Benutzerstandard“ ( --user-default-angle ), falls verfügbar;
  3. Fallback zum Schluss auf unsere „Werkseinstellung“ (90deg ).

Siehe Pen [Custom Props: Fallback Values](https://codepen.io/smashingmag/pen/jjGvVm/) von Miriam Suzanne.

Siehe Pen Custom Props: Fallback Values ​​von Miriam Suzanne.

Indem wir Fallback-Werte in var() festlegen, anstatt die benutzerdefinierte Eigenschaft explizit zu definieren, stellen wir sicher, dass es keine Spezifitäts- oder Kaskadenbeschränkungen für den Parameter gibt. Alle *-angle sind „frei“, um von jedem Kontext geerbt zu werden.

Browser-Fallbacks im Vergleich zu variablen Fallbacks

Wenn wir Variablen verwenden, müssen wir zwei Fallback-Pfade beachten:

  1. Welcher Wert sollte von Browsern ohne Variablenunterstützung verwendet werden?
  2. Welcher Wert sollte von Browsern verwendet werden, die Variablen unterstützen, wenn eine bestimmte Variable fehlt oder ungültig ist?
 p { color: blue; color: var(--paragraph); }

Während alte Browser die Variablendeklarationseigenschaft ignorieren und auf blue zurückgreifen, lesen moderne Browser beides und verwenden letzteres. Unser var(--paragraph) ist möglicherweise nicht definiert, aber er ist gültig und überschreibt die vorherige Eigenschaft, sodass Browser mit Variablenunterstützung auf den geerbten oder anfänglichen Wert zurückgreifen, als ob sie das Schlüsselwort unset verwenden würden.

Das mag zunächst verwirrend erscheinen, hat aber gute Gründe dafür. Der erste ist technischer Natur: Browser-Engines verarbeiten ungültige oder unbekannte Syntax zur „Parse-Zeit“ (was zuerst geschieht), aber Variablen werden erst zur „berechneten Wertzeit“ (was später passiert) aufgelöst.

  1. Zur Parse-Zeit werden Deklarationen mit ungültiger Syntax vollständig ignoriert und auf frühere Deklarationen zurückgegriffen. Dies ist der Weg, dem alte Browser folgen werden. Moderne Browser unterstützen die Variablensyntax, sodass die vorherige Deklaration stattdessen verworfen wird.
  2. Zum Zeitpunkt des berechneten Werts wird die Variable als ungültig kompiliert, aber es ist zu spät – die vorherige Deklaration wurde bereits verworfen. Gemäß der Spezifikation werden ungültige Variablenwerte genauso behandelt wie unset :

Siehe Pen [Custom Props: Invalid/Unsupported vs Undefined](https://codepen.io/smashingmag/pen/VJMGbJ/) von Miriam Suzanne.

Siehe Pen Custom Props: Invalid/Unsupported vs Undefined von Miriam Suzanne.
 html { color: red; /* ignored as *invalid syntax* by all browsers */ /* - old browsers: red */ /* - new browsers: red */ color: not a valid color; color: var(not a valid variable name); /* ignored as *invalid syntax* by browsers without var support */ /* valid syntax, but invalid *values* in modern browsers */ /* - old browsers: red */ /* - new browsers: unset (black) */ --invalid-value: not a valid color value; color: var(--undefined-variable); color: var(--invalid-value); }

Dies ist auch gut für uns als Autoren, da wir mit komplexeren Fallbacks für die Browser spielen können, die Variablen unterstützen, und einfache Fallbacks für ältere Browser bereitstellen. Noch besser, das ermöglicht es uns, den null / undefined Zustand zu verwenden, um die erforderlichen Parameter festzulegen. Dies wird besonders wichtig, wenn wir eine Funktion in ein Mixin oder eine Komponente umwandeln möchten.

Benutzerdefinierte Eigenschaft „Mixins“

In Sass geben die Funktionen Rohwerte zurück, während Mixins im Allgemeinen die tatsächliche CSS-Ausgabe mit Eigenschaft-Wert-Paaren zurückgeben. Wenn wir eine universelle Eigenschaft --stripes definieren, ohne sie auf eine visuelle Ausgabe anzuwenden, ist das Ergebnis funktionsähnlich. Wir können dafür sorgen, dass sich das mehr wie ein Mixin verhält, indem wir die Ausgabe auch universell definieren:

 * { --stripes: linear-gradient( var(--stripes-angle), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); background-image: var(--stripes); }

Solange --stripes-angle ungültig oder undefiniert bleibt, kann das Mixin nicht kompiliert werden und es wird kein background-image angewendet. Wenn wir für ein beliebiges Element einen gültigen Winkel festlegen, berechnet die Funktion und gibt uns einen Hintergrund:

 div { --stripes-angle: 30deg; /* generates the background */ }

Leider wird dieser Parameterwert erben , sodass die aktuelle Definition einen Hintergrund für das div und alle Nachkommen erstellt. Um das zu beheben, müssen wir sicherstellen, dass der Wert --stripes-angle nicht erbt, indem wir ihn für jedes Element auf den initial (oder einen ungültigen Wert) setzen. Wir können das mit demselben Universalselektor tun:

Siehe den Stift [Custom Props: Mixin](https://codepen.io/smashingmag/pen/ZdXMJx/) von Miriam Suzanne.

Siehe Pen Custom Props: Mixin von Miriam Suzanne.
 * { --stripes-angle: initial; --stripes: /* etc… */; background-image: var(--stripes); }

Sichere Inline-Stile

In einigen Fällen müssen wir den Parameter dynamisch von außerhalb von CSS festlegen – basierend auf Daten von einem Back-End-Server oder Front-End-Framework. Mit benutzerdefinierten Eigenschaften können wir Variablen sicher in unserem HTML definieren, ohne uns um die üblichen Spezifitätsprobleme kümmern zu müssen:

Siehe den Stift [Custom Props: Mixin + Inline Style](https://codepen.io/smashingmag/pen/qzPMPv/) von Miriam Suzanne.

Siehe Pen Custom Props: Mixin + Inline Style von Miriam Suzanne.
 <div>...</div>

Inline-Stile haben eine hohe Spezifität und sind sehr schwer zu überschreiben – aber mit benutzerdefinierten Eigenschaften haben wir eine andere Option: ignorieren. Wenn wir das div auf background-image: none (zum Beispiel) setzen, hat diese Inline-Variable keine Auswirkung. Um noch weiter zu gehen, können wir eine Zwischenvariable erstellen:

 * { --stripes-angle: var(--stripes-angle-dynamic, initial); }

Jetzt haben wir die Möglichkeit, --stripes-angle-dynamic im HTML zu definieren oder es zu ignorieren und --stripes-angle direkt in unserem Stylesheet zu setzen.

Siehe den Stift [Custom Props: Mixin + Inline / Override](https://codepen.io/smashingmag/pen/ZdXMao/) von Miriam Suzanne.

Siehe Pen Custom Props: Mixin + Inline / Override von Miriam Suzanne.

Voreingestellte Werte

Für komplexere Werte oder gängige Muster, die wir wiederverwenden möchten, können wir auch einige voreingestellte Variablen zur Auswahl bereitstellen:

 * { --tilt-down: 6deg; --tilt-up: -6deg; }

Und verwenden Sie diese Voreinstellungen, anstatt den Wert direkt festzulegen:

 <div>...</div> 

Siehe Pen [Custom Props: Mixin + Presets](https://codepen.io/smashingmag/pen/LKemZm/) von Miriam Suzanne.

Siehe Pen Custom Props: Mixin + Presets von Miriam Suzanne.

Dies ist großartig, um Diagramme und Grafiken basierend auf dynamischen Daten zu erstellen oder sogar einen Tagesplaner zu erstellen.

Siehe Pen [Balkendiagramm in CSS-Raster + Variablen](https://codepen.io/smashingmag/pen/wLrEyg/) von Miriam Suzanne.

Siehe das Pen Bar-Diagramm in CSS-Raster + Variablen von Miriam Suzanne.

Kontextuelle Komponenten

Wir können unser „Mixin“ auch als „Komponente“ umgestalten, indem wir es auf einen expliziten Selektor anwenden und die Parameter optional machen. Anstatt sich auf das Vorhandensein oder Fehlen von --stripes-angle zu verlassen, um unsere Ausgabe umzuschalten, können wir uns auf das Vorhandensein oder Fehlen eines Komponentenselektors verlassen. Dadurch können wir Fallback-Werte sicher festlegen:

Siehe Pen [Custom Props: Component](https://codepen.io/smashingmag/pen/QXqVmM/) von Miriam Suzanne.

Siehe Pen Custom Props: Component von Miriam Suzanne.
 [data-stripes] { --stripes: linear-gradient( var(--stripes-angle, to right), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); background-image: var(--stripes); }

Indem wir den Fallback in die var() -Funktion einfügen, können wir --stripes-angle undefiniert und „frei“ lassen, um einen Wert von außerhalb der Komponente zu erben. Dies ist eine großartige Möglichkeit, bestimmte Aspekte eines Komponentenstils für kontextbezogene Eingaben verfügbar zu machen. Sogar „bereichsbezogene“ Stile, die von einem JS-Framework generiert werden (oder innerhalb des Schatten-DOM, wie SVG-Symbole), können diesen Ansatz verwenden, um bestimmte Parameter für äußere Einflüsse verfügbar zu machen.

Isolierte Komponenten

Wenn wir den Parameter nicht für die Vererbung verfügbar machen möchten, können wir die Variable mit einem Standardwert definieren:

 [data-stripes] { --stripes-angle: to right; --stripes: linear-gradient( var(--stripes-angle, to right), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); background-image: var(--stripes); }

Diese Komponenten würden auch mit einer Klasse oder einem anderen gültigen Selektor funktionieren, aber ich habe das data- -Attribut gewählt, um einen Namensraum für alle gewünschten Modifikatoren zu erstellen:

 [data-stripes='vertical'] { --stripes-angle: to bottom; } [data-stripes='horizontal'] { --stripes-angle: to right; } [data-stripes='corners'] { --stripes-angle: to bottom right; } 

Siehe Pen [Custom Props: Isolated Components](https://codepen.io/smashingmag/pen/agLaGX/) von Miriam Suzanne.

Siehe Pen Custom Props: Isolated Components von Miriam Suzanne.

Selektoren und Parameter

Ich wünsche mir oft, ich könnte Datenattribute verwenden, um eine Variable zu setzen – eine Funktion, die von der CSS3 attr() -Spezifikation unterstützt wird, aber noch in keinem Browser implementiert ist (siehe die Registerkarte Ressourcen für verknüpfte Probleme in jedem Browser). Das würde es uns ermöglichen, einen Selektor enger mit einem bestimmten Parameter zu verknüpfen:

 <div data-stripes="30deg">...</div> /* Part of the CSS3 spec, but not yet supported */ /* attr( , ) */ [data-stripes] { --stripes-angle: attr(data-stripes angle, to right); } <div data-stripes="30deg">...</div> /* Part of the CSS3 spec, but not yet supported */ /* attr( , ) */ [data-stripes] { --stripes-angle: attr(data-stripes angle, to right); } <div data-stripes="30deg">...</div> /* Part of the CSS3 spec, but not yet supported */ /* attr( , ) */ [data-stripes] { --stripes-angle: attr(data-stripes angle, to right); }

In der Zwischenzeit können wir etwas Ähnliches erreichen, indem wir das Attribut style verwenden:

Siehe Pen [Custom Props: Style Selectors](https://codepen.io/smashingmag/pen/PrJdBG/) von Miriam Suzanne.

Siehe Pen Custom Props: Style Selectors von Miriam Suzanne.
 <div>...</div> /* The `*=` atttribute selector will match a string anywhere in the attribute */ [style*='--stripes-angle'] { /* Only define the function where we want to call it */ --stripes: linear-gradient(…); }

Dieser Ansatz ist am nützlichsten, wenn wir zusätzlich zu dem festgelegten Parameter andere Eigenschaften einbeziehen möchten. Wenn Sie beispielsweise einen Rasterbereich festlegen, können Sie auch Polsterung und Hintergrund hinzufügen:

 [style*='--grid-area'] { background-color: white; grid-area: var(--grid-area, auto / 1 / auto / -1); padding: 1em; }

Fazit

Wenn wir beginnen, all diese Teile zusammenzufügen, wird deutlich, dass benutzerdefinierte Eigenschaften weit über die üblichen Anwendungsfälle von Variablen hinausgehen, mit denen wir vertraut sind. Wir sind nicht nur in der Lage, Werte zu speichern und sie der Kaskade zuzuordnen – wir können sie auch verwenden, um die Kaskade auf neue Weise zu manipulieren und intelligentere Komponenten direkt in CSS zu erstellen.

Dies erfordert, dass wir viele der Tools überdenken, auf die wir uns in der Vergangenheit verlassen haben – von Namenskonventionen wie SMACSS und BEM bis hin zu „bereichsbezogenen“ Stilen und CSS-in-JS. Viele dieser Tools helfen dabei, Spezifitäten zu umgehen oder dynamische Stile in einer anderen Sprache zu verwalten – Anwendungsfälle, die wir jetzt direkt mit benutzerdefinierten Eigenschaften ansprechen können. Dynamische Stile, die wir oft in JS berechnet haben, können jetzt durch die Übergabe von Rohdaten an das CSS verarbeitet werden.

Zunächst mögen diese Änderungen als „zusätzliche Komplexität“ angesehen werden – da wir nicht daran gewöhnt sind, Logik in CSS zu sehen. Und wie bei jedem Code kann Over-Engineering eine echte Gefahr darstellen. Aber ich würde argumentieren, dass wir diese Macht in vielen Fällen nutzen können, um die Komplexität nicht zu erhöhen, sondern um die Komplexität aus den Tools und Konventionen von Drittanbietern zurück in die Kernsprache des Webdesigns und (was noch wichtiger ist) zurück in die Browser. Wenn unsere Stile eine Berechnung erfordern, sollte diese Berechnung in unserem CSS enthalten sein.

Alle diese Ideen können viel weiter getrieben werden. Benutzerdefinierte Eigenschaften werden gerade erst breiter angenommen, und wir haben gerade erst begonnen, an der Oberfläche dessen zu kratzen, was möglich ist. Ich bin gespannt, wohin das führt und was die Leute sich noch einfallen lassen. Habe Spaß!

Weiterführende Lektüre

  • „Es ist an der Zeit, benutzerdefinierte CSS-Eigenschaften zu verwenden“, Serg Hospodarets
  • „Ein Strategieleitfaden für benutzerdefinierte CSS-Eigenschaften“, Michael Riethmuller