You write a CSS rule. It does not apply. You add another, more specific. Still nothing. You add !important out of frustration, and the stylesheet becomes a tangled mess nobody wants to touch. The cause is almost always specificity.
CSS specificity is the algorithm browsers use to decide which style declarations get applied when multiple rules target the same element. It is not random. It follows a precise scoring system you can calculate and predict.
Once you understand how it works, debugging CSS gets much easier. You stop fighting the cascade and start working with it. Fewer overrides, fewer !important flags, stylesheets that are easier to maintain.
The Specificity Scoring System
Specificity is calculated as a four-part score, often written as (a, b, c, d). Each part corresponds to a category of selector.
Inline styles (a): styles applied directly in the HTML style attribute. Score: (1, 0, 0, 0). These beat almost everything except !important.
IDs (b): selectors that use #id. Each ID in the selector adds 1 to this column. Score per ID: (0, 1, 0, 0).
Classes, attributes, pseudo-classes (c): selectors using .class, [type="text"], :hover, :nth-child(), etc. Each one adds 1 to this column. Score per item: (0, 0, 1, 0).
Elements and pseudo-elements (d): selectors using tag names like div, p, h1, or pseudo-elements like ::before. Score per item: (0, 0, 0, 1).
Examples:
pscores (0, 0, 0, 1).cardscores (0, 0, 1, 0)#header .nav ascores (0, 1, 1, 1)div.sidebar ul li.active a:hoverscores (0, 0, 3, 4)
The comparison works left to right. A selector with (0, 1, 0, 0) always beats (0, 0, 15, 15), no matter how many classes and elements are stacked up. One ID outweighs any number of classes.
Common Specificity Mistakes and How to Fix Them
The most frequent mistake is over-qualifying selectors. Writing div.container ul.nav li.item a.link when .nav-link would do the same job. The long selector has high specificity, which means overriding it later requires an equally specific (or more specific) selector.
Another common issue is using IDs for styling. IDs are unique per page, which is great for JavaScript hooks and anchor links, but terrible for CSS. A single #sidebar in your stylesheet creates a specificity wall that forces everything targeting sidebar elements to also use the ID.
The !important flag overrides all specificity calculations. It wins regardless of the selector score. But when two rules both have !important, specificity decides between them. This leads to !important wars where developers keep adding the flag to override previous !important declarations.
Better alternatives:
- Use classes for styling, reserve IDs for JavaScript and anchors
- Keep selectors as short and flat as possible
- Follow a naming convention like BEM (Block Element Modifier) to avoid nesting
- Use the cascade intentionally by ordering your stylesheets from generic to specific
- If you must override a third-party library, use a single class with higher specificity rather than
!important
After restructuring your selectors, run your stylesheet through the CSS Minifier to strip whitespace and reduce the file size for production.

