There’s a moment that happens on almost every site with more than a handful of plugins or a page builder involved. You add a CSS rule. You save it. You refresh. Nothing changes. Someone, somewhere, tells you it’s a “specificity conflict.” They send you to a calculator. You paste your selector in, you get a score like (0,1,1), and you stare at it. And you still don’t know what’s winning against you, because the number you just calculated is only your number. You have no idea what the number is for the rule that beat you.
That’s where the calculator stops being useful, and where the real problem begins.
What CSS specificity actually is
When two CSS rules target the same element and try to set the same property, the browser has to pick one. It doesn’t pick randomly, and it doesn’t always pick the one that came last. It picks the one with the higher specificity score.
Specificity is a scoring system. Every CSS selector carries a score, expressed as three numbers: one for ID selectors, one for class and attribute selectors, one for element selectors. A rule targeting #main .button scores (1,1,0). A rule targeting just .button scores (0,1,0). The first one wins, regardless of which one comes later in the stylesheet.
That’s the mechanism. Understanding it on paper is easy. The trouble is that most articles stop there, and leave you to apply it on a live site where you didn’t write most of the CSS and can’t see the rules competing against you.
Why the calculator doesn’t fix the problem
CSS specificity calculators, including the popular keegan.st, work like this: you paste a selector in, and you get its score. That’s a genuinely useful tool if you’re comparing two selectors you already know about.
But the person searching “css specificity calculator” is usually in a different situation. They changed a button color. It stayed wrong. They added !important. Sometimes it worked, sometimes it didn’t. Someone told them it’s a specificity conflict. They found the calculator.
Here’s the gap: the calculator can only score the selector you give it. It can’t tell you what selector is already winning on your live page, where it came from, or what score it carries. If you’re working on a WordPress site with BetterDocs, Elementor, a theme, a child theme, and three other plugins, there are dozens of stylesheets loading in some order, and any one of them might have written a rule that outranks yours. The calculator scores your rule. It doesn’t show you the competition.
I ran into this exact situation while customizing the BetterDocs sidebar on the Loupely knowledge base. I was trying to change the background color of the active category link, the one that highlighted the article currently being read. I tried a CSS fix. Then another. Then added !important, which I always reach for when a fix isn’t landing, because sometimes it gets the job done and sometimes it just doesn’t, and it’s frustrating when it doesn’t. At one point my styles applied for exactly the moment it took the page to finish loading, and then they were gone. I had that sinking feeling in my stomach when I watched my fix appear and then disappear. The rule I was fighting wasn’t visible to me anywhere. BetterDocs was injecting styles at runtime, after my stylesheet had already loaded, and winning every time.
That’s not a problem a specificity calculator can surface. You’d need to know BetterDocs was doing that to even know what selector to paste in.
What most articles get wrong about this
Articles about CSS specificity tend to explain it in a clean environment: two rules, both visible, paste them in, compare the scores, the higher one wins. The framing is that specificity is a math problem with a clear answer.
That framing works when you’re writing CSS on a blank page. It doesn’t work when you’re trying to override styles on a site that’s running a page builder, a BetterDocs installation, a Shopify theme, a Webflow global stylesheet, or anything else that generates CSS you didn’t write and can’t easily read. On those sites, the real difficulty isn’t calculating your selector’s score. It’s finding out what the winning rule is and where it came from, so you have anything meaningful to compare against.
Most specificity articles don’t account for plugins that inject styles after the page loads. They don’t account for theme stylesheets with highly specific selectors targeting the exact element you’re trying to change. They don’t account for inline styles written directly onto elements by a page builder, which carry a specificity score of (1,0,0,0) that beats anything in a stylesheet. They explain the failure in isolation. The person reading has a site with twelve plugins and a page builder competing against their stylesheet, and the forum answer is for someone else’s site entirely.
How to calculate CSS specificity
If you want to manually score a selector, here’s how it works.
Count three things, in this order:
ID selectors (the ones starting with #): each one adds 1 to the first number. A rule like #sidebar a has an ID count of 1.
Class selectors, attribute selectors, and pseudo-classes (the ones starting with ., or written in brackets like [type="text"], or like :hover): each adds 1 to the second number. A rule like .menu-item.active has a class count of 2.
Element selectors and pseudo-elements (tag names like a, div, p, or things like ::before): each adds 1 to the third number.
A score of (1,0,0) beats (0,99,99) every time, because the first column always has more weight than the second, and the second has more weight than the third. If you’re scoring two rules and the first columns are tied, move to the second column to break the tie, then the third.
Inline styles, written directly on an element with a style="" attribute, sit above all of this. And !important sits above inline styles, except when two !important rules compete, at which point specificity comes back into play.
That’s the full picture. The reason the scoring alone doesn’t solve your problem is what’s above: you can score your rule perfectly and still not know what’s beating it if the winning rule came from somewhere you haven’t looked.
Why is my CSS style being overridden?
If your CSS change isn’t applying, it’s almost always one of these things:
Another rule has a higher specificity score. It could be in your theme’s stylesheet, a plugin’s stylesheet, or written inline on the element by a page builder. The only way to know is to find the rule that’s actually winning, which means looking at what the browser is rendering rather than what you wrote.
A plugin is injecting styles after the page loads. BetterDocs does this. Several page builders do this. If a stylesheet loads after yours via JavaScript, it can win by position even if its specificity score is lower, because rules at the same specificity level, the later one wins. And an !important injected after page load beats your !important in a static stylesheet, because the injected one lands last.
You’re targeting the wrong selector. The element on the live page might have a different class than what you think. Or a parent element might be the one controlling the style you’re trying to change, and your selector doesn’t reach it.
The style is being set by inline CSS. If the page builder or plugin writes a style="" attribute directly on the element, your stylesheet rule loses, period, unless you’re using !important in your rule.
If you’ve tried all of this and still can’t find what’s winning, the issue is visibility: you can’t see the full cascade behind the element from the outside. At that point, Loupely Lens can help. You click the broken element on any website in Chrome, Lens reads the full CSS cascade behind it, and tells you which rule is winning and where it came from, in real human terms. That’s the part the calculator can’t do: it shows you the competition, not just your own score.
What to actually do when your CSS isn’t applying
Open your browser’s developer tools. Right-click the element that’s wrong and click “Inspect.” In the Styles panel on the right, look for rules with a strikethrough through them. That strikethrough means the browser calculated that rule and it lost. The rule at the top of the list, without a strikethrough, is the one that won.
Look at where the winning rule came from. DevTools shows you the source: which stylesheet, which file name, which line. If it says something like betterdocs-pro.css or elementor-global.css or style.min.css from a plugin, now you know. That’s the rule you need to beat, and now you can score it and write something that outranks it, or add !important to your rule and at least understand why that might or might not work.
If the winning rule shows as a long, highly specific selector like body #wrapper .sidebar-widget ul li a.current, you know what you’re up against. That rule scores (0,1,3) or similar, and your .current-link rule at (0,1,0) was never going to beat it. Now you can write a rule that matches or exceeds that specificity, or you can use !important knowing what you’re overriding.
If DevTools shows an inline style on the element (the Styles panel will show it as element.style {} at the very top), that’s coming from a page builder or plugin writing directly onto the element. A stylesheet rule alone won’t override it. You need !important in your rule, or you need to remove the inline style from the source.
The core diagnostic insight is this: the browser already knows which rule is winning. It’s showing you in DevTools, with the strikethroughs, in the order it evaluated everything. The calculator helps you understand the scoring system. DevTools shows you the outcome of the competition on your actual site, with your actual plugins and stylesheets loaded.
Once you can see what’s winning, you can finally write something that beats it. Or at least understand why nothing you’ve tried has worked, and know what the right move actually is from here.
If you’ve already been through the DevTools process and still can’t find the source of the winning rule, check the CSS override not working post for the full diagnostic sequence, including how to handle rules injected by JavaScript and inline styles from page builders.