Zum Inhalt springen
React & Tailwind

Styles auf eine Komponente begrenzen mit @scope

Warum Komponenten-Styles sich gegenseitig überschreiben, wie @scope einen Bereich mit Ober- und Untergrenze absteckt, was der Donut-Scope ist, wie Nähe in der Kaskade entscheidet und wann man es neben Tailwind wirklich braucht.

9 Min. LesezeitVon Leonie RoesmannStand: 12. November 2026
CSSKomponentenKaskadeTailwind

Wer Komponenten mit eigenem CSS baut, kennt das Muster: Eine Regel für die Überschrift in der Karte trifft plötzlich auch die Überschrift in einer anderen Komponente, die zufällig in der Karte steckt. Die üblichen Antworten sind längere Selektoren, Namenskonventionen wie BEM oder Werkzeuge wie CSS Modules, die Klassennamen umschreiben. @scope löst dasselbe Problem im Browser. Die Regel steckt einen Bereich ab, der an einem Element beginnt und auf Wunsch an einem anderen endet, und Selektoren darin greifen nur innerhalb dieser Grenzen. Treffen zwei Scopes auf dasselbe Element, gewinnt bei gleicher Spezifität der nähere. Chrome und Edge kennen @scope seit Version 118, Firefox seit 146 im Dezember 2025, und seit März 2026 führt MDN es als Baseline. Dieser Beitrag zeigt die Syntax, den Donut-Scope, die Rolle der Nähe in der Kaskade, wo @scope neben Tailwind hilft und wo nicht.

Das Wichtigste in Kürze
  • @scope (.card) { ... } wendet die Regeln darin nur auf Elemente innerhalb von .card an. Einfache Selektoren wie h3 oder img brauchen dafür keine längere Kette und bekommen keine zusätzliche Spezifität.
  • Mit to (...) bekommt der Bereich eine Untergrenze. @scope (.card) to (.card-slot) lässt alles in .card-slot unberührt, etwa eingesetzte Inhalte oder andere Komponenten. Das nennt man Donut-Scope.
  • Treffen zwei Scopes mit gleicher Spezifität auf dasselbe Element, gewinnt der, dessen Wurzel näher liegt. Die Nähe kommt in der Kaskade nach der Spezifität und vor der Reihenfolge im Stylesheet.
  • @scope isoliert nichts. Vererbte Eigenschaften wie color laufen weiter über die Grenze, und ein Browser ohne Unterstützung verwirft den ganzen Block. Für Pflicht-Styles braucht es deshalb eine Fassung, die auch ohne trägt.
01

Warum überschreiben sich Komponenten-Styles gegenseitig?

Weil ein Selektor in CSS nur sagt, wo etwas anfängt, aber nicht, wo es aufhört. .card h3 trifft jede Überschrift dritter Ordnung irgendwo innerhalb der Karte, auch die in einer Komponente, die in der Karte steckt und ihre eigenen Regeln mitbringt. Welche Regel gewinnt, entscheidet dann die Spezifität oder die Reihenfolge im Stylesheet, also etwas, das mit der Absicht nichts zu tun hat.

Die bekannten Antworten arbeiten alle um das Problem herum. BEM verlangt, dass jedes Element eine eigene Klasse mit dem Namen der Komponente trägt, damit Selektoren nie verschachtelt werden müssen. CSS Modules und ähnliche Werkzeuge schreiben Klassennamen beim Bauen um, sodass sie eindeutig werden. Beides funktioniert, beides verschiebt die Arbeit in Namen und Werkzeuge.

Was fehlte, war eine Grenze im CSS selbst: diese Regeln gelten hier, und dort nicht mehr. Genau das ist @scope.

Ein Selektor sagt, wo es anfängt. @scope sagt auch, wo es aufhört.

02

Wie funktioniert @scope?

Man nennt in Klammern die Wurzel des Bereichs, und alle Regeln im Block gelten nur für die Wurzel und ihre Nachfahren. Innerhalb des Blocks schreibt man einfache Selektoren wie h3, p oder img, ohne die Klasse der Komponente davor. Die Wurzel selbst erreicht man mit :scope.

