You write your CSS.
You refresh the browser.
Nothing happens.
You check your selector.
It looks correct.
You check the property.
It looks correct too.
So you try another rule.
Still nothing.
Then comes the thought every CSS beginner eventually has:
Maybe I should just add
!important. 😅
Sometimes that appears to solve the problem.
But if you regularly need !important just to make your CSS work, there is probably something more important you need to understand first:
CSS specificity.
Specificity is one of the concepts that explains why one CSS rule can override another when multiple rules target the same element.
Once you understand it, CSS debugging becomes much less mysterious.
In this article, we'll explore:
- What CSS specificity means
- How element selectors, classes, and IDs compare
- Where inline styles fit in
- What
!importantactually does - Why beginners shouldn't use
!importantas a first solution - How the cascade affects your CSS
- How to debug CSS rules systematically
Let's begin.
What Is CSS Specificity?
Imagine you have a paragraph:
Hello, world!
Now suppose you write three different CSS rules:
p {
color: blue;
}
.text {
color: green;
}
intro {
color: red;
}
At first glance, you might wonder:
Which colour should the paragraph have?
The browser has to make a decision.
If multiple declarations apply to the same element and property, CSS uses several parts of its decision-making process—including specificity—to determine which declaration wins.
Specificity is essentially a way of measuring how specifically a selector targets an element.
A very simplified hierarchy beginners can start with is:
Element selector < Class selector < ID selector
Let's look at each one.
- Element Selectors
An element selector targets HTML elements by their tag name.
For example:
p {
color: blue;
}
This targets paragraph elements.
You could also write:
h1 {
color: purple;
}
or:
button {
background-color: black;
}
Element selectors are relatively low in specificity because they can potentially match many elements.
For example:
p {
color: blue;
}
could affect every paragraph on the page.
That makes element selectors useful for general styling.
But they can easily be overridden by more specific selectors.
- Class Selectors
A class selector targets elements with a particular class.
HTML:
Important information
CSS:
.highlight {
color: green;
}
The . tells CSS that highlight is a class selector.
Classes are more specific than ordinary element selectors.
For example:
p {
color: blue;
}
.highlight {
color: green;
}
If the paragraph has the highlight class:
Important information
the text will be green.
Why?
Because the class selector is more specific than the element selector.
This makes classes extremely useful for reusable styles.
- ID Selectors
An ID selector targets an element using its id.
For example:
Welcome to my website.
CSS:
intro {
color: red;
}
The # indicates an ID selector.
An ID selector has greater specificity than a class selector.
Consider:
p {
color: blue;
}
.text {
color: green;
}
intro {
color: red;
}
And:
Hello!
All three selectors target the same paragraph.
But the ID selector has greater specificity.
So the text becomes:
Red.
This is why specificity matters.
A Simple Specificity Example
Let's put the three selectors side by side:
p {
color: blue;
}
.text {
color: green;
}
intro {
color: red;
}
HTML:
Hello, world!
The browser sees:
- p → element selector
- .text → class selector
- #intro → ID selector
The ID selector wins.
So:
Hello, world!
appears in red.
A useful beginner-friendly way to remember the basic hierarchy is:
Element → Class → ID
As you become more advanced, you'll learn that specificity is actually calculated using specificity components rather than a simple one-level ladder.
- What About Multiple Classes?
You can also have multiple classes in a selector.
For example:
.text {
color: green;
}
.text.highlight {
color: orange;
}
HTML:
Hello!
Both selectors can match the paragraph.
But:
.text.highlight
contains two class selectors, making it more specific than:
.text
Therefore, the text becomes orange.
This demonstrates an important idea:
Specificity depends on the selector as a whole, not simply on the type of selector you notice first.
- What About Inline Styles?
Now let's add an inline style.
Hello, world!
The style is written directly inside the HTML element.
This is called an inline style.
Compared with ordinary author stylesheet rules, inline styles generally have higher precedence in the cascade.
For example:
.text {
color: green;
}
but:
Hello, world!
The inline declaration will normally win over the class rule.
The result is:
Purple.
However, don't interpret this as:
Inline styles are always the best way to style elements.
They aren't.
Using large amounts of inline styling can make your code harder to maintain because your presentation becomes scattered throughout your HTML.
For larger projects, reusable CSS classes are generally easier to manage.
- Then There's
!important
Now we arrive at the famous CSS escape hatch:
!important
For example:
p {
color: red !important;
}
The !important annotation changes how the declaration participates in the cascade and can override normal declarations.
This is why beginners often discover it and think:
I've found the solution to every CSS problem! 😂
But that's where things can go wrong.
Why Beginners Shouldn't Immediately Use !important
Imagine your CSS contains:
p {
color: blue !important;
}
Later, you want a particular paragraph to be green:
.special {
color: green;
}
But the green rule doesn't work.
So you write:
.special {
color: green !important;
}
Now you have created another !important rule.
Later, another component needs a different colour.
You add another override.
Then another.
Eventually your stylesheet becomes a maze of declarations that are difficult to reason about.
Instead of asking:
Why isn't my CSS working?
you're constantly asking:
What do I need to override this override?
That's not a good development workflow.
!important Isn't Evil
It's important to be clear about this.
!important is not inherently bad.
There are legitimate situations where it can be useful.
For example, you may encounter third-party styles that you genuinely need to override, or certain utility and legacy-code situations where it has a practical role.
The problem is using it automatically whenever something doesn't work.
Think of it this way:
!important is a tool, not a debugging strategy.
If you don't understand why the original rule isn't winning, adding !important may only hide the underlying issue.
Specificity Isn't the Whole Story
Here's another important point.
Beginners sometimes learn:
The most specific selector always wins.
That's an oversimplification.
CSS is governed by the cascade , which considers more than just selector specificity.
When multiple declarations compete, factors such as:
- Origin and importance
- Cascade layers
- Specificity
- Scope
- Source order
can affect which declaration wins.
For everyday beginner CSS, specificity is an excellent concept to learn first.
But as you progress, understanding the cascade will explain many situations that simple specificity rules cannot.
What Is the Cascade?
The word cascade refers to how CSS resolves competing declarations.
Imagine two rules target the same element:
p {
color: blue;
}
p {
color: green;
}
Both selectors have the same specificity.
So which colour wins?
In this simple case, the later declaration wins.
The paragraph becomes green.
This is where source order becomes important.
If two declarations have the same relevant cascade conditions and specificity, the one that comes later can win.
So CSS isn't simply asking:
Which rule appears first?
or:
Which selector looks bigger?
It's evaluating the rules according to the cascade.
Specificity and Source Order Together
Consider this:
.text {
color: green;
}
.text {
color: orange;
}
Both selectors have the same specificity.
Therefore, the later declaration wins.
The text becomes orange.
But now change the second selector:
.text {
color: green;
}
intro {
color: orange;
}
If the element has:
Hello!
the ID selector has greater specificity, so orange wins.
This is why understanding both specificity and source order is useful when debugging CSS.
How to Debug a CSS Rule That Isn't Working
Instead of immediately adding !important, follow a process.
Step 1: Check the Selector
Ask:
Am I actually targeting the element I think I am?
For example:
.button {
background: blue;
}
But your HTML says:
Click me
The CSS isn't working because .button doesn't match .btn.
This isn't a specificity problem.
It's a selector problem.
Step 2: Check the Property and Value
Make sure the property exists and the value is valid.
For example:
p {
colr: red;
}
There is a typo.
It should be:
p {
color: red;
}
No amount of specificity will fix an invalid property name.
Step 3: Check Whether Another Rule Is Winning
Use your browser's developer tools.
Inspect the element and look at the Styles panel.
Browsers can show which CSS declarations are being applied and which ones are being overridden.
This is one of the most useful habits you can develop as a frontend developer.
Instead of guessing:
Maybe CSS is broken.
you can inspect the element and see:
This rule is being overridden by that selector.
That's much faster.
Step 4: Compare Specificity
Suppose you see:
p {
color: blue;
}
.card p {
color: green;
}
The second selector is more specific because it contains both a class selector and an element selector.
If your paragraph is inside an element with the card class, the green declaration can win over the general p rule.
Instead of adding:
p {
color: blue !important;
}
ask whether the selector you're using is appropriate for the design.
Step 5: Check for Inline Styles
Look at the HTML.
Is there something like:
If so, your external stylesheet may be competing with an inline declaration. Understand why it's there before deciding how to override it. Step 6: Check Inheritance Some CSS properties are inherited from parent elements. For example: body { color: #333; } A paragraph inside the body may inherit that colour unless it has its own applicable declaration. Understanding inheritance can explain why an element appears styled even when you didn't directly style that element. Step 7: Check Whether the Stylesheet Is Loading Sometimes the problem has nothing to do with specificity. Your stylesheet might not be connected correctly. For example: If the path is incorrect, the browser can't apply the intended stylesheet. Before debugging complex specificity problems, make sure your CSS is actually reaching the browser. A Useful Example: The Specificity Trap Consider this CSS: p { color: blue; } .card p { color: green; } #special { color: red; } And this HTML:
Hello!
All three selectors match the paragraph.
Which one wins?
The ID selector:
special {
color: red;
}
The paragraph appears red.
Now imagine a beginner wants the paragraph to be blue and writes:
p {
color: blue !important;
}
Yes, that may change the result.
But the better question is:
Why does the paragraph need to override the ID rule in the first place?
Understanding the design and selector structure is usually better than throwing !important at the problem.
A Better CSS Mindset
Instead of thinking:
How can I force this rule to work?
start thinking:
Why isn't this rule winning?
That small change in mindset can dramatically improve your CSS debugging skills.
When something isn't working, investigate:
Selector → Property → Cascade → Specificity → Source order → Inheritance → Stylesheet loading
This turns CSS debugging into a process rather than guesswork.
Should You Avoid IDs in CSS?
Not necessarily.
IDs are valid CSS selectors and can be useful in certain situations.
However, many modern CSS codebases rely heavily on classes because classes are reusable and tend to make styles easier to manage.
For example:
.card {
padding: 20px;
}
can be reused across many elements.
An ID is intended to uniquely identify an element within a document.
Understanding specificity doesn't mean:
Never use IDs.
It means understanding the consequences of using a highly specific selector.
Don't Create Specificity Wars
One of the easiest ways to make CSS difficult to maintain is to keep increasing selector specificity.
You might start with:
.button {
color: blue;
}
Then:
.container .button {
color: green;
}
Then:
.page .container .button {
color: red;
}
And eventually:
body .page .container .button {
color: purple;
}
At some point, you're no longer writing CSS that is easy to understand.
You're writing CSS designed to defeat other CSS.
This is sometimes called a specificity war.
Good CSS aims for selectors that are specific enough to express your intention without becoming unnecessarily difficult to override.
What Beginners Should Remember
You don't need to memorize every detail of the CSS cascade on your first day.
Start with these principles:
- Element selectors have relatively low specificity
p {}
- Class selectors are more specific than element selectors
.text {}
- ID selectors are more specific than classes
intro {}
- Inline styles generally outrank normal stylesheet declarations
-
!importantchanges the importance of a declaration
color: red !important;
- Equal-specificity rules can be resolved by source order
The later applicable declaration can win.
- Specificity isn't the entire cascade
As you progress, learn about cascade layers, origins, importance, scope, inheritance, and other parts of CSS's decision-making process.
- Don't use
!importantas your first debugging tool
Find the actual cause first.
Final Thoughts
CSS can feel frustrating when a perfectly reasonable-looking rule refuses to work.
But most of the time, the browser isn't randomly ignoring your code.
There's usually a reason.
Maybe another selector is more specific.
Maybe another declaration comes later.
Maybe an inline style is involved.
Maybe the property is inherited.
Maybe the selector doesn't match.
Maybe the stylesheet isn't loading.
Or perhaps the cascade is resolving competing declarations in a way you haven't learned yet.
That's why understanding CSS specificity and the cascade is so valuable.
You stop seeing CSS as:
I wrote this rule, so the browser should use it.
And start seeing it as:
Several declarations may apply. The browser needs to determine which one wins.
That shift in thinking makes CSS much easier to debug.
And when you're tempted to write:
!important
pause for a moment.
Ask:
Why isn't my rule winning?
Because learning to answer that question is far more valuable than memorizing another CSS trick.
What About You?
Have you ever solved a CSS problem by adding !important—only to discover later that the real problem was specificity, the selector, or the cascade?
Share your experience. 👇

