Problem
Ihr sollt ein Third-Party-Widget in eine Webseite einbinden. Das Widget bringt schon eigene Styles mit, aber die entsprechen nicht dem gewünschten Look-and-Feel. Also wollt ihr das Third-Party-CSS mit eigenen Styles überschreiben. Aber das klappt irgendwie nicht!?
Erklärung
Für den (hier vorliegenden) Fall, dass für ein DOM-Element dieselbe CSS-Eigenschaft mehrfach gesetzt wird, gibt es viele Entscheidungskriterien, welche der Deklarationen "gewinnt". Mit absteigender Wichtigkeit sind das:
-
Ist die Deklaration mit !important versehen?
-
Ist es ein Inline-Style?
-
Spezifität des zugehörigen CSS-Selektors
-
Anzahl der IDs (z.B. #example)
-
Anzahl der Klassen (z.B. .myClass), Pseudoklassen (z.B. :hover) und Attribute (z.B. [type="radio"])
-
Anzahl der Elemente (z.B. a oder div) und Pseudoelemente (z.B. ::before)
-
-
Reihenfolge ("spät gewinnt")
-
Vererbung
Die Spezifität wird oft durch drei Zahlen dargestellt, z.B. (0, 2, 1) für zwei (Pseudo-)Klassen und ein (Pseudo-)Element.
Für alle Entscheidungskriterien gilt: Sobald der erste Unterschied gefunden ist, ist die Prüfung vorbei.
Das heißt: Eine einzige ID (1, 0, 0) ist wichtiger als 1000 Klassen (0, 1000, 0). Pseudoklassen sind aber genauso wichtig wie normale Klassen.
Lösung
Die Reihenfolge geht in der Regel zu Gunsten der Third-Party-Styles aus. Und von !important und Inline-Styles solltet ihr nach Möglichkeit die Finger lassen, weil diese später noch schwerer zu überschreiben sind.
Bleibt noch die Spezifität: Ihr müsst es irgendwie schaffen, dass die Spezifität der Selektoren in eurem CSS über der Selektor-Spezifität im Third-Party-CSS liegt.
Option 1:
Ihr ergänzt den Selektor um weitere Dinge, die auf das gewünschte Element zutreffen.
Das können z.B. das HTML-Element (div.tp-widget) oder die Klasse eines Eltern-Elements (.tp .tp-widget) sein.
Option 2:
Ihr schreibt eine Klasse doppelt in den Selektor.
Vorteil: Das erhöht die Spezifität, ohne die Logik zu ändern (wenn die Klasse einmal da ist, ist sie auch "doppelt" da).
Nachteil: Es ist alles andere als offensichtlich, warum die Klasse doppelt dort steht. Hier solltet ihr also immer einen erklärenden Kommentar dazu schreiben.
Die Spezifitätserhöhung funktioniert leider nur dann, wenn auch das Third-Party-CSS per <style> eingebunden ist. Bei Third-Party-Inline-Styles hilft leider nur noch ein !important.
Beispiel
Im Third-Party-Widget haben Links eine Unterstreichung. Als Hover-Effekt ändert sich die Farbe.
Im gewünschten Design sind Links aber nur farbig, die Unterstreichung stellt stattdessen den Hover-Effekt dar.
.tp-widget a { /* (0, 1, 1) */
text-decoration: underline;
}
.tp-widget a:hover { /* (0, 2, 1) */
color: skyblue;
}
/* increase specificity to override
third-party styles */
.tp-widget.tp-widget.tp-widget a {
text-decoration: none;
color: #ef7d00;
}
.tp-widget.tp-widget a:hover {
text-decoration: underline;
}
Hier musste das .tp-widget sogar verdreifacht werden (0, 3, 1), um die Farbe im Third-Party-Hover-Style (0, 2, 1) zu überschreiben. Im Hover-Style reichen hingegen zwei .tp-widgets (die zusammen mit :hover ebenfalls auf 3 (Pseudo-)Klassen kommen), weil bei gleicher Spezifität die Reihenfolge entscheidet.
Weiterführende Aspekte:
- VS Code zeigt die Selektor-Spezifität als Tooltip an (siehe Screenshot rechts).
- Beispiel zum Ausprobieren
- Specificity: CSS declaration