Die Wurzel zählt bei der Spezifität nicht mit. Ein h3 im Scope ist so stark wie ein h3 ohne, MDN beschreibt das, als stünde ein :where(:scope) davor. Nur wer :scope ausdrücklich schreibt, bekommt dessen Gewicht dazu, das einer Klasse. Damit bleiben Selektoren kurz und schwach, und eine bewusst gesetzte Klasse am Element setzt sich weiter durch.

Es gibt auch eine Form ohne Klammern. Ein style-Element mit @scope ohne Wurzel gilt für das Elternelement des style-Elements. Das ist praktisch für Inhalte, die mitsamt ihrem CSS an einer Stelle eingesetzt werden, etwa eine einzelne Landingpage-Section aus einem CMS.

  @scope (.card) {  :scope {    border-radius: 16px;    padding: 1.5rem;  }   h3 {    font-size: 1.25rem;  }   img {    border-radius: 12px;  }}
Einfache Selektoren, die nur innerhalb der Karte greifen.
03

Was ist ein Donut-Scope?

Ein Bereich mit Loch: Er beginnt an der Wurzel und endet an einer Untergrenze, die man mit to angibt. @scope (.card) to (.card-slot) gilt für alles in der Karte, außer für das, was in .card-slot steckt. Die Wurzel gehört dazu, die Untergrenze selbst nicht.

Das ist genau der Fall vom Anfang. Eine Karte, in deren Inhalt andere Komponenten eingesetzt werden, kann ihre Überschriften und Bilder gestalten, ohne die Überschriften und Bilder der eingesetzten Komponenten zu berühren. Die Grenze steht im CSS, nicht in den Namen, und die eingesetzte Komponente muss nichts davon wissen.

Die Untergrenze darf eine ganze Liste sein. Wer mehrere Komponenten kennt, die nie von außen gestaltet werden sollen, schreibt sie mit Komma getrennt in die zweite Klammer.

  @scope (.card) to (.card-slot, .media-player) {  img {    aspect-ratio: 4 / 3;    object-fit: cover;  }}
Die Karte gestaltet ihre Bilder, aber nicht die im eingesetzten Inhalt.
Mit langem Selektor
  • .card img trifft auch die Bilder in jeder Komponente, die in der Karte steckt.
  • Um das zu verhindern, kommt ein :not() dazu, und die Spezifität steigt mit.
  • Die eingesetzte Komponente braucht danach selbst stärkere Selektoren, um sich durchzusetzen.
Mit Donut-Scope
  • img im Scope trifft nur die Bilder bis zur Untergrenze.
  • Die Spezifität bleibt die eines einfachen Elements.
  • Die eingesetzte Komponente gestaltet sich selbst, ohne gegen die Karte zu kämpfen.
04

Welcher Scope gewinnt, wenn zwei auf dasselbe Element treffen?

Bei gleicher Spezifität der, dessen Wurzel näher am Element liegt. Diese Nähe ist eine eigene Stufe in der Kaskade. Sie kommt nach Wichtigkeit, Ebenen und Spezifität, aber vor der Reihenfolge im Stylesheet. Bisher entschied an dieser Stelle die Reihenfolge, also die Frage, welche Regel zuletzt geschrieben wurde.

Das bekannteste Beispiel sind verschachtelte Farbschemata. Eine helle Fläche in einem dunklen Bereich, darin ein Link: Mit gewöhnlichen Selektoren wie .dark a und .light a gewinnt der, der weiter unten im Stylesheet steht, egal wie die Elemente verschachtelt sind. Mit zwei Scopes gewinnt der nähere, und der Link in der hellen Fläche wird hell gestaltet, auch wenn diese Fläche selbst in einem dunklen Bereich liegt.

Wichtig ist die Reihenfolge der Stufen. Nähe schlägt nur die Reihenfolge, nicht die Spezifität. Ein Selektor mit einer Klasse mehr gewinnt weiter, auch aus dem ferneren Scope. Wer sich auf die Nähe verlassen will, hält die Selektoren in beiden Scopes gleich stark.

