Custom Properties: Vererbung verstehen und mit @property registrieren
AI generated
{ }
@
CSS · Custom Properties · @property · Design Systems
Custom Properties richtig vererben lassen
Vererbungsverhalten und Registrierung mit @property im Detail

Eine ungeregistrierte Custom Property vererbt in CSS immer, unabhängig davon, ob ihr Name einen lokalen, komponentenspezifischen Wert suggeriert. Erst @property gibt einer Custom Property einen festen Typ, einen garantierten Initialwert und ein explizites Vererbungsverhalten, und schließt damit eine ganze Klasse von Bugs, die in großen Design-Systemen sonst schwer aufzuspüren sind.

15 Min. Lesezeit @property · inherits · initial-value Chrome · Edge · Firefox · Safari (weitgehend unterstützt)

1. Wie Custom Properties standardmäßig vererben, ohne Registrierung

Eine Custom Property, die einfach mit --farbe: blue; deklariert wird, verhält sich in der Cascade nicht wie eine normale CSS-Eigenschaft, sondern wie ein reiner Text-Token, der grundsätzlich vererbt, genau wie color es tut. Das gilt selbst dann, wenn der Name der Property einen komponentenlokalen Zweck suggeriert, etwa --card-spacing: Ohne explizite Registrierung sickert dieser Wert in jedes verschachtelte Kindelement, sofern dort nicht ein neuer Wert für dieselbe Property gesetzt wird.

Dieses Standardverhalten überrascht viele, die aus objektorientierten Sprachen kommen und erwarten, dass eine lokal auf einer Komponente definierte Variable automatisch auch lokal bleibt. In CSS gibt es diese Kapselung ohne @property nicht: Jede Custom Property ist von Natur aus global im Sinne der Cascade und vererbt an alle Nachfahren, bis sie irgendwo überschrieben oder mit unset zurückgesetzt wird.

2. @property-Grundlagen: syntax, inherits und initial-value

Die @property-Regel registriert eine Custom Property mit drei Pflichtangaben: syntax legt den erlaubten Werttyp fest, etwa '<color>' oder '<length>', inherits legt explizit fest, ob der Wert an Kindelemente weitergegeben wird, und initial-value definiert den Wert, den die Property annimmt, wenn sie an keiner Stelle in der Cascade gesetzt wurde. Alle drei Angaben sind Pflicht, fehlt eine, wird die gesamte @property-Regel als ungültig verworfen und die Property bleibt ungeregistriert.

Der Typ aus syntax ist mehr als Dokumentation: Der Browser validiert jeden zugewiesenen Wert gegen diesen Typ und verwirft ungültige Zuweisungen, statt sie wie bei einer ungeregistrierten Property klaglos als Text zu übernehmen. Eine registrierte --dot-gap mit syntax: '<length>' akzeptiert also kein auto oder einen Farbwert, was Tippfehler schon beim Parsen sichtbar macht statt erst bei einem unerklärlich kaputten Layout.


@property --brand-color {
  syntax: '<color>';
  inherits: true;
  initial-value: #4a1d96;
}

@property --card-spacing {
  syntax: '<length>';
  inherits: false;
  initial-value: 16px;
}

3. inherits: false im Detail: komponentenlokale Werte kapseln

Setzt man inherits: false, verhält sich die Custom Property in Bezug auf Vererbung wie eine gewöhnliche, nicht vererbende CSS-Eigenschaft, etwa margin: Ein Kindelement sieht ausschließlich den initial-value, nicht den Wert, der weiter oben in der Cascade auf einem Vorfahren gesetzt wurde, es sei denn, das Kind erbt ihn explizit über inherit als Wert oder die Property wird direkt auf dem Kind neu gesetzt.

Das ist genau das Verhalten, das man sich für echte Komponenten-interne Werte wünscht, etwa den internen Innenabstand einer Card-Komponente, der nicht versehentlich in eine tief verschachtelte Unterkomponente durchsickern soll, nur weil diese zufällig denselben Property-Namen für einen völlig anderen Zweck verwendet. inherits: false macht Custom Properties damit zu einem echten Werkzeug für Kapselung, das ohne @property in CSS schlicht nicht existiert.


