You write a perfectly reasonable rule, .button { color: blue; }, save the file, refresh the page, and the button is still red. You didn't misspell anything. The selector matches the element. The CSS file is definitely loading, since other rules in it work fine. So why is this one rule being ignored?
Almost every time this happens, the answer is specificity: some other rule targeting the same element is simply considered "more important" by the browser, according to a fairly mechanical set of rules that most people never actually learn, they just guess and add more classes until something works.
Welcome to BytebaseX. I'm Suptojit Modak, and in this guide, I'll break down exactly how specificity is calculated, why !important causes more problems than it solves, and a couple of habits that will get you out of the "just add another class" cycle for good.
What Specificity Actually Is
When two or more CSS rules target the same element and set the same property to different values, the browser needs a way to decide which one wins. Specificity is that scoring system. Every selector gets a specificity score, and when rules conflict, the rule with the higher score applies, regardless of which one appears later in the file.
That last part is the piece people miss most often: specificity beats source order. A rule at the very top of your stylesheet can still override a rule at the very bottom, if its selector has higher specificity.
How the Score Is Actually Calculated
Specificity is usually written as four numbers, often shown as (a, b, c, d), though the first is rarely used outside inline styles. Here's what feeds into each column:
| Column | What counts | Example |
|---|---|---|
| a | Inline styles (written directly in the HTML style attribute) | style="color: red;" |
| b | IDs | #header |
| c | Classes, attribute selectors, and pseudo-classes | .button, [type="text"], :hover |
| d | Elements and pseudo-elements | div, p, ::before |
To compare two selectors, you compare these columns left to right, like comparing version numbers. One ID always beats any number of classes. One class always beats any number of element selectors. It doesn't matter how many low-value selectors you stack up; they never add up to outrank a single selector one column to the left.
Let's calculate a few real selectors:
.button → (0, 0, 1, 0)
.card .button → (0, 0, 2, 0)
#submit-btn → (0, 1, 0, 0)
#submit-btn.button → (0, 1, 1, 0)
div.card .button:hover → (0, 0, 3, 1)
Look at the second-to-last line. A single ID selector, on its own, already beats .card .button, which is two chained classes. This is exactly why styling elements by ID is discouraged in most modern CSS workflows: an ID's specificity is so high that overriding it later, from anywhere else in your stylesheet, becomes genuinely annoying.
A Live Specificity Comparison
Rather than mentally comparing tuples, try it directly. Click each selector to see which one would actually win against the others.
This paragraph's color depends on which selector wins.
Click a selector above. Selector: div p, .text, and #intro all target this same paragraph.
What About !important?
!important isn't part of the specificity scoring system at all, it's a separate override that sits above everything else. A declaration marked !important wins against any normal rule, regardless of that rule's specificity score:
.button {
color: blue !important;
}
This will override even an inline style, which normally has the highest specificity of all. It sounds like a convenient escape hatch, and the first time it fixes a stubborn styling problem, it feels like a superpower. But it creates a real long-term problem:
!important, overriding that rule later requires either another !important with higher specificity than the first, or editing the original rule directly. Stylesheets that lean on !important heavily tend to spiral, since every new override needs to out-muscle the last one, and eventually nobody can predict what will actually render without opening DevTools.There's a legitimate, narrow use case: overriding styles from a third-party library or framework you don't control and can't edit directly. Outside of that, if you find yourself reaching for !important in your own stylesheet, it's usually a sign the actual selector specificity needs fixing instead.
Specificity vs Source Order: The Distinction People Mix Up
These are two separate tie-breaking systems, and confusing them is where a lot of "but I put my rule after the other one" confusion comes from.
- Specificity is compared first. Higher specificity always wins, no matter where the rule sits in the file.
- Source order only matters as a tiebreaker, when two selectors have the exact same specificity score. In that case, whichever rule appears later in the CSS (or later in a
<link>loading order) wins.
.button { color: blue; }
.button { color: green; } /* This wins: identical specificity, appears later */
#header .button { color: blue; }
.button { color: green; } /* This loses: lower specificity, doesn't matter that it's later */
If you've ever moved a CSS rule to the very bottom of your file assuming "last one wins" and it still didn't apply, this is why. Source order only breaks ties between equally specific selectors; it never overrides a genuinely more specific one.
Debugging With DevTools Instead of Guessing
Rather than adding classes and refreshing repeatedly, open your browser's DevTools, right-click the element, and inspect it. The Styles panel shows every rule targeting that element, with any rule that's being overridden shown with a strikethrough. This tells you immediately which selector is winning and, just as usefully, exactly which ones are losing and why.
Most modern browsers will even show you the calculated specificity of a selector directly when you hover over it in this panel, which turns a guessing game into a two-second lookup.
How This Shows Up in Real Layout Bugs
Specificity issues aren't just an abstract color-not-applying example. They're behind a genuinely common real-world frustration: writing a centering rule that should work and watching it silently do nothing, because a more specific selector elsewhere in the stylesheet is quietly overriding it. If you've run into exactly that with margin: 0 auto or flexbox centering not taking effect, Why Centering Div Confuses Everyone covers the centering side of things in depth, and this specificity issue is very often the hidden reason behind a case that looks like a centering bug but isn't one.
Common Mistakes That Create Specificity Problems
Styling by ID
IDs are meant for JavaScript hooks and anchor links, not styling. An ID's specificity is high enough that overriding it later requires either another ID selector or !important, both of which make your stylesheet harder to maintain. Use classes for styling, and save IDs for the things only IDs are actually needed for.
Deeply Nested Selectors
Selectors like .page .sidebar .widget .title accumulate specificity with every level, even though each individual piece is "just a class." The deeper the nesting, the harder that rule becomes to override anywhere else, and the more likely you are to reach for !important later just to fight your own earlier CSS.
Fighting Specificity Instead of Fixing the Selector
The instinct when a style doesn't apply is often to add another class or another level of nesting to "win." This works, but it escalates the specificity of that part of your CSS permanently, making the next override even harder. It's usually better to lower the specificity of the rule that's incorrectly winning, rather than raising the specificity of the one that should win.
A Modern Escape Hatch: CSS Layers
If you're working with a codebase that already has real specificity conflicts baked in, particularly when mixing your own CSS with a third-party framework, @layer offers a cleaner way out than !important ever did:
@layer framework, components, overrides;
@layer framework {
.button { color: gray; }
}
@layer overrides {
.button { color: blue; }
}
Rules inside a later-declared layer win over rules in an earlier layer, regardless of the actual specificity of the selectors inside each layer. This lets you say "my own styles should always beat the framework's styles" as a structural rule, rather than fighting individual selector specificity every time a conflict comes up. Browser support is solid across all current major browsers as of 2026, so this is safe to reach for in new projects.
Quick Reference
| Selector type | Specificity weight |
|---|---|
| Inline style attribute | Highest (beats everything except !important) |
ID selector (#id) | High |
Class, attribute, pseudo-class (.class, [attr], :hover) | Medium |
Element, pseudo-element (div, ::before) | Lowest |
!important | Overrides all of the above (use sparingly) |
Frequently Asked Questions
Does the order of CSS files being loaded affect specificity?
No, load order doesn't change a selector's specificity score. It only matters as a tiebreaker when two selectors have identical specificity, in which case whichever file or rule loads later wins. A more specific selector in an earlier-loaded file will still beat a less specific one in a later-loaded file.
Do universal selectors and combinators like * and > add any specificity?
No. The universal selector (*), combinators (>, +, ~, and the space for descendant selectors), and the :where() pseudo-class all add zero specificity. They affect what a selector matches, but not how strongly it wins a conflict.
Is it ever okay to use an ID selector for styling?
It's not forbidden, and it works fine for one-off elements you genuinely never intend to restyle from elsewhere, like a single unique page section. The concern is less about correctness and more about future maintenance: once styled by ID, overriding that rule later gets noticeably harder than overriding a class would.
Why does my inline style still get overridden even though inline styles have the highest specificity?
The only thing that beats an inline style is a rule marked !important somewhere in your CSS. If an inline style isn't applying, check for an !important declaration targeting the same property; that's almost always the cause.
What's the fastest way to find out why a specific style isn't applying?
Open DevTools, right-click the element, and choose Inspect. The Styles panel lists every rule targeting that element, with overridden rules shown crossed out, so you can see exactly which selector is winning without manually calculating specificity yourself.
Conclusion
Most "my CSS isn't working" moments aren't actually mysteries, they're specificity conflicts with a mechanical, learnable answer. Learn the four-column scoring system, understand that specificity always beats source order, and use DevTools to check rather than guess, and the "just add another class and hope" cycle mostly disappears.
If you take one habit away from this: before reaching for !important or piling on another class, open DevTools first and actually look at what's winning. It takes ten seconds and almost always tells you exactly what to fix.
For how specificity fits alongside the box model, positioning, Flexbox, Grid, and centering as one connected system, see The Complete Guide to CSS Layout.