Steuerung der Spezifizität bei mehreren Attributen am gleichen Tag
Autor: Aykut Şensoy, Datum:
Die Definition von mehreren CSS-Klassen an einem html-Tag ist gängige Praxis, insbesondere die Nutzung einer zweiten Klasse als Modifier, die die erste Klasse überschreibt. Weiterhin ist es auch ganz normal und sogar oft notwendig, mehrere Attribute am gleichen html-Tag zu definieren. Der Zugriff auf Klassen erfolgt mit Klassen-Selektoren, analog dazu der Zugriff auf Attribute mit Attribut-Selektoren. Das Problem dabei ist lediglich die Spezifizität.
Wenn am gleichen html-Tag zwei CSS-Klassen definiert sind, haben sie die gleiche Spezifizität. Die zweite Klasse überschreibt die erste nicht, weil sie nach der ersten Klasse definiert wurde, sondern nur wenn die CSS in der richtigen Reihenfolge deklariert wurde. D.h. die Reihenfolge der Klassen spielt keine Rolle, solange die CSS in der gewünschten Reihenfolge definiert wurde. Hier die Beispiele:
<style>
.foo {
font-size: 24px;
}
.bar {
font-size: 32px; // bar überschreibt foo
}
</style>
<div class="foo bar">lorem ipsum</div>
<div class="bar foo">lorem ipsum</div>
<style>
.bar {
font-size: 32px;
}
.foo {
font-size: 24px; // foo überschreibt bar
}
</style>
<div class="foo bar">lorem ipsum</div>
<div class="bar foo">lorem ipsum</div>
Es ist also egal, in welcher Reihenfolge "foo" oder "bar" im HTML-Quellcode stehen, relevant ist nur, dass in der CSS "bar" nach "foo" deklariert wurde, damit "foo" durch "bar" überschrieben wird.
In großen Projekten ist das aber nicht so eine sichere Sache. Denn beim Bundling (oder bei einer Minifizierung) einer großen Menge CSS, ist nicht unbedingt gesichert, dass CSS immer in der gewünschten Reihenfolge gebundelt wird. In solchen Fällen hilft das Chaining von Klassen. Dadurch lässt sich per CSS die Spezifizität steuern und die Reihenfolge der CSS ist dann nicht mehr relevant. Hier ein entsprechendes Beispiel:
<style>
.foo.bar {
font-size: 32px; // bar überschreibt foo
}
.foo {
font-size: 24px;
}
</style>
<div class="foo bar">lorem ipsum</div>
<div class="bar foo">lorem ipsum</div>
Wie ist es aber bei Attribut-Selektoren? Da existiert das gleiche Problem. Attribute am gleichen html-Tag haben die gleiche Spezifizität, d.h. auch hier ist die Reihenfolge am html-Tag irrelevant, es entscheidet allein die Reihenfolge in der CSS. Genau dieses Problem hatte ich in einem Fall, wo wir für unterschiedliche Mandanten unterschiedliche Themings hatten, die über data-Attribute mit CSS-Variablen gesteuert werden. Hier die Beispiele:
<style>
[data-tenant="foo"] {
font-size: 24px;
}
[data-theme="bar"] { // bar überschreibt foo
font-size: 32px;
}
</style>
<div data-tenant="foo" data-theme="bar">lorem ipsum</div>
<div data-theme="bar" data-tenant="foo">lorem ipsum</div>
<style>
[data-theme="bar"] {
font-size: 32px;
}
[data-tenant="foo"] { // foo überschreibt bar
font-size: 24px;
}
</style>
<div data-tenant="foo" data-theme="bar">lorem ipsum</div>
<div data-theme="bar" data-tenant="foo">lorem ipsum</div>
Wenn man auch hier die Reihenfolge der CSS-Deklarationen unabhängig gestalten will, dann hilft hier auch nur das Chaining. Es ist zwar allgemein nicht üblich bei Attribut-Selektoren zu chainen, aber Attribut-Selektoren lassen sich auch per Chaining in ihrer Spezifizität beeinflussen. Interessant ist die Syntax, die nicht ganz so offensichtlich ist. Hier ein entsprechendes Beispiel:
<style>
[data-tenant="foo"][data-theme="bar"] { // bar überschreibt foo
font-size: 32px;
}
[data-tenant="foo"] {
font-size: 24px;
}
</style>
<div data-tenant="foo" data-theme="bar">lorem ipsum</div>
<div data-theme="bar" data-tenant="foo">lorem ipsum</div>
Der Trick ist also die Attribut-Selektoren ohne Punkt und ohne Leerzeichen direkt nebeneinander zu schreiben, um sie zu chainen. Damit der Parser auch nicht in allen html-Elementen nach den Attributen sucht, kann man das Ganze noch wie folgt auf das html-Element eingrenzen.
<style>
div[data-tenant="foo"][data-theme="bar"] { // bar überschreibt foo nur an div-Elementen
font-size: 32px;
}
[data-tenant="foo"] {
font-size: 24px;
}
</style>
<div data-tenant="foo" data-theme="bar">lorem ipsum</div>
<div data-theme="bar" data-tenant="foo">lorem ipsum</div>