.card {
  --card-spacing: 16px;
  padding: var(--card-spacing);
}

.card .nested-widget {
  /* --card-spacing here is 16px again (the initial-value),
     NOT the 16px set on .card -- because inherits: false */
  padding: var(--card-spacing, 8px);
}

4. Typischer Bug: unerwartetes Durchsickern ungeregistrierter Properties

Der häufigste Bug in großen CSS-Codebasen entsteht, wenn ein Team eine Custom Property in einer tief verschachtelten Komponente für einen lokalen Zweck einführt, etwa --icon-size in einem Button, ohne zu registrieren. Weil die Property ohne @property immer vererbt, kann ein ganz anderer, weiter oben in der Baumstruktur liegender Elternteil, der zufällig denselben Namen für etwas völlig anderes nutzt, plötzlich einen unerwarteten Wert an diesen Button weitergeben.

Solche Bugs sind besonders schwer zu debuggen, weil in den DevTools der "computed"-Wert korrekt anzeigt, woher die Vererbung kommt, aber der Entwickler oft gar nicht auf die Idee kommt, dort nachzuschauen, weil er die Property für rein lokal hält. Die zuverlässigste Prävention ist, jede Custom Property, die nicht bewusst global vererben soll, von Anfang an mit @property und inherits: false zu registrieren, statt sich auf Namenskonventionen wie ein vorangestelltes --_ zu verlassen.

5. invalid-at-computed-value-time: der Fallback-Unterschied bei ungültigen Werten

Ohne Registrierung gilt für Custom Properties das Konzept des "guaranteed-invalid value": Wird einer ungeregistrierten Property ein syntaktisch ungültiger Wert zugewiesen, verhält sie sich beim Verbrauch über var() so, als wäre sie gar nicht gesetzt, und der Fallback-Wert von var(--x, fallback) greift. Bei einer registrierten Property mit @property ist das Verhalten anders und für viele überraschend: Ein ungültiger Wert lässt die Property nicht auf den var()-Fallback zurückfallen, sondern auf den in @property definierten initial-value.

Dieser Unterschied wird zur Falle, wenn ein Team gewohnheitsmäßig einen var()-Fallback als Sicherheitsnetz einbaut, aber nicht merkt, dass dieser Fallback bei einer registrierten Property im Fehlerfall gar nicht mehr greift. Wer @property nutzt, muss den initial-value deshalb bewusst als den echten, einzigen Fallback-Mechanismus behandeln und den var()-Fallback nur noch als zusätzliche Doku-Absicherung verstehen, nicht als funktionale Garantie.


@property --gap {
  syntax: '<length>';
  inherits: false;
  initial-value: 8px;
}

.box {
  --gap: not-a-length; /* invalid for the registered <length> syntax */
  /* Falls back to the initial-value (8px), NOT to the var() fallback below */
  gap: var(--gap, 24px);
}

6. Registrierungsstrategie für ein konsistentes Design-System

In einem wachsenden Design-System lohnt es sich, alle Custom Properties, die als Design-Tokens dienen, in einer einzigen zentralen Stylesheet-Datei mit @property-Regeln zu registrieren, statt die Registrierung über verschiedene Komponenten-Dateien zu verstreuen. Diese zentrale Registrierung macht auf einen Blick sichtbar, welche Tokens im System existieren, welchen Typ sie haben und ob sie global vererben sollen oder komponentenlokal bleiben.

Als Faustregel für die Registrierungsstrategie gilt: Farb- und Typografie-Tokens, die bewusst durch den gesamten Baum durchgereicht werden sollen, bekommen inherits: true, während komponenteninterne Layout-Werte wie interne Abstände oder Icon-Größen konsequent mit inherits: false registriert werden, damit sie niemals versehentlich in fremde Komponenten hineinwirken.

