When CSS “doesn’t work,” the browser is usually doing exactly what the cascade tells it to do. The confusing part is identifying which declaration wins and why. Specificity is one part of that decision, alongside origin, importance, cascade layers, scoping proximity and source order.
If you want to check a selector while reading, open the free CSS Specificity Calculator.
What CSS specificity means
Specificity is the weight the browser assigns to a selector when competing declarations are otherwise at the same cascade level. A practical way to read a selector is as three columns: ID selectors; classes, attributes and pseudo-classes; and type selectors plus pseudo-elements.
For example, .button has one class-like component, so its score is 0-1-0. .header .button is 0-2-0. #checkout .button is 1-1-0. Because the ID column is compared before the class column, 1-1-0 outranks 0-20-0 despite the second selector containing many classes.
Specificity is not the entire cascade
A common mistake is to treat specificity as the only reason one declaration wins. Before comparing selector scores, the browser considers whether declarations are important, where they came from, and whether cascade layers apply. Source order can decide the winner when competing declarations have equal specificity.
This explains why blindly adding another class sometimes changes nothing. It also explains why !important can appear to “fix” a problem while creating a harder maintenance problem later.
How to calculate common selectors
Classes and elements
.card h2 has one class and one element: 0-1-1. .card .title has two classes: 0-2-0. The second selector wins a normal specificity comparison because the class column is higher.
IDs
#site .card h2 is 1-1-1. IDs are powerful because the ID column is compared first. This is why styling components with IDs can make later overrides unnecessarily difficult.
Attributes and pseudo-classes
Attribute selectors such as [aria-expanded="true"] and pseudo-classes such as :hover, :focus and :nth-child() contribute in the class-like column. A selector such as .menu a:hover therefore has a class, a pseudo-class and an element.
Pseudo-elements
Pseudo-elements such as ::before and ::after contribute like type selectors. They are not counted like pseudo-classes.
The special case of :where()
:where() is intentionally useful for writing easy-to-override CSS because the pseudo-class and its arguments contribute zero specificity. A theme or component library can use :where(.content a) to provide a default without forcing downstream developers into a specificity battle.
:is(), :not() and :has()
Functional pseudo-classes need more care than a simple character count. Their arguments can affect the selector’s specificity. When you are debugging modern selectors, inspect the most specific argument rather than assuming the functional pseudo-class behaves like an ordinary class.
What about inline styles?
Inline declarations are handled specially in the cascade and generally beat normal declarations in author stylesheets. This matters in WordPress because page builders and plugins sometimes write values directly into a style attribute. Before increasing selector specificity, inspect the element in DevTools and see whether an inline declaration is involved.
Where !important fits
!important does not simply add points to a specificity score. Important declarations are compared in a different cascade step. Among competing important declarations, specificity can still matter. Use !important intentionally rather than as a default response to every crossed-out rule.
A reliable debugging workflow
- Inspect the element in browser DevTools.
- Find the property that is crossed out and the declaration that currently wins.
- Check whether either declaration is important or inline.
- Compare the selectors with the CSS Specificity Calculator.
- If specificity ties, check source order and cascade layers.
- Change the smallest thing necessary: selector scope, component structure, source order or architecture.
Specificity in WordPress themes
WordPress sites combine core styles, theme styles, block styles, plugin styles and custom CSS. A theme may target a component through several nested classes while your custom rule uses only one. That does not mean the theme is broken; it means your rule is competing with a more specific selector or another cascade factor.
Prefer adding a meaningful component class rather than copying an enormous generated selector from DevTools. Generated selectors are brittle and may change after an update.
Specificity in Elementor
Elementor adds wrapper and widget classes around content. Custom CSS can lose because a global style, theme rule or widget rule is more specific, but cache can also make an old generated stylesheet appear to remain active. If the correct rule is present but crossed out, investigate the cascade. If your new rule is absent entirely, investigate CSS generation and caching instead.
For responsive values, the CSS Clamp Generator can help you avoid unnecessary breakpoint overrides.
How to keep specificity low
Use classes as your primary styling hooks. Keep selectors shallow. Avoid IDs for reusable component styling. Use semantic component naming. Consider :where() for defaults designed to be overridden. Do not nest selectors merely because your preprocessor or editor makes nesting convenient.
FAQ
Does a longer selector always win?
No. The categories matter more than raw length. One ID can outrank a selector containing many classes.
Does source order beat specificity?
Source order is normally used when declarations are otherwise tied at the relevant cascade level. A later low-specificity selector does not automatically beat an earlier higher-specificity selector.
Should I remove every !important?
No. Important declarations have legitimate uses, especially in carefully designed utility or override systems. The problem is using them reflexively without understanding why the original declaration wins.
Can specificity explain every CSS problem?
No. Invalid syntax, caching, media queries, inheritance, unsupported properties, cascade layers and inline styles can all produce similar symptoms.
Final takeaway
Treat specificity as a diagnostic tool, not a contest to create the biggest selector. Inspect the actual winning declaration, understand the cascade level, compare selector weights, then make the smallest maintainable change.