@scope (.light) {  a { color: #1d4ed8; }} @scope (.dark) {  a { color: #93c5fd; }} <section class="dark">  <div class="light">    <a href="/pricing">See pricing</a>  </div></section>
Der Link nimmt die Farbe des nächsten Bereichs an, nicht die der letzten Regel.
Ebenen und Scopes ergänzen sich@layer und @scope beantworten verschiedene Fragen. Ebenen legen fest, welche Gruppe von Regeln grundsätzlich Vorrang hat, etwa Komponenten vor Grundstilen. Scopes legen fest, wo eine Regel gilt und welche bei gleichem Gewicht näher dran ist. Weil Ebenen in der Kaskade vor der Spezifität kommen und die Nähe danach, lassen sich beide ohne Widerspruch kombinieren.
05

Wo hilft @scope neben Tailwind?

Dort, wo man keine Klassen an jedes Element schreiben kann. Für eine Komponente, deren Markup man selbst schreibt, braucht es @scope meist nicht, denn eine Klasse wie text-lg trifft genau das Element, an dem sie steht. Das Problem der zu weit greifenden Selektoren entsteht dort gar nicht.

Anders ist es bei Inhalten, die von außen kommen. Fließtext aus einem CMS oder aus Markdown bringt Überschriften, Listen und Bilder ohne Klassen mit. Tailwind bietet dafür Varianten wie [&_h2]:text-2xl am Container oder ein Plugin für Fließtext. Ein kleines Stylesheet mit @scope (.article-body) to (.embed) ist oft lesbarer, und die Untergrenze schützt eingebettete Komponenten wie Code-Blöcke oder Videos vor den Regeln für den Text.

Der zweite Fall ist Markup aus Bibliotheken, deren Klassen man nicht ändern kann. Statt deren Klassen zu überschreiben, steckt man einen Scope um den Container und gestaltet die Elemente darin mit einfachen Selektoren, ohne dass die Regeln an anderer Stelle der Seite etwas anrichten.

  @scope (.article-body) to (.embed) {  h2 {    margin-block: 2.5rem 1rem;    font-size: var(--text-2xl);  }   a {    text-decoration-thickness: 1px;    text-underline-offset: 3px;  }}
Fließtext aus dem CMS gestalten, eingebettete Komponenten auslassen.
06

Wo stößt @scope an Grenzen?

An drei Stellen, und alle drei hängen damit zusammen, dass @scope eine Regel im Stylesheet ist und keine Trennung im Dokument. Die erste ist die Vererbung. Eigenschaften wie color oder font-family werden weiter an Kinder vererbt, auch über die Untergrenze hinweg. Der Donut-Scope verhindert nur, dass Selektoren dort greifen, nicht, dass vererbte Werte dort ankommen.

Die zweite ist das Verschachtelungszeichen &. MDN weist darauf hin, dass Browser und Versionen es innerhalb von @scope unterschiedlich behandeln, und die Chrome-Dokumentation rechnet ihm die Spezifität der Wurzel zu, während einfache Selektoren keine bekommen. Wer verlässliche Ergebnisse will, schreibt einfache Selektoren oder :scope.

Die dritte ist der Rückfall. Ein Browser ohne @scope verwirft den ganzen Block. Anders als bei einer neuen Eigenschaft, die einfach übersprungen wird, fehlen dann alle Regeln darin. Seit März 2026 ist das in aktuellen Browsern kein Thema mehr, für Seiten mit vielen Besuchern auf alten Geräten aber schon. Dort gehören die tragenden Regeln in gewöhnliche Klassen-Selektoren und die Feinheiten in den Scope.

  1. Vererbung läuft weiter. color, font-family und andere vererbte Werte erreichen auch Elemente hinter der Untergrenze.
  2. & ist unzuverlässig. Innerhalb von @scope einfache Selektoren oder :scope schreiben, nicht &.
  3. Ohne Unterstützung fehlt alles. Der ganze Block wird verworfen. Tragende Regeln deshalb außerhalb des Scopes schreiben, wenn alte Browser zählen.

@scope begrenzt, wo Regeln greifen. Was vererbt wird, begrenzt es nicht.

Teilen
Geschrieben von
Leonie RoesmannDesign Engineering

Wir bauen Websites und Web-Apps bei Systra Studios in Münster und legen die Bausteine daraus hier ab. Was in den Beiträgen steht, kommt aus echten Projekten, nicht aus einem Werbeprospekt.

Systra Studios: Webdesign aus Münster

Jede Karte hat ihre Grenzen.
Die Sections dazu stehen bereit.

Feature-Sections aus Karten, in denen Überschriften, Texte und Bilder nebeneinander stehen. Kopieren und dort, wo Inhalte aus einem CMS dazukommen, die Regeln für den Text mit @scope auf die Karte begrenzen.

  • Eine Datei je Komponente, als Quelltext im eigenen Projekt.
  • Gebaut mit React, TypeScript und Tailwind CSS v4.
  • Reduzierte Bewegung ist in jeder Komponente berücksichtigt.
Feature-Sections ansehen
FAQ

Häufige Fragen

Shadow DOM trennt, @scope begrenzt nur. Ein Shadow Root ist eine eigene Welt: Styles von außen kommen nicht hinein, Styles von innen nicht hinaus, abgesehen von vererbten Eigenschaften und ausdrücklich freigegebenen Teilen. Dafür braucht es ein Custom Element oder ein Skript, das den Shadow Root anlegt, und Formulare, Fokus und Suchen im Text verhalten sich an der Grenze teils anders. @scope ändert am Dokument nichts. Es ist eine Regel im Stylesheet, die festlegt, wo andere Regeln greifen. Styles von außen erreichen die Komponente weiter, und eine globale Regel mit höherer Spezifität gewinnt wie bisher. Für ein Design-System im eigenen Projekt reicht @scope fast immer. Shadow DOM lohnt sich, wenn eine Komponente in fremden Seiten laufen soll, deren CSS man nicht kennt.

Nein, der Bereich in den Klammern zählt nicht mit. Ein h3 innerhalb von @scope (.card) hat dieselbe Spezifität wie ein h3 ohne Scope, nämlich die eines einzelnen Elements. MDN beschreibt das so, als stünde vor jedem einfachen Selektor ein :where(:scope), und :where zählt nicht. Anders ist es, wenn man :scope selbst schreibt: :scope hat die Spezifität einer Klasse, :scope img also die einer Klasse plus eines Elements. Vorsicht beim Verschachtelungszeichen &: MDN weist darauf hin, dass Browser und Versionen es innerhalb von @scope unterschiedlich behandeln, und die Chrome-Dokumentation rechnet ihm die Spezifität der Wurzel zu. Wer verlässliche Ergebnisse will, schreibt innerhalb von @scope einfache Selektoren oder :scope und lässt & weg.

Für die meisten Komponenten nicht. Tailwind setzt Klassen direkt an das Element, das sie betreffen, und eine Klasse wie text-lg trifft genau dieses Element und kein anderes. Das Problem, das @scope löst, entsteht dort gar nicht. Es entsteht an den Stellen, an denen man keine Klassen an jedes Element schreiben kann: Inhalte aus einem CMS oder aus Markdown, Markup aus einer fremden Bibliothek, oder Grundregeln für alle Bilder und Überschriften in einem Bereich. Dort ist @scope die schlankere Alternative zu langen Selektoren oder zu einer Variante wie [&_h3]:text-lg an jedem Container. Ein kleines Stylesheet mit @scope (.article-body) für den Fließtext eines Blogs ersetzt dann eine lange Liste solcher Klassen.

Sie verwerfen den ganzen Block, und die Regeln darin fehlen vollständig. Das ist der wichtigste Unterschied zu Eigenschaften wie text-box oder field-sizing, die ein alter Browser einfach überspringt, während der Rest der Regel greift. Bei @scope fällt alles weg, was im Block steht. Chrome und Edge unterstützen es seit Version 118, Firefox seit 146 vom Dezember 2025, und MDN führt es seit März 2026 als Baseline; für Safari nennen caniuse und die Chrome-Dokumentation unterschiedliche erste Versionen, 17.4 und 26.4. Wer noch nennenswert ältere Browser bedient, schreibt die Regeln, ohne die eine Komponente kaputt aussieht, als gewöhnliche Klassen-Selektoren und nutzt @scope für die Feinheiten, deren Fehlen niemandem schadet. Mit jedem Monat wird dieser Rückfall weniger wichtig.

Deine Frage war nicht dabei? Wir helfen gern weiter.

Kontakt aufnehmen