7. CSS.registerProperty(): dieselbe Registrierung zur Laufzeit per JavaScript

Neben der deklarativen @property-Regel im Stylesheet gibt es mit CSS.registerProperty() eine gleichwertige JavaScript-API, die dieselben drei Angaben (Name, syntax, inherits, initialValue) zur Laufzeit setzt. Das ist nützlich, wenn ein Design-System seine Tokens dynamisch aus einer Konfigurationsdatei oder einem Theme-Objekt generiert und die Registrierung deshalb nicht als statisches CSS vorliegen kann, sondern erst zur Laufzeit feststeht.

Ein wichtiger Unterschied zur @property-Regel ist, dass CSS.registerProperty() bei einem Aufruf mit einem bereits registrierten Namen eine Exception wirft, während zwei widersprüchliche @property-Regeln im Stylesheet einfach nach den normalen Cascade-Regeln aufgelöst werden. Wer Tokens sowohl per JavaScript als auch per Stylesheet registriert, sollte deshalb klar festlegen, welcher der beiden Wege für welche Property die einzige Quelle der Wahrheit ist, um Laufzeitfehler durch doppelte Registrierung zu vermeiden.

8. Performance-Effekt: registrierte Custom Properties und Animation

Registrierte Custom Properties bringen neben der Typsicherheit einen praktischen Zusatznutzen: Weil der Browser dank syntax weiß, dass etwa --dot-gap eine Länge ist, kann er zwischen zwei Werten diskret animieren, ein Effekt, der bei ungeregistrierten Custom Properties nicht funktioniert, weil der Browser dort nur Text sieht und Text sich nicht interpolieren lässt. Eine transition auf einer ungeregistrierten Custom Property springt deshalb abrupt vom alten zum neuen Wert, statt sanft überzublenden.

Zusätzlich kann die Style-Engine bei registrierten Properties frühzeitig entscheiden, ob eine Änderung überhaupt eine Neuberechnung nachgelagerter Werte auslösen muss, weil Typ und Vererbungsverhalten schon zur Registrierungszeit feststehen, statt bei jeder Verwendung neu aus dem Kontext geraten zu werden. In sehr großen Stylesheets mit vielen Custom-Property-Referenzen kann sich das in der Style-Recalculation-Zeit messbar bemerkbar machen.

9. Browser-Support und Migration bestehender Custom Properties

@property wird mittlerweile von Chrome, Edge, Firefox und Safari unterstützt, sodass eine schrittweise Migration bestehender, ungeregistrierter Custom Properties in produktiven Projekten heute risikoarm möglich ist. Wichtig ist, dass eine @property-Regel additiv wirkt: Bestehende var()-Aufrufe funktionieren unverändert weiter, nur das beschriebene Fallback-Verhalten bei ungültigen Werten ändert sich, sobald die Registrierung greift.

Für die Migration empfiehlt sich ein schrittweises Vorgehen: zuerst die Custom Properties registrieren, die am häufigsten animiert werden oder in tief verschachtelten Komponenten für Verwirrung gesorgt haben, und erst danach die verbleibenden, unkritischen Tokens nachziehen. So lässt sich der Effekt jeder einzelnen Registrierung isoliert beobachten, statt alle Properties auf einmal umzustellen und bei einem Regressions-Bug nicht mehr zu wissen, welche Änderung ihn ausgelöst hat.

Verhalten Ungeregistrierte Property @property mit inherits: true @property mit inherits: false
Vererbung an Kinder Immer, wie ein Text-Token Ja, wie eine normale vererbende Eigenschaft Nein, wie margin oder padding
Ungültiger Wert Fällt auf var()-Fallback zurück Fällt auf initial-value zurück Fällt auf initial-value zurück
Animierbar Nein, nur Text-Interpolation Ja, typisierte Interpolation Ja, typisierte Interpolation
Typprüfung beim Setzen Keine Ja, gegen syntax geprüft Ja, gegen syntax geprüft

Mironsoft