The Cascade: When Specificity Is Equal
When two selectors have identical specificity scores, the cascade uses source order as the tiebreaker. The rule that appears later in the stylesheet wins. This is why the order of your CSS files and the order of rules within those files matters.
The full cascade algorithm evaluates rules in this priority:
- Origin and importance (user agent styles, author styles,
!importantauthor styles) - Specificity score
- Source order (later wins)
This means you can structure your stylesheets strategically. Put your base reset and typography styles first. Then component styles. Then utility classes. Then overrides. Each layer naturally has the opportunity to override the previous one because of source order.
CSS layers (@layer) introduced in modern browsers formalize this pattern. You can define named layers and control their priority explicitly:
`css
@layer base, components, utilities;
`
Rules in the utilities layer will override rules in components, which override base, regardless of specificity within those layers. It makes large stylesheets and third-party CSS integration much easier to manage without specificity wars.
Practical Strategies for Managing Specificity
The most maintainable approach is to keep specificity consistently low across your entire project. When every selector is a single class, overriding any rule is straightforward.
The one-class rule: aim for selectors that are a single class name. .card-title instead of .card .title. .nav-link--active instead of .nav li a.active. This keeps every selector at (0, 0, 1, 0) and makes source order the only factor.
Utility-first CSS (Tailwind, etc.): applies styles directly in HTML using utility classes. Specificity is uniformly low because each utility is a single class. Overrides happen by changing the HTML, not by writing competing CSS.
BEM naming: Block__Element--Modifier creates unique class names that avoid nesting. .card__header--highlighted is one class with specificity (0, 0, 1, 0), no matter how deep the component structure goes.
The :where() specificity eraser: the :where() pseudo-class always has zero specificity, regardless of what is inside it. This is incredibly useful for default styles that should be easy to override:
`css
:where(.button) { background: gray; }
.primary { background: blue; }
`
The .primary class easily overrides :where(.button) because :where() contributes zero to specificity.
Visualise the cascade with quick experiments in the CSS Gradient Generator when you need to test how layered styles stack. Clean, simple stylesheets make specificity conflicts easier to spot during review.
The most maintainable approach is to keep specificity consistently low across your entire project.
Debugging Specificity in Browser DevTools
Every modern browser shows you exactly which rules are applied, which are overridden, and why. In Chrome or Firefox DevTools, inspect an element and look at the Styles panel.
Overridden rules appear with a strikethrough. Hover over a rule to see its specificity score (Chrome shows this in newer versions). If a rule you expected to apply is struck through, compare its specificity with the winning rule.
Steps for debugging:
- Right-click the element and select Inspect
- In the Styles panel, find the property that is not working
- Look for the same property in other rules above it (higher specificity) or below it with
!important - Compare the selectors and calculate their specificity
- Adjust your selector to match or exceed the winning specificity, or restructure to avoid the conflict
The Computed tab shows the final computed value for every property and which rule set it. Click the arrow next to any value to jump to the winning rule.
For production, minify your final stylesheet with the HTML Minifier for the HTML and the CSS Minifier for styles. Smaller files load faster and reduce the amount of CSS the browser has to parse.

FAQ
Does the universal selector (*) have any specificity?
No. The universal selector * has zero specificity (0, 0, 0, 0). It matches everything but never beats any other selector. Combinators like >, +, and ~ also have zero specificity. Only the actual selectors (IDs, classes, elements) contribute to the score.
Is specificity different from importance?
Yes. Specificity determines which rule wins among rules of the same importance level. The !important flag elevates a rule to a higher importance level entirely. Within !important rules, specificity still decides the winner. Think of importance as a tier system: normal rules compete within their tier, important rules compete within theirs.
Can I see specificity scores in my IDE?
Some IDEs and extensions show specificity on hover. VS Code with the CSS extension displays specificity when you hover over a selector. Stylelint can also be configured to warn when specificity exceeds a threshold, helping enforce low-specificity conventions across a team.
How does specificity work with CSS-in-JS?
CSS-in-JS libraries like styled-components and Emotion generate unique class names at runtime. Because each class is unique, specificity conflicts between components are rare. The trade-off is that overriding styles from outside the component requires knowing the generated class name or using a wrapper with higher specificity.
### Does the universal selector (*) have any specificity.
HTML Semantic Elements and SEO: A Practical Guide
HTML semantic elements help SEO indirectly through accessibility and content parsing. Learn which tags affect rankings and how to use them correctly.
Responsive Images with srcset and sizes Explained
Use HTML srcset and sizes to ship the right responsive image to every device. Cut page weight, fix Core Web Vitals, and protect LCP on mobile screens.
Mesh Gradients: How to Build One That Does Not Look Like a Screensaver
What a mesh gradient is, how to write one as layered CSS radial-gradients, which generators are worth opening, and the rules that keep text readable on top of it.
CSS Generators: Gradients, Flexbox, Grid and Shadows
Use CSS generators to create gradients, flexbox layouts, grid systems, and box shadows. Visual editors output ready-to-copy CSS for your project.
