Das Überschreiben einer CSS-Variablen auf einem Kindelement erstellt keine zusammengesetzte benutzerdefinierte Eigenschaft neu, die vom Elternelement geerbt wurde. Das Elternelement hat den verschachtelten Verweis bereits aufgelöst. Damit der Wert des Kindelements verwendet wird, deklarieren Sie die zusammengesetzte Eigenschaft auf diesem Kindelement oder setzen Sie dort die endgültige Eigenschaft zusammen.

Diese Anleitung erklärt dieses Verhalten und bietet eine reproduzierbare Übung für den Desktop in Chrome auf einem Mac. Ziel ist es, den Rahmen einer Komponente ihrer lokalen Farbüberschreibung folgen zu lassen und dabei gemeinsame Standardwerte beizubehalten.

Warum behält die geerbte zusammengesetzte Eigenschaft die Farbe des Elternelements?

Betrachten Sie diesen CSS-Ausschnitt. Er setzt ein .sample-tile-Element innerhalb eines .sample-board-Elements voraus:

.sample-board {
  --sample-color: teal;
  --sample-border: 6px solid var(--sample-color);
}

.sample-tile {
  --sample-color: darkorange;
  border: var(--sample-border);
}

Der Rahmen des Kindelements bleibt türkis. Die CSS-Variablen-Spezifikation legt fest, dass benutzerdefinierte Eigenschaften ihre verschachtelten var()-Funktionen zum Zeitpunkt der Berechnung des Werts und vor der Vererbung auflösen. Daher erbt das Kindelement eine zusammengesetzte Eigenschaft, die 6px solid teal entspricht; es erbt keinen Ausdruck, der --sample-color des Kindelements einsetzt. CSS Custom Properties for Cascading Variables Module Level 1.

Hier handelt es sich um zwei separate Eigenschaften. Wenn --sample-color auf dem Kindelement gesetzt wird, ändert sich der lokale Wert dieser Eigenschaft. Dadurch wird nicht zusätzlich ein neuer Wert für --sample-border deklariert. Die MDN-Referenz zu benutzerdefinierten Eigenschaften beschreibt gewöhnliche, nicht registrierte benutzerdefinierte Eigenschaften als vererbt, wobei ihre Variablen in den berechneten Werten ersetzt sind. Custom properties: CSS variables.

Die Abfolge in diesem Beispiel lautet:

  1. Das Elternelement berechnet --sample-color als teal.
  2. Seine --sample-border-Eigenschaft setzt diese Farbe ein und ergibt 6px solid teal.
  3. Das Kindelement erbt diese zusammengesetzte Eigenschaft und setzt separat sein eigenes Farbtoken auf darkorange.
  4. Die Deklaration border des Kindelements verwendet die geerbte türkise zusammengesetzte Eigenschaft.

Dies ist kein dauerhaft gespeicherter Schnappschuss des Elternelements. Ändert sich dessen Farbe, kann sich auch seine zusammengesetzte Eigenschaft ändern und erneut vererbt werden. Entscheidend ist das Element, auf dem der verschachtelte Verweis aufgelöst wird: Eine Überschreibung auf einem Nachfahren wirkt sich nicht rückwirkend auf diese Berechnung aus. CSS Variables: resolving dependencies.

Verhalten in Chrome auf einem Mac nachvollziehen

Verwenden Sie eine leere Seite, damit vorhandene Seitenstile nicht dazwischenfunken. Öffnen Sie einen neuen Chrome-Tab, geben Sie about:blank in die Adressleiste ein und drücken Sie anschließend Command + Option + J, um die Console zu öffnen. Google dokumentiert diese Tastenkombination für Mac in seiner DevTools-Anleitung. Open Chrome DevTools.

Das folgende vollständige Programm für die Browserkonsole erstellt drei beschriftete abstrakte Formen und ihr Stylesheet. Es benötigt weder Bibliotheken noch Bilder, Netzwerkanfragen oder vorhandene Seitenelemente. Führen Sie den gesamten Block einmal auf der leeren Seite aus. Die erwarteten Ergebnisse unten leiten sich aus den dokumentierten CSS-Regeln ab; sie werden nicht als aufgezeichnete Browsermessungen dargestellt.

(() => {
  const board = document.createElement("section");
  board.className = "sample-board";

  const style = document.createElement("style");
  style.textContent = `
    .sample-board {
      --sample-color: teal;
      --sample-border: 6px solid var(--sample-color);
      display: flex;
      flex-wrap: wrap;
      gap: 24px;
      padding: 32px;
      font: 16px system-ui;
    }

    .sample-board .sample-tile {
      --sample-color: darkorange;
      display: grid;
      place-items: center;
      width: 120px;
      height: 80px;
      border: var(--sample-border);
      border-radius: 24px;
      background: white;
      color: black;
    }

    .sample-board .sample-local {
      --sample-border: 6px solid var(--sample-color);
    }

    .sample-board .sample-direct {
      border: 6px solid var(--sample-color);
    }
  `;

  const samples = [
    ["Inherited", "sample-inherited"],
    ["Local", "sample-local"],
    ["Direct", "sample-direct"]
  ];

  const tiles = samples.map(([label, className]) => {
    const tile = document.createElement("div");
    tile.className = `sample-tile ${className}`;
    tile.textContent = label;
    board.append(tile);
    return tile;
  });

  document.head.append(style);
  document.body.append(board);

  function report(stage) {
    console.log(stage);
    console.table(tiles.map((tile) => {
      const computed = getComputedStyle(tile);
      return {
        sample: tile.textContent,
        localColor: computed
          .getPropertyValue("--sample-color").trim(),
        composite: computed
          .getPropertyValue("--sample-border").trim(),
        paintedBorder: computed.borderTopColor
      };
    }));
  }

  report("Initial values");
  board.style.setProperty("--sample-color", "rebeccapurple");
  report("After changing the parent color");
})();