Modernes CSS, Layout-Architektur und Rendering-Performance

CSS, das wartbar bleibt statt mit jeder Änderung zu brechen?

Wir prüfen bestehende Stylesheets auf Spezifitäts-Chaos und Layout-Thrashing und bauen daraus eine CSS-Architektur mit Cascade Layers, Custom Properties und modernen Layout-Primitiven, die auch nach dem zehnten Feature noch verständlich ist.

CSS-Audit

Spezifität, Cascade-Konflikte und ungenutzte Selektoren systematisch aufdecken.

Architektur-Refactoring

Cascade Layers, Custom Properties und Design Tokens sauber einführen.

Performance-Tuning

Layout-Thrashing, teure Selektoren und Rendering-Engpässe gezielt beheben.

10. Zusammenfassung

Custom Properties und @property: Das Wichtigste auf einen Blick

Standardverhalten

Ungeregistrierte Custom Properties vererben immer, unabhängig vom Namen oder vom suggerierten Geltungsbereich.

Kapselung

@property mit inherits: false macht eine Custom Property zu einem echten, nicht vererbenden Wert wie margin.

Fallback-Falle

Bei registrierten Properties greift bei ungültigen Werten der initial-value, nicht der var()-Fallback.

Strategie

Design-Tokens zentral mit @property registrieren, globale Tokens mit inherits: true, komponentenlokale mit inherits: false.

11. FAQ: Custom Properties und @property: Das Wichtigste auf einen Blick

1Vererben Custom Properties standardmäßig?
Ja. Eine ungeregistrierte Custom Property vererbt immer an Kindelemente, unabhängig vom Namen, genau wie eine vererbende Standard-Eigenschaft wie color.
2Was bewirkt @property genau?
@property registriert eine Custom Property mit festem Typ (syntax), explizitem Vererbungsverhalten (inherits) und einem garantierten Initialwert (initial-value), statt sie als reinen Text-Token zu behandeln.
3Wie verhindere ich, dass eine Custom Property durchsickert?
Mit @property und inherits: false registrieren. Damit verhält sich die Property wie eine normale, nicht vererbende Eigenschaft und Kinder sehen nur den initial-value.
4Was passiert bei einem ungültigen Wert für eine registrierte Property?
Sie fällt auf den in @property definierten initial-value zurück, nicht auf den var()-Fallback. Das unterscheidet sich vom Verhalten ungeregistrierter Properties.
5Warum wird meine Custom Property nicht animiert?
Ungeregistrierte Custom Properties können nur als Text interpoliert werden, was bei einer transition zu einem abrupten Sprung führt. Eine Registrierung mit passendem syntax-Typ ermöglicht echte, diskrete Interpolation.
6Sind alle drei Angaben in @property Pflicht?
Ja, syntax, inherits und initial-value müssen alle gesetzt sein. Fehlt eine, wird die gesamte @property-Regel als ungültig verworfen und die Property bleibt ungeregistriert.
7Wie registriere ich Design-Tokens für ein ganzes Team?
Am besten zentral in einer einzigen Stylesheet-Datei mit @property-Regeln, statt über einzelne Komponenten verstreut. So ist auf einen Blick sichtbar, welche Tokens existieren und wie sie sich verhalten.
8Können Browser bei @property den Wert vor dem Setzen prüfen?
Ja. Der Browser validiert jeden zugewiesenen Wert gegen die in syntax definierte Grammatik und verwirft ungültige Zuweisungen, statt sie klaglos als Text zu übernehmen.
9Ist die Migration bestehender Custom Properties riskant?
Nicht grundsätzlich, weil @property additiv wirkt und bestehende var()-Aufrufe weiterlaufen. Nur das Fallback-Verhalten bei ungültigen Werten ändert sich, deshalb empfiehlt sich eine schrittweise Migration.
10Unterstützen alle Browser @property?
Chrome, Edge, Firefox und Safari unterstützen @property mittlerweile, sodass eine produktive Nutzung heute risikoarm möglich ist.