Abschnitt 15 · Lektion 3
Getypte Custom Properties mit @property
--luft: 8px legt eine Custom Property an, und var(--luft) setzt sie ein. Für den Browser ist der Wert bis zum Einsetzen aber nur eine Zeichenkette. Er weiß nicht, dass 8px eine Länge ist, und pling wäre ihm genauso recht.
Meistens stört das nicht. Es stört genau dann, wenn der Browser rechnen soll, und rechnen soll er beim Animieren. Zwischen zwei Texten gibt es keine Mitte. Eine animierte Custom Property springt deshalb, statt zu fließen.
Was die Registrierung tut
<div class="ring">Mit @property</div> @property --winkel {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
}
.ring {
display: grid;
place-items: center;
width: 200px;
height: 200px;
border-radius: 50%;
color: #52525b;
background: conic-gradient(
from var(--winkel),
#2563eb,
#e4e4e7 40%,
#2563eb
);
animation: drehen 6s linear infinite;
}
@keyframes drehen {
to {
--winkel: 360deg;
}
} Mit @property --winkel erfährt der Browser in drei Angaben, was für ein Ding diese Custom Property ist.
syntax nennt den Typ. Die gängigen sind "<color>", "<length>", "<angle>", "<number>", "<percentage>" und "<length-percentage>". Ein "*" heißt “irgendwas”, also genau der alte Zustand ohne Typ.
inherits legt fest, ob sich der Wert nach unten weitergibt. Bei Werten, die zu einer Komponente gehören, ist false fast immer richtig. Dann bleibt der Wert dort, wo er gesetzt wurde, und läuft nicht durch den halben Baum.
initial-value ist der Standardwert und bei allem außer "*" Pflicht. Er muss für sich allein ausrechenbar sein, also keine Angabe, die erst später ihren Bezug bekommt. 0px geht, 1rem nicht, denn rem hängt an der Wurzelschriftgröße.
<div class="ring">Ohne @property</div> /* Hier fehlt die Registrierung. Die Custom Property ist
damit nur Text, und Text lässt sich nicht
interpolieren: Der Browser schaltet auf halber
Strecke einmal um, statt zu rechnen. */
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
}
.ring {
--winkel: 0deg;
display: grid;
place-items: center;
width: 200px;
height: 200px;
border-radius: 50%;
color: #52525b;
background: conic-gradient(
from var(--winkel),
#2563eb,
#e4e4e7 40%,
#2563eb
);
animation: drehen 6s linear infinite;
}
@keyframes drehen {
to {
--winkel: 360deg;
}
} In beiden Beispielen stecken derselbe Verlauf und dieselbe Animation, einmal registriert und einmal nicht. Im zweiten steht der Verlauf still. Zwischen 0deg und 360deg sieht der Browser nur zwei Texte und schaltet deshalb auf halber Strecke einmal vom einen auf den anderen um, statt dazwischen zu rechnen. Beide Winkel sehen gleich aus, also bemerkst du den Sprung gar nicht.
Der Typ ändert auch, was zurückkommt
Eine registrierte Custom Property hat auf jedem Element einen berechneten Wert, auch dort, wo sie nie gesetzt wurde, nämlich den initial-value. Eine nicht registrierte hat dort gar keinen.
Außerdem formt der Browser den Wert beim Berechnen um. #2563eb in einer <color> liest sich hinterher als rgb(37, 99, 235), 1.5rem in einer <length> als Pixelwert. Ohne Registrierung bleibt beides Zeichen für Zeichen so stehen, wie du es hingeschrieben hast.
Daran siehst du am schnellsten, ob eine Registrierung wirklich greift, und zwar in der Eingabezeile der Console mit
getComputedStyle(document.documentElement).getPropertyValue('--winkel')
Im ersten Beispiel antwortet sie 0deg, im zweiten kommt gar nichts zurück. Dieselbe Custom Property am selben Ort, und der Unterschied ist die eine Registrierung. Custom Properties stehen nicht im Auskunftsstreifen, deshalb ist diese Frage der direkte Weg.
Was bei einem ungültigen Wert passiert
<div class="kachel a">registriert, gültiger Wert</div>
<div class="kachel b">registriert, Unsinn als Wert</div>
<div class="kachel c">nicht registriert, Unsinn als Wert</div> @property --flaeche {
syntax: "<color>";
inherits: false;
initial-value: #f4f4f5;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
.kachel {
margin-bottom: 8px;
padding: 16px;
border-radius: 8px;
background-color: var(--flaeche, #f4f4f5);
}
.a {
--flaeche: #dcfce7;
}
/* Ungültig, aber registriert: der Browser nimmt den
initial-value und die Kachel bleibt grau. */
.b {
--flaeche: pling;
}
/* Ungültig und nicht registriert: der Unsinn steht
als Text drin, var() liefert ihn aus, und
background-color fällt auf transparent zurück.
Der Fallback in var() greift dabei nicht. */
.c {
--unbekannt: pling;
background-color: var(--unbekannt, #f4f4f5);
} Schreibst du in eine registrierte Custom Property etwas Ungültiges, nimmt der Browser den initial-value. Das ist ein echtes Sicherheitsnetz, besonders wenn die Werte von außen kommen.
Bei den drei Kacheln im Beispiel kannst du das in der Eingabezeile Zeile für Zeile nachfragen. An der ersten steht --flaeche auf rgb(220, 252, 231), also das hingeschriebene #dcfce7, umgeformt. An der zweiten, in die pling geschrieben wurde, steht rgb(244, 244, 245). Das ist der initial-value, ebenfalls umgeformt. Und an der dritten, die nicht registriert ist, antwortet --unbekannt mit pling, Zeichen für Zeichen.
Im Streifen steht, was daraus wird, rgb(220, 252, 231), rgb(244, 244, 245) und rgba(0, 0, 0, 0). Beim dritten liefert var() den Unsinn brav aus, und background-color kann damit nichts anfangen.
Ohne Registrierung steht der Unsinn also als Text in der Custom Property, und erst die Eigenschaft dahinter merkt beim Einsetzen, dass sie damit nichts anfangen kann. Der Fallback in var() hilft dabei nicht. Dieser zweite Parameter greift nur, wenn die Custom Property gar nicht gesetzt ist.
Wo du es wirklich brauchst
Animierte Verläufe sind der offensichtliche Fall. Der zweite ist ein Token-System, in dem Farben nicht nur stehen, sondern auch weich wechseln sollen, etwa beim Umschalten zwischen hell und dunkel. Und eine Custom Property, deren Wert von außen kommt, bekommt mit initial-value einen Fallback, der immer da ist.
In der Aufgabe hat ein Hero einen Rahmen aus einem Kegelverlauf, der sich noch nicht dreht.
Zum Mitnehmen
Eine Custom Property ohne Typ ist für den Browser nur Text, und zwischen zwei Texten gibt es nichts zu rechnen. Erst @property mit syntax, inherits und initial-value macht daraus eine Zahl, und aus einem Sprung eine Bewegung.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.