Adding !important can make a stubborn declaration win immediately while leaving the original conflict unexplained. A better debugging session identifies the element, the property and the competing declarations before changing their priority. The CSS specificity calculator helps with one part of that investigation: comparing selector tuples. This walkthrough shows where that comparison is decisive and where the browser needs a different explanation.
Construct a conflict you can explain by hand
Use a button inside a container with an id of app. Give the button both button and primary classes. In a small stylesheet, compare these selectors for the same color property:
.button.primary { color: green; }
#app .button { color: purple; }
The first selector has two class contributions, giving (0, 2, 0). The second has one id and one class, giving (1, 1, 0). When both declarations are applicable and reach the same relevant cascade priority, the second wins. Compare the id column first, then the class column, then the type column. Do not add the columns into one ordinary decimal score.
Paste the selectors on separate lines in the calculator and compare its output with the hand result. An id contribution is not defeated by accumulating a convenient number of classes. Keep the tuple visible in your debugging notes so the reason for the winner remains clear after someone reformats or reorders the stylesheet.
Confirm that the selector actually matches
A high-specificity selector that matches no element has no influence on that element's color. Inspect the real DOM, including component wrappers and state classes. A selector written for a descendant does not necessarily match a sibling. A disabled state, attribute value or pseudo-class may also change whether a rule applies at the time of the screenshot.
Use browser developer tools to find the selected element and the computed property. Read the matched-rule list before editing a stylesheet. Check whether the declaration was parsed successfully and whether another rule overrides it. This distinguishes a matching problem from a priority problem. The local calculator analyzes selector text; it does not have your document, active states or browser's matched-rule list.
Handle functional pseudo-classes deliberately
Some functional pseudo-classes carry the weight of their arguments rather than a single ordinary class contribution. For example, :is(.notice, #urgent) uses the most specific argument. Adding .button yields (1, 1, 0), even when the element matches the class alternative rather than the id alternative. Read the entire selector list inside the function.
By contrast, :where(#app) .button has (0, 1, 0), because :where and its arguments contribute zero. This can help write defaults that remain easy to override. The distinction is useful in a component library: two selectors that appear similarly structured can intentionally have different override behavior. Do not simplify one into the other without reviewing that design decision.
The :not and :has functions also require argument-aware calculation. Positional selectors such as :nth-child have their own contribution and can add the specificity of an of-selector list. Use the calculator for supported syntax, then confirm the actual selector in your target browser. An unfamiliar selector is a reason to read its rules rather than guess from its length.
Check the decisions that happen before specificity
Specificity does not compare every declaration across the entire cascade without context. Origin, importance and cascade layers can decide priority before selector weights matter. A simple selector in a higher-priority applicable layer can beat a more elaborate selector elsewhere. Important declarations also have different layer ordering from normal declarations, so avoid extending an ordinary-layer intuition blindly.
For a controlled demonstration, place a high-specificity normal rule inside a named layer and a lower-specificity normal rule outside layers. With the same author origin and no other competing mechanism, the unlayered normal rule takes priority. A calculator can correctly report the tuples while appearing to predict the wrong color if you omit that context. Record layer placement alongside selector text.
Use source order only for a genuine tie
If the relevant priority and specificity are equal, source order can resolve a straightforward tie. Move the two equal-weight rules in a reduced example and confirm that the later declaration wins. In a real application, verify the delivered stylesheet order, including imported files and injected component styles. The order in a source folder is not necessarily the order the browser receives.
Do not keep adding selector fragments until the result changes. That creates harder future overrides and may conceal a misplaced stylesheet or unintended layer. Prefer fixing the relevant ownership rule: a component default, a documented variant or a deliberately scoped page override. Keep the smallest selector that expresses that responsibility.
Close the bug with an observable check
After changing the rule, inspect the computed property again in each relevant state. Check the default button, its primary variant and a neighboring component that should remain unchanged. Repeat at a viewport where layout or state changes could select another rule. Save the minimal reproduction with the expected computed value.
The finished explanation should identify why the original declaration lost and why the revised rule wins. Sometimes that explanation is a tuple comparison. Sometimes it is a missing match, layer priority or stylesheet order. Understanding which decision mattered produces a smaller repair than escalating every stubborn declaration with !important.



