Scoped CSS & :global
Styles in Svelte gelten standardmäßig nur für ihre eigene Komponente. Kein Ausufern, keine Klassenkonflikte — und wenn du globale Styles willst, sagst du es explizit.
Styles sind standardmäßig scoped
Das <style> einer Komponente gilt nur für diese Komponente.
Schreibst du eine Regel für h1, betrifft sie ausschließlich die <h1>-Elemente in dieser Datei — nicht die in anderen Komponenten.
<h1>Titel dieser Komponente</h1>
<style>
/* Gilt NUR für <h1> in DIESER Datei.
Svelte hängt automatisch eine eindeutige Klasse an,
z.B. <h1 class="s-a1b2c3">. */
h1 {
color: rebeccapurple;
}
</style>Wie funktioniert das? Der Compiler vergibt pro Komponente eine eindeutige Klasse und hängt sie an die betroffenen Elemente und Selektoren an. Aus deiner einfachen Regel wird also etwas Spezifisches:
<!-- Svelte erzeugt aus dem obigen Markup ungefähr das: -->
<h1 class="svelte-a1b2c3">Titel dieser Komponente</h1>
<style>
h1.svelte-a1b2c3 { color: rebeccapurple; }
</style>ℹ️ Du musst dir nichts merken
Das Anhängen der Klasse passiert vollautomatisch zur Build-Zeit. Du schreibst ganz normales CSS — Svelte sorgt dafür, dass es dort bleibt, wo es hingehört.
Rails-Vergleich: der globale Namensraum
🛤️ Asset Pipeline kennt kein Scoping
In Rails werden alle Stylesheets über die Asset Pipeline (oder Propshaft/Sprockets) zu einem
großen, globalen Stylesheet zusammengefügt. Eine .card-Regel
aus users.css und eine gleichnamige aus products.css landen im
selben Namensraum und überschreiben sich gegenseitig — je nach Ladereihenfolge.
/* app/assets/stylesheets/application.css */
/* Alle Stylesheets landen im selben globalen Namensraum. */
/* users.css */
.card { border: 1px solid #ccc; }
/* products.css — ÜBERSCHREIBT versehentlich obiges .card! */
.card { border: 2px solid red; }
/* Keine Kapselung → Konflikte, BEM/Namespacing als Notlösung */Deshalb greifen Rails-Teams zu Konventionen wie BEM oder Namespacing, um Kollisionen zu
vermeiden. In Svelte ist das überflüssig: Jede Komponente hat ihren eigenen
CSS-Namensraum. Du darfst überall .card heißen, ohne dass es kracht.
Bewusst global werden: :global(...)
Manchmal willst du, dass eine Regel über die Komponentengrenze hinaus wirkt — etwa
für Basis-Styles oder für Markup, das du nicht selbst kontrollierst. Dann wickelst du den
Selektor in :global(...):
<style>
/* Bricht das Scoping bewusst auf — gilt app-weit: */
:global(body) {
margin: 0;
font-family: system-ui;
}
/* Auch nur für Kinder DIESER Komponente,
aber inkl. dynamisch eingefügtem Markup: */
.inhalt :global(a) {
color: var(--brand);
}
</style>Für wirklich app-weite Grundlagen (Reset, Schriftarten, CSS-Variablen) nutzt du außerdem ein klassisches globales Stylesheet, das du typischerweise im Root-Layout importierst:
// src/routes/+layout.svelte oder ein Import in +layout
import '../app.css'; // globale Basis-Styles, Reset, CSS-Variablen⚠️ Sparsam einsetzen
:global() ist die bewusste Ausnahme, nicht die Regel. Jede globale Regel
bringt genau die Konflikte zurück, die Scoping eigentlich verhindert. Nutze es gezielt für
Basis-Styles — nicht als Standard.
Klassen dynamisch: die class:-Direktive
Um eine Klasse abhängig von einer Bedingung zu setzen, brauchst du kein String-Gebastel. Die class:name={bedingung}-Direktive fügt die Klasse hinzu, wenn die Bedingung true ist, und entfernt sie sonst:
<script lang="ts">
let aktiv = $state(false);
</script>
<!-- class:name={bedingung} — Klasse "aktiv" nur wenn true -->
<button class:aktiv onclick={() => (aktiv = !aktiv)}>
Umschalten
</button>
<!-- Kurzform: class:aktiv ist gleichbedeutend mit class:aktiv={aktiv} -->
<!-- Mehrere gleichzeitig: -->
<div class:aktiv class:gross={istGross}>…</div>
<style>
.aktiv { background: var(--brand); color: white; }
.gross { font-size: 2rem; }
</style>Steht die Klasse gleich der Variable, gibt es die Kurzform: class:aktiv ist
dasselbe wie class:aktiv={aktiv}. Mehrere class:-Direktiven am
selben Element sind problemlos möglich.
Einzelne Eigenschaften: die style:-Direktive
Für einzelne, dynamisch berechnete CSS-Eigenschaften gibt es das Gegenstück style:prop={wert}. Das ist lesbarer als ein zusammengesetzter style-String und ideal für Werte, die aus dem Zustand kommen:
<script lang="ts">
let farbe = $state('#ff3e00');
</script>
<!-- style:prop={wert} setzt eine einzelne CSS-Eigenschaft dynamisch -->
<p style:color={farbe}>Farbiger Text</p>
<!-- Mehrere & mit Einheiten: -->
<div style:width="{breite}px" style:opacity={0.5}>…</div>Live ausprobieren
Der Button unten nutzt zwei class:-Direktiven und die style:-Direktive für die Farbe. Alles reagiert live auf $state:
Der Code dahinter — reines class: plus ein bisschen scoped CSS:
<script lang="ts">
let aktiv = $state(false);
let gross = $state(false);
</script>
<button
class:aktiv
class:gross
onclick={() => (aktiv = !aktiv)}
>
{aktiv ? 'Aktiv!' : 'Klick mich'}
</button>
<style>
button { transition: all 0.2s; }
.aktiv { background: #ff3e00; color: white; }
.gross { transform: scale(1.25); }
</style>Zusammenfassung
💡 Das Wichtigste
- CSS in einer Komponente ist automatisch scoped — keine Konflikte.
:global(...)durchbricht das Scoping bewusst; sparsam einsetzen.- App-weite Basis-Styles kommen aus einem globalen Stylesheet (z. B. im Layout).
class:name={bedingung}schaltet Klassen reaktiv; Kurzformclass:name.style:prop={wert}setzt einzelne CSS-Eigenschaften dynamisch.
🎯 Probier es selbst
Füge dem Demo-Button eine dritte class:-Direktive hinzu (z. B. class:demo-rund), die ihn per Checkbox stark abrundet
(border-radius: 9999px). Beobachte, wie sich Klassen unabhängig voneinander
an- und abschalten lassen.