Why we use `!important` with Tailwind
Tl;dr: We use !important
because it solves annoying specificity issues. Despite being overkill in most situations, we haven't come across any practical drawbacks from globally enabling it.
If you want to learn more about how we came to that conclusion and how CSS specificity works, read on!
Tailwind has a configuration option to mark all utilities as !important
.
// tailwind.config.jsmodule.exports = { important: true,}
.p-4 { padding: 1rem !important;}
We enable this setting because utilities should always have the final say. This is a habit we inherited from ITCSS long before Tailwind existed. We weren't building interfaces utility-first, but we were using utilities.
From Harry Roberts' introduction to ITCSS:
This layer beats – or 'trumps' – all other layers, and has the power to override anything at all that has gone before it. It is inelegant and heavy-handed, and contains utility and helper classes, hacks and overrides.
Most of the time, this doesn't matter in Tailwind. When you style an element with utilities, the odds are slim that there will be clashes among them because they target a single CSS property.
<button class="px-2 font-semibold border">
This applies to padding-left
, padding-right
, font-weight
, and border
. No properties are declared twice. This is different when you want to override a component with utilities.
.card { padding: 2rem;} .p-4 { padding: 1rem;}
<div class="card p-4">
In this example, the padding
property gets applied twice. To ensure the utility wins, Tailwind places utilities after components in the generated CSS. The other way around, utilities would never win.
@tailwind base;@tailwind components;@tailwind utilities;
This falls apart when a component class has a higher specificity. For example, when using the @tailwindcss/typography
.prose
class.
<div class="prose"> <h1 class="font-medium"> </h1></div>
.prose h1
specifies font-weight: 800
. The .font-medium
class will never apply because .prose-h1
has a higher specificity.
Visit specificity.keegan.st to play around with the specificity calculator.
Setting important: true
was also the Tailwind team's response to a similar issue.
This breaks our guiding principle: utilities should always have the final say. To work around these kinds of problems, we mark all utilities as !important
.
Using !important
for all utilities is like using a sledgehammer to hit a nail in the wall. Despite being overkill in most situations, we haven't come across any practical drawbacks from globally enabling it.
Alternative methods
If you don't like the idea of !important
everywhere, Tailwind provides two more escape hatches.
The high specificity selector solution
Tailwind has another option: add a high specificty selector to all utilities.
// tailwind.config.jsmodule.exports = { important: '#app',}
The #app
selector is arbitrary, as long as it has a high specificity like an ID. Tailwind will prepend all utilities with this selector.
#app .p-4 { padding: 1rem;}
This solves the .prose h1
vs. .font-medium
problem because #app
will always win.
The benefit is that you're not forced to use !important
when you want to override a utility. Yes, this breaks our "utilities should always have the final say" guideline. But I've come across situations where I want to override a utility with an inline style in JavaScript.
This example doesn't work without !important
with the important: true
configuration:
<div class="flex" style="display: none;"></div>
The downsides are that your stylesheet depends on an arbitrary selector to exist (you need to wrap everything in #app
) and the noise it adds to the utilities' selectors (which is a minor issue).
We haven't tried important: '#app'
in a meaningful project yet—because we hadn't found out about it before—but might in the future.
Tailwind JIT
Tailwind's new Just-In-Time mode comes with an on-demand !important
modifier.
<div class="prose"> <h1 class="!font-medium"> </h1></div>
With this modifier, we'd be able to keep !important
out of our codebase and call upon it when needed. After further consideration, I don't think we'll be turning off !important
for two reasons.
First, it makes it harder for developers with less CSS knowledge to contribute. They now have to make a conscious decision between an !important
and non-!important
utility.
Second, it can get confusing when we extract components. We'd need to add !important
preemptively to ensure it renders as expected depending on the context.
<!-- Header --><h1 class="font-medium"></h1> <!-- a) This works --><Header /> <!-- b) This doesn't --><div class="prose"> <Header /></div>
By specifying CSS rules with !important
, we can ensure they're applied in no matter what context. Exactly what we want of utilities.
Resources
A lot of our opinions on CSS are based on Harry Roberts' guidelines. He wrote an article about !important
on his blog, CSS Wizardry.
If you want to learn how specificity is calculated, read up on MDN. If you learn more about the cascade in general, I recommend this excellent article on CSS Tricks.