Das Programm gibt zwei Tabellen aus. Die erwarteten Rahmenfarben sind:

Beispiel Anfangsfarbe Nach der Änderung am Elternelement Grund
Inherited Türkis Lila Verwendet die berechnete zusammengesetzte Eigenschaft des Elternelements
Local Orange Orange Deklariert die zusammengesetzte Eigenschaft auf dem Kindelement
Direct Orange Orange Setzt die lokale Farbe direkt in border ein

Alle drei Kindelemente behalten darkorange als lokales Farbtoken. Das aufschlussreiche Beispiel ist Direct: Seine geerbte zusammengesetzte Eigenschaft enthält weiterhin die Farbe des Elternelements, aber sein gezeichneter Rahmen verwendet das lokale Token, weil die Rahmendeklaration die zusammengesetzte Eigenschaft nicht nutzt.

getComputedStyle() liest die aufgelösten Eigenschaftswerte, nachdem die aktiven Stylesheets angewendet wurden. So lässt sich erkennen, welchen Wert ein Element tatsächlich erhält, statt nur den verfassten Ausdruck zu sehen. Die Serialisierung einer Farbe kann als rgb()-Wert statt als ursprünglicher Farbname erscheinen. Window: getComputedStyle() method.

Laden Sie den leeren Tab neu, um die Übung zurückzusetzen. Bei einer bestehenden Seite gehört die gewählte CSS-Korrektur in ihr Stylesheet; Änderungen über die Konsole eignen sich zur Diagnose, bearbeiten aber nicht Ihre Quelldateien.

Welche Korrektur sollten Sie wählen?

Die zusammengesetzte Eigenschaft an der Komponentengrenze deklarieren

Das Beispiel Local behält das wiederverwendbare Rahmen-Token bei und deklariert dessen Zusammensetzung auf demselben Element, das die Überschreibung bereitstellt. Hier ist der relevante CSS-Ausschnitt aus dem Programm:

.sample-board .sample-local {
  --sample-color: darkorange;
  --sample-border: 6px solid var(--sample-color);
  border: var(--sample-border);
}

Wählen Sie dieses Muster, wenn mehrere Deklarationen denselben zusammengesetzten Wert benötigen. Legen Sie einfache Standardwerte auf einem Vorfahren ab und deklarieren Sie die zusammengesetzte Eigenschaft auf der Komponente, die für die Anpassung zuständig ist. Benutzerdefinierte Eigenschaften können auf bestimmten Elementen deklariert werden und sich über deren Nachfahren vererben; sie müssen nicht auf :root stehen. Using CSS custom properties.

Die Platzierung bleibt auch weiter unten im Elementbaum wichtig. Überschreibt ein Enkelkind das einfache Token erneut, spiegelt eine von dieser Komponente geerbte zusammengesetzte Eigenschaft weiterhin die Berechnung dieser Komponente wider. Deklarieren Sie die Zusammensetzung an jeder Nachfahrengrenze erneut, an der ein anderes Ergebnis entstehen soll.

Die endgültige Eigenschaft dort zusammensetzen, wo sie verwendet wird

Das Beispiel Direct entfernt das Zwischentoken aus der Rahmendeklaration. Dieser CSS-Ausschnitt enthält die entscheidende Regel:

.sample-board .sample-direct {
  --sample-color: darkorange;
  border: 6px solid var(--sample-color);
}

Wählen Sie diese Lösung, wenn nur eine Deklaration die Kombination benötigt. Sie können weiterhin separate Tokens für Breite, Stil und Farbe gemeinsam nutzen und sie dann in der border-Deklaration der Komponente zusammensetzen. MDN dokumentiert, dass var() den gesamten oder einen Teil des Werts einer anderen Eigenschaft ersetzen kann. Using CSS custom properties.

Die gesamte zusammengesetzte Eigenschaft überschreiben, wenn sie die öffentliche Schnittstelle bildet

Manchmal soll die Komponente vollständig über ihren Rahmen angepasst werden. In diesem Fall können Sie --sample-border lokal ersetzen, etwa durch 6px solid darkorange, statt nur den bisherigen Eingabewert zu ändern.

Diese Entscheidungshilfe unterstützt Sie bei der Wahl der Komponentenschnittstelle:

