Detailed explanation
CSS specificity is the weight used to compare selectors when competing declarations target the same property on the same element. It matters because unexpected styles often come from competing rules, not incorrect CSS syntax. However, specificity is only one stage of the cascade: origin, importance, and cascade-layer precedence are resolved first. A more specific selector therefore does not automatically defeat every other declaration, especially one marked !important.
For interview explanations, specificity is commonly represented as (inline styles, IDs, classes/attributes/pseudo-classes, elements/pseudo-elements). Thus, #nav has (0,1,0,0), .menu.active has (0,0,2,0), and div.menu has (0,0,1,1). Compare these tuples from left to right; the first unequal category decides the winner. Any number of classes still loses to one ID when earlier cascade stages are equal. These are not decimal scores, so you must not add them as ordinary numbers. Strictly, selectors have three specificity columns; inline declarations are handled separately by the cascade, but the four-part model is a useful teaching shorthand.
Count each ID selector in the ID column; each class, attribute selector, or pseudo-class in the next column; and each type selector or pseudo-element in the final column. Universal selectors and combinators such as *, >, +, and spaces add nothing. Important exceptions are :is(), :not(), and :has(): they contribute the specificity of their most specific argument, rather than an extra pseudo-class point. In contrast, :where() and its arguments always contribute zero specificity. For example, :is(#nav, .menu) has (0,1,0,0), even when the element matches only .menu.
For <div id="nav" class="menu active" style="color: green;">, normal author declarations #nav { color: red; } and .menu.active { color: blue; } lose to the inline green declaration. However, an applicable author declaration using !important overrides that normal inline style. If competing declarations remain tied after specificity, scoping proximity can decide the result for scoped rules; otherwise, later source order wins. Interviewers usually expect correct counting, left-to-right comparison, and awareness that specificity operates within the broader cascade.
Key points to remember
- Compare specificity categories left to right, not as an ordinary numeric total.
- IDs outrank classes, attributes, and pseudo-classes; those outrank types and pseudo-elements.
- Universal selectors and combinators add no specificity.
- :is(), :not(), and :has() use their most specific argument; :where() always contributes zero.
- Resolve cascade origin, importance, and layer precedence before comparing specificity.
Common mistakes candidates make
- Saying inline styles always win, without accounting for important declarations and other cascade precedence.
- Treating specificity as a decimal score, as though enough classes could outweigh an ID.
- Assuming the last rule always wins, even when an earlier applicable rule has higher specificity.
Likely follow-up questions
Does !important increase specificity?
No; it changes a declaration's importance in the cascade, not its selector specificity. If competing important declarations have the same origin and layer precedence, specificity can still decide between them.
What happens when two selectors have equal specificity?
Assuming earlier cascade stages are equal, scoping proximity is considered for scoped rules. If that does not distinguish them, the declaration appearing later in source order wins.
How can you avoid specificity problems in a growing stylesheet?
Prefer low-specificity class selectors and avoid unnecessary IDs or deeply qualified selectors. Use cascade layers for deliberate precedence and :where() when you want matching conditions without adding specificity.
Interview tip: Define specificity, compare #nav with .menu.active, and then explain why inline styles and !important require broader cascade context.
