While reading the MDN page for @keyframes
, which are used to make CSS animations, I saw the sentence:
Declarations in a keyframe qualified with !important
are ignored.
This seemed surprising. I generally have the sense that adding !important
to a declaration makes that declaration “win out”, i.e. over ones that came earlier in the stylesheet and ones that lack the !important
annotation.
I dug into why this is the case, and understanding it starts all the way back with what “cascading” in Cascading Style Sheets (CSS) means.
The “cascading” in Cascading Style Sheets (CSS)
One guiding principle of CSS is that multiple different stylesheets can influence how a document is presented. There are two processes that help make this happen:
Cascading
Defaulting
In cascading, we have different declarations trying to set values for the same combination of element and property, and the cascade figures out which one to use, based on a particular order of precedence.
Defaulting is the opposite: no declarations attempt to set a value for a given element/property combination, so the value defaults to inheriting from the parent or using a property’s initial value.
We’ll be focusing on the cascade, because the @keyframes
quirk is about the conflict between regular animation declarations and !important
ones.
The cascade sorting order and !important
To determine which value wins, the cascade looks at where the declaration came from. There are three core origins:
Author origin: Think of this as the developer of the website. Style sheets with this origin might be included in the webpage document or linked externally.
User origin: The reader of the website can specify styles to be used, often for accessibility purposes, like making the font size large across all sites.
User-agent origin: This usually refers to the default style sheets provided by browsers.
There are also two origins that arise from extensions to CSS:
Animation origin
Transition origin
The !important
annotation exists to let authors or users move a declaration from its default spot in the precedence order to a certain higher one. A declaration is called important if it contains the !important
annotation. Otherwise, it is normal.
Putting these categories all together, this is the cascade sorting order:
Transition
Important user agent
Important user
Important author
Animation
Normal author
Normal user
Normal user agent
Notice that the three important origins are in the reverse order of the three normal origins. As alluded to before, this can be helpful for accessibility, letting !important
user origin styles override declarations that a website author deems !important
.
Also, notice that all important origins, specifically the important author origin (#4), have more weight than animation (#5). This is something that the CSS specification on animations points out, and the MDN docs reflect.
Back to the main question
Once I learned all this, I thought: well, that’s enlightening, but it doesn’t fully explain… why should and why does !important
not work in @keyframes
? Can’t an important declaration in a keyframe just be extra-emphasized but effectively do the same thing as a normal one?
The why: As mentioned above, !important
provides a way to shift the balance of power among different style declaration origins. Animations already override all normal declarations, so it’s useful to have something at one’s disposal to override animations. That “something” is !important
.
As for how, I looked into how animation works. It seems that the animation process as a whole looks at the keyframes and then adds appropriate values into the CSS cascade. But the declarations in the keyframes don’t directly interact with the cascade themselves. !important
makes no sense outside the context of cascading(!), so important declarations are invalid and therefore ignored by the animation process.