A Specificity Battle! (and other trickery)
The following is a guest adventure by Francisco Dias from HubSpot. I saw Francisco and Cris Necochea give this as a quick, fun presentation at the Show & Tell at CSS Dev Conf this year. He graciously agreed to prepare it for a blog post!
19 ways to override a background color
Starting with pretty much the lowest specificity selector:
div {
background: black;
}
There are an awful lot of ways to one-up the existing specificity without repeating the same trick.
Click through each step to see the spies force their color (black or white) on top of the previous color. When the specificity one-upping stops, there are still more tricks up their sleeve!
(The Spy shape is created from a clip-path
on the div
in the demo to make it more fun and Spy-vs-Spy like. If the browser you’re currently in doesn’t support clip-path, you’ll just see white or black boxes.)
See the Pen Spy Vs Spy by Francisco Dias (@FranDias) on CodePen.
What the heck is going on here? The text inside the demo Pen above has a bit more information on each step, but just as a quick overview, here are the stages:
Step 1: Element Selector
0, 0, 0, 1
div {
background: black;
}
Step 2: Two Element Selectors
0, 0, 0, 2
body div {
background: white;
}
Step 3: Three Element Selectors
0, 0, 0, 3
html body div {
background: black;
}
Step 4: Class Selector
0, 0, 1, 0
.spy {
background: white;
}
Step 5: Class + Element Selector
0, 0, 1, 1
div.spy {
background: black;
}
Step 6: Element + Class + Pseudo-Class Selector
0, 0, 2, 1
div.spy:only-of-type {
background: white;
}
Step 7: Stacked Classes
0, 0, ?, 0
.spy.spy.spy.spy.spy.spy.spy.spy.spy {
background: black;
}
Step 8: ID Selector
0, 1, 0, 0
#spy {
background: white;
}
Step 9: ID + Attribute Selector
0, 1, 1, 0
#spy[class^="s"] {
background: black;
}
Step 10: Combining Many Above…
0, 1, 3, 3
html body div#spy[class="spy"]:first-of-type.spy {
background: white;
}
Step 11: Inline Style
1, 0, 0, 0
<div class="spy" id="spy" style="background: black;"></div>
Step 12: !important
Kind of like [1, 0, 0, 0] on a per-property basis (can override an inline style).
div {
background: white !important;
}
Step 13: !important on inline style
Kind of like [?, 0, 0, 0] on a per property basis, which no CSS can override.
<div class="spy" id="spy" style="background: black !important;"></div>
Step 14: box-shadow trickery
Some properties paint on top of others. box-shadow
paints on top of background.
div {
box-shadow: inset 0 9001rem 0 white;
}
Step 15: Invert Filter
div {
-webkit-filter: invert(1);
filter: invert(1);
}
Step 16: Pseudo Element Overlay
div::after {
content: "";
height: 9001px;
width: 9001px;
background: black;
top: 0;
left: 0;
position: absolute;
}
Step 17: !important again
All the specificity battles could be fought again, including the per-property battle using !important;
, so let’s end that here.
div:after {
background: white !important;
}
Step 18: @keyframes trickery
!important
isn’t animateable, so the animation can override it.
@keyframes white {
to {
background: black;
}
}
div:after {
animation: white 1s linear;
animation-play-state: paused;
animation-delay: -1s;
animation-fill-mode: forwards;
}
Step 19: Coloring the Content
Putting a huge block character over everything flips the color again!
div:after {
content: "█";
line-height: 0;
color: white;
font-size: 9001px;
}
A gif of the demo in large-screen-Chrome:
A Specificity Battle! (and other trickery) is a post from CSS-Tricks