Verschachteltes CSS – ganz ohne Sass, direkt im Browser
Das Feature, für das die meisten Leute überhaupt erst einen CSS-Präprozessor eingerichtet haben, kann der Browser inzwischen selbst. Kein Build-Schritt, keine Abhängigkeit – nur ein paar Regeln, die man kennen sollte.
Ich habe jahrelang Sass eingerichtet, hauptsächlich wegen einer einzigen Sache: Verschachtelung. Zusammengehörige Regeln standen dadurch zusammen statt über die Datei verteilt. Dafür brauchte es einen Build-Schritt, eine Abhängigkeit und einen Ordner mit generierten Dateien.
Das ist inzwischen erledigt — der Browser kann es selbst:
.karte {
padding: 16px;
border: 1px solid var(--line);
border-radius: 12px;
& h3 {
margin: 0 0 6px;
font-size: 17px;
}
& p {
color: var(--muted);
line-height: 1.5;
}
&:hover {
border-color: var(--akzent);
}
@media (max-width: 600px) {
padding: 12px;
}
}Alles, was .karte betrifft, steht an einer Stelle — inklusive Hover-Zustand und Media Query. Kein Suchen mehr nach der zugehörigen @media-Regel dreihundert Zeilen weiter unten.
Das kaufmännische Und
& steht für den äußeren Selektor. Der Unterschied ist wichtig:
.karte {
& .titel { } /* .karte .titel – ein Nachfahre */
&.aktiv { } /* .karte.aktiv – dasselbe Element */
& + & { } /* .karte + .karte – die nächste Karte */
}In den meisten Fällen darf man & vor einem Nachfahren-Selektor weglassen — h3 { … } funktioniert genauso wie & h3 { … }. Ich schreibe es trotzdem hin. Es kostet zwei Zeichen und macht beim Überfliegen sofort klar, dass hier verschachtelt wird.
Bei &:hover und &.aktiv ist es dagegen zwingend, weil sich sonst die Bedeutung ändert.
Die eine Regel, die man wirklich beachten muss
Nicht zu tief verschachteln. Das war schon bei Sass der klassische Fehler und ist hier derselbe:
/* ⚠️ so nicht */
.seite {
& .inhalt {
& .karte {
& .titel {
& a { color: red; } /* .seite .inhalt .karte .titel a */
}
}
}
}Das Ergebnis ist ein Selektor mit einer Spezifität, gegen die man später nur noch mit !important ankommt. Meine Grenze: zwei Ebenen. Wird es tiefer, ist das ein Hinweis, dass der Baustein eine eigene Klasse verdient hätte.
Was ich damit gewonnen habe
Der eigentliche Gewinn ist nicht die Verschachtelung, sondern das, was ich dafür nicht mehr brauche: keinen Build-Schritt, keine Node-Abhängigkeit, keine generierten Dateien im Repository. Die CSS-Datei, die im Browser landet, ist genau die, die ich geschrieben habe — mit Zeilennummern, die in den Entwicklerwerkzeugen stimmen.
Für ein kleines Projekt war eine Sass-Einrichtung schon immer schwer zu rechtfertigen. Jetzt gibt es dafür fast keinen Grund mehr. Wer Variablen braucht, hat mit var(--…) ohnehin etwas Besseres — die funktionieren nämlich zur Laufzeit und lassen sich per Dark Mode umschalten, was eine Sass-Variable nie konnte.
Du hast ein Projekt, bei dem der Build-Schritt mehr Ärger macht als er bringt? Manchmal lohnt sich der Rückbau. Schreib mir, ich sage dir ehrlich, ob es sich in deinem Fall rechnet.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
@layer in CSS: Kaskadenebenen statt !important-Wettrüsten
Irgendwann steht in jedem gewachsenen Stylesheet das erste !important – und ein halbes Jahr später das dreißigste. Mit @layer legst du die Reihenfolge einmal fest, und ab da gewinnt nicht mehr der längste Selektor, sondern die Ebene, die du bestimmt hast.
:is() und :where() – kürzere Selektoren, und einer davon wiegt nichts
Drei fast gleiche Selektoren untereinander, nur weil die Überschrift mal h2 und mal h3 heißt? :is() fasst das zusammen. Und :where() macht dasselbe, ohne die Spezifität hochzutreiben – der Unterschied ist der ganze Trick.
content-visibility – lange Seiten rendern, ohne dass der Browser alles auf einmal baut
Bei sehr langen Seiten rechnet der Browser Layout und Malen für jeden Abschnitt – auch für die, die 8.000 Pixel weiter unten liegen. Eine CSS-Eigenschaft verschiebt diese Arbeit auf den Moment, in dem sie gebraucht wird.