Beabsichtigte Anpassung Passendes Muster
Eine lokale Farbe steuert einen Rahmen Direkt in border zusammensetzen
Mehrere Deklarationen nutzen denselben zusammengesetzten Wert Die zusammengesetzte Eigenschaft auf der Komponente deklarieren
Ein Aufrufer gibt den gesamten Rahmen vor Die zusammengesetzte Eigenschaft selbst überschreiben
Nachfahren sollen exakt den zusammengesetzten Wert des Elternelements verwenden Die geerbte zusammengesetzte Eigenschaft beibehalten

Warum setzen Spezifität, Fallbacks oder Vererbungs-Schlüsselwörter die Eigenschaft nicht neu zusammen?

Prüfen Sie zuerst, ob die Überschreibung des einfachen Tokens tatsächlich wirksam wurde. Ist --sample-color des Kindelements bereits zu darkorange aufgelöst, ändert eine spezifischere Deklaration nichts an der separaten geerbten Eigenschaft --sample-border. Beide Eigenschaften haben bereits Werte; das Problem liegt darin, wo die zusammengesetzte Eigenschaft gebildet wurde.

Ebenso übernimmt --sample-border: inherit den berechneten Wert des Elternelements. Bei der gewöhnlichen Vererbung werden berechnete Werte übertragen; die verfasste Deklaration des Elternelements wird nicht erneut im Kontext des Kindelements ausgeführt. Inheritance.

Auch ein Fallback wie var(--sample-border, 6px solid darkorange) lässt einen gültigen geerbten türkisfarbenen Rahmen unverändert. Ein var()-Fallback greift bei einer benutzerdefinierten Eigenschaft ohne verwendbaren Wert; er vergleicht diesen Wert nicht mit den bevorzugten Eingaben eines Nachfahren. var() CSS function.

Trennen Sie ungültige Werte von diesem Vererbungsproblem. Ein nicht registriertes Token mit dem Wert 12px kann beispielsweise als Wert einer benutzerdefinierten Eigenschaft existieren und beim Einsetzen in border-color dennoch ein ungültiges Ergebnis erzeugen. Der Fallback innerhalb dieses Aufrufs von var() ist keine allgemeine Alternative zur Typprüfung. MDN erklärt, warum das Ersetzen einer benutzerdefinierten Eigenschaft Deklarationen ergeben kann, die zum Zeitpunkt der Berechnung des Werts ungültig werden. Using CSS custom properties.

Behebt die Registrierung der Variablen mit @property das Problem?

Mit @property können Sie die Syntax, den Anfangswert und das Vererbungsverhalten einer benutzerdefinierten Eigenschaft festlegen. Dadurch wird eine vom Elternelement geerbte zusammengesetzte Eigenschaft jedoch nicht anhand überschriebener Eingaben des Kindelements neu berechnet. Würden Sie das einfache Token mit inherits: false registrieren, änderte sich, wie dieses Token bezogen wird. Eine Registrierung der zusammengesetzten Eigenschaft mit dieser Einstellung würde ihre normale Vererbung unterbinden. In keinem der beiden Fälle wird die fehlende lokale Zusammensetzungsregel bereitgestellt. @property CSS at-rule.

Die Registrierung hat außerdem eine andere Kompatibilitätsgrenze. Laut MDN sind gewöhnliche benutzerdefinierte Eigenschaften seit April 2017 breit verfügbar, während @property im Juli 2024 die Baseline-Verfügbarkeit erreichte und in älteren Browsern möglicherweise nicht verfügbar ist. Für die beiden gezeigten Korrekturen ist keine Registrierung erforderlich. Custom properties: CSS variables, @property CSS at-rule.

Das gleiche Problem im Stylesheet diagnostizieren

Vergleichen Sie auf dem Element mit der fehlerhaften Darstellung die drei Spalten aus der Übung:

  1. Lesen Sie das einfache Token ab, etwa --sample-color.
  2. Lesen Sie das zusammengesetzte Token ab, etwa --sample-border.
  3. Lesen Sie die gerenderte Eigenschaft ab, etwa border-top-color.
  4. Suchen Sie das Element, auf dem die Deklaration der zusammengesetzten Eigenschaft greift.
  5. Verschieben oder wiederholen Sie diese Deklaration an der gewünschten Anpassungsgrenze oder setzen Sie die endgültige Eigenschaft direkt zusammen.

Um die relevanten Regeln in Chrome zu finden, klicken Sie mit der rechten Maustaste auf die Form und wählen Sie Inspect. DevTools wählt das Element im Bereich Elements aus; im Tab Styles werden die darauf angewendeten CSS-Regeln angezeigt. Open Chrome DevTools.

Zeigt das einfache Token den gewünschten lokalen Wert an, während das zusammengesetzte Token weiterhin den Wert eines Vorfahren enthält, überprüfen Sie zuerst, wo die zusammengesetzte Eigenschaft deklariert wird. Definieren Sie die Zusammensetzung dort, wo ihre Eingaben berücksichtigt werden sollen, und überprüfen Sie anschließend sowohl das aufgelöste Token als auch das sichtbare Ergebnis. So erhält die Komponente eine klare und vorhersehbare Anpassungsgrenze.

Quellen