The Complete CSS !important Interactive Tutorial
!important is CSS’s “override switch”… but it’s not a magic wand. It can save your day, or it can
quietly
ruin your future weekends (because now you have to fight your own CSS later).
In this tutorial, you’ll learn how !important actually fits into the cascade, how it interacts with
specificity, why it sometimes “doesn’t work”, and what to do when inline styles enter the chat.
What !important is (and isn’t)
When you add !important to a declaration, you are telling the browser:
“If there’s a conflict for this property, prefer this declaration over normal ones.”
- It affects only that declaration (that property), not the whole selector.
- It does not increase selector specificity. It changes the “importance” step in the cascade.
-
It can still be overridden (by another
!importantdeclaration with greater specificity, or later source order if specificity ties).
.note {
background: #ffe9a8 !important;
}
.note {
background: #c9f0ff;
}
.note {
background: #c9f0ff !important;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrapper {
display: grid;
gap: 12px;
font-family: ui-sans-serif, system-ui, sans-serif;
padding: 16px;
}
.note {
padding: 14px 16px;
border: 2px solid #111;
border-radius: 14px;
}
.hint {
font-size: 14px;
opacity: 0.85;
}
This box is .noteTry each snippet. Notice:!importantonly affects background.
In snippet 1, the background “wins” even if another normal rule exists. In snippet 3, the later rule also uses
!important, so now we’re back to normal CSS tie-breakers (specificity, then source order).
The cascade in plain English
When two declarations try to set the same property on the same element, the browser decides which one wins using a predictable order. Here’s the beginner-friendly version:
- Origin: where the CSS comes from (browser default, your CSS, extensions, etc.).
-
Importance: normal vs
!important. - Specificity: how “targeted” the selector is.
- Source order: if everything ties, the later rule wins.
The biggest takeaway: !important is not “above everything”. It’s “above normal declarations”
within the same origin/importance context, and it still participates in specificity comparisons against
other !important rules.
Learn more about the cascade in the CSS Cascade Interactive Tutorial.
CSS specificity (the scoreboard)
Specificity is a way to compare selectors. A helpful mental model is a 3-part score:
-
IDs (greatest specificity among these three):
#header -
Classes / attributes / pseudo-classes:
.card,[data-x],:hover -
Elements / pseudo-elements:
button,h2,::before
The selector with the greater score wins. If the score is equal, the later rule wins.
Learn more about specificity in the CSS Specificity Interactive Tutorial.
a {
color: #2c5cff;
}
.link {
color: #b31d1d;
}
#nav .link {
color: #0a7a2f;
}
#nav a.link {
color: #7a1bb3;
}
*,
::before,
::after {
box-sizing: border-box;
}
.demo {
font-family: ui-sans-serif, system-ui, sans-serif;
padding: 16px;
border: 2px solid #111;
border-radius: 14px;
display: grid;
gap: 10px;
}
.small {
font-size: 14px;
opacity: 0.8;
}
Play with the selectors above, delete some then CTRL-Z them back in place. Notice how a is weaker than
.link, and how adding an ID selector (#nav) makes the rule much harder to beat.
Using !important responsibly
The most common “good” uses of !important are:
-
Utility override classes like
.is-hidden,.sr-only,.no-scroll. - Third-party CSS you can’t change (temporary patch until you can fix properly).
- Debugging: quickly prove whether “this property is being overridden”.
.card {
display: block;
}
.is-hidden {
display: none !important;
}
.is-hidden {
display: none !important;
}
.card {
display: block;
}
*,
::before,
::after {
box-sizing: border-box;
}
.grid {
display: grid;
gap: 12px;
font-family: ui-sans-serif, system-ui, sans-serif;
}
.card {
padding: 14px 16px;
border: 2px solid #111;
border-radius: 14px;
background: #f2f2f2;
}
.note {
font-size: 14px;
opacity: 0.85;
}
CardThis element has both.cardand.is-hidden.Snippet 2 usesdisplay: none !important. Snippet 3 shows that even if.cardappears later, the!importantdeclaration still wins.
Used like this, !important acts like a deliberate “escape hatch” with a clear, narrow goal.
CSS !important override: how to beat it
Here’s the rule you’ll use 99% of the time:
-
If a declaration has
!important, you usually need another declaration with!importantto compete. -
When both declarations are
!important, specificity decides. - If specificity ties, later source order wins.
.button {
background: #1e6bff !important;
}
.button {
background: #1e6bff !important;
}
.page .button {
background: #0a7a2f !important;
}
.button {
background: #1e6bff !important;
}
.page .button {
background: #0a7a2f;
}
*,
::before,
::after {
box-sizing: border-box;
}
.page {
font-family: ui-sans-serif, system-ui, sans-serif;
padding: 16px;
display: grid;
gap: 12px;
}
.button {
border: 2px solid #111;
border-radius: 999px;
padding: 10px 14px;
cursor: pointer;
}
.small {
font-size: 14px;
opacity: 0.85;
}
Snippet 2 beats the original!importantby using another!importantplus a more specific selector. Snippet 3 shows a common mistake: adding specificity but forgetting!important.
The key idea: you don’t beat !important with “normal CSS”. If you want to override it
in CSS,
you usually need your own !important, and then you must win specificity (or source order).
What about inline styles?
Inline styles (like style="color: red;" in the HTML) behave like they have great specificity.
If the inline declaration itself uses !important, it’s effectively “CSS final boss”.
Later in the tutorial you will learn how to use JavaScript to remove or change inline styles, which is often the
best solution when you’re fighting an inline !important.
CSS !important not working: common reasons
If you wrote !important and nothing changed, one of these is almost always the reason:
-
You’re changing the wrong property. Example: you try
colorbut the text is inside a child element that has its own color. - The element is not the one you think. Example: your selector doesn’t match, or matches a different element.
-
Another rule is also
!importantand is more specific. - The style is inline (or injected later by JavaScript).
- You’re fighting a different origin (less common, but can happen with extensions or user styles).
- You’re overriding a shorthand with a longhand (or vice versa) in a way you didn’t expect.
.card {
color: #b31d1d !important;
}
.card {
color: #b31d1d !important;
}
.card strong {
color: #0a7a2f;
}
.card {
color: #b31d1d !important;
}
.card strong {
color: #0a7a2f !important;
}
.card {
color: #b31d1d !important;
}
.card strong {
color: inherit;
}
*,
::before,
::after {
box-sizing: border-box;
}
.card {
font-family: ui-sans-serif, system-ui, sans-serif;
padding: 16px;
border: 2px solid #111;
border-radius: 14px;
background: #f7f7f7;
}
.small {
margin-top: 8px;
font-size: 14px;
opacity: 0.85;
}
This word can be styled separatelySnippet 1 changes the card’s color. Snippet 2 shows a child rule can still set its own color. Snippet 3 adds!importantto the child. Snippet 4 usesinheritto “force” inheritance.
Debug habit: in DevTools, click the element and look at the “Computed” styles. The browser will literally show you which rule won and which ones were crossed out.
CSS !important class: myth and reality
You can’t make a class “more important” as a selector by adding !important somewhere. Why?
Because !important is not part of the selector. It belongs to a declaration.
What you can do is:
- Add a more specific selector for the same class (for example, include a parent class).
-
Use harmless specificity “boosters” like
:not(#incspec)(a common trick because it adds ID-level specificity without matching anything).
.badge {
background: #ddd;
}
.badge {
background: #ddd;
}
.widget .badge {
background: #c9f0ff;
}
.badge {
background: #ddd;
}
.badge:not(#incspec) {
background: #ffe9a8;
}
.badge {
background: #ddd !important;
}
.badge:not(#incspec) {
background: #ffe9a8 !important;
}
*,
::before,
::after {
box-sizing: border-box;
}
.widget {
font-family: ui-sans-serif, system-ui, sans-serif;
padding: 16px;
border: 2px solid #111;
border-radius: 14px;
display: grid;
gap: 10px;
}
.badge {
display: inline-block;
padding: 6px 10px;
border: 2px solid #111;
border-radius: 999px;
}
A caution: specificity hacks are powerful, but they are also a signal you may want to refactor your CSS architecture. Use them as a last resort, not as a lifestyle.
CSS !important inline: the “remove it with JavaScript” moment
Inline styles are applied directly on the element. When you see something like:
<div style="color: red;">
it often beats your stylesheet rules.
Even worse: <div style="color: red !important;">
is very hard to override with CSS alone.
In practice, if inline styles are being injected by JavaScript (or a page builder, or a widget), the most reliable fix is: remove or change the inline style.
JavaScript example: remove one inline property
// Remove only the inline "color" property:
const el = document.querySelector('.target');
el.style.removeProperty('color');
JavaScript example: remove the entire style attribute
// Remove the whole style attribute (all inline styles):
const el = document.querySelector('.target');
el.removeAttribute('style');
JavaScript example: remove an inline !important value
// If a script set: el.style.setProperty('color', 'red', 'important')
const el = document.querySelector('.target');
el.style.removeProperty('color');
If you’re working with a builder/plugin that keeps re-inserting inline styles, you’ll want to fix the source (the builder settings, the plugin option, or the script) rather than playing whack-a-mole.
.target {
color: #0a7a2f;
}
.target {
color: #0a7a2f !important;
}
.target {
color: #0a7a2f !important;
}
#wrap .target {
color: #7a1bb3 !important;
}
*,
::before,
::after {
box-sizing: border-box;
}
#wrap {
font-family: ui-sans-serif, system-ui, sans-serif;
padding: 16px;
border: 2px solid #111;
border-radius: 14px;
display: grid;
gap: 10px;
}
.target {
padding: 12px 14px;
border: 2px solid #111;
border-radius: 12px;
background: #f2f2f2;
}
.small {
font-size: 14px;
opacity: 0.85;
}
I have an inlinecolorwith!important.Switch to the HTML view to see the inline styles. CSS can struggle here. In real projects, removing the inline style with JavaScript (or fixing the source) is the practical solution.
Practical patterns and what to do instead
Pattern 1: Use utility overrides intentionally
-
Good:
.is-hidden { display: none !important; } -
Good:
.no-scroll { overflow: hidden !important; } -
Good:
.sr-onlypatterns for accessibility (often use!importantfor reliability)
Pattern 2: Keep specificity low by default
If you avoid overly specific selectors like #page .sidebar ul li a, you’ll need fewer overrides later.
Prefer class-based, reusable selectors like .nav-link.
Pattern 3: Fix the source of inline styles
Inline styles are often produced by:
- A page builder setting
- A third-party component script
-
Your own JavaScript doing
element.style...
When possible, move those values into a class and toggle the class instead of writing inline styles.
Avoid “!important wars”
If you find yourself stacking !important everywhere, it’s usually a sign of:
- Overly specific selectors earlier in the codebase
- CSS rules that are doing too much (components not isolated)
- Styles applied inline by tools/scripts
The healthiest long-term fix is often: refactor toward clearer layers (base → components → utilities), and keep your selectors simple and predictable.
Quick reference cheat sheet
-
!importantaffects a declaration, not selector specificity. -
To override a
!importantrule in CSS, you usually need your own!important. -
If both are
!important, specificity wins. -
Inline styles are very strong; inline +
!importantis best handled by removing/changing it (often with JavaScript). -
If
!important“doesn’t work”, check: selector match, element target, competing!important, inline styles, inheritance.
