Start with a property on one element

CSS conflicts become easier to understand when you ask a narrow question: “What value does this property have on this element?” A button might receive a color declaration from a button selector, a class selector, and a rule that targets buttons inside a navigation area. The browser does not choose one complete rule and discard the others. It resolves each property separately, so the winning color may come from a different rule than the winning border.

The cascade is the process that chooses among declarations that apply to the same property on the same element. Specificity is one part of that process: it compares how precisely selectors target the element. Inheritance is different. It can supply a value from a parent when the element has no winning declaration of its own for an inheritable property. Keeping these three ideas separate prevents many common debugging mistakes.

Understand the cascade before counting selectors

First, a declaration must apply. A selector has to match the element, and any condition around the rule, such as a media query, has to be satisfied. Among applicable declarations, the browser considers factors including where a style comes from, whether it is marked !important, and whether it belongs to a cascade layer. Only after earlier priorities are settled does selector specificity become decisive. If competing declarations remain tied, later rules can win.

For ordinary rules you write in a simple stylesheet, a practical starting point is this: when two selectors match the same element and set the same property, the more specific selector usually wins. If their specificity is equal, the declaration that appears later wins. For example, given p { color: navy; } followed by p { color: teal; }, the paragraph is teal. Reversing the rule order makes it navy.

There are important limits to that shortcut. A declaration marked !important can outrank an otherwise more specific normal declaration. For normal author styles, a rule outside a cascade layer generally takes precedence over a rule inside one, regardless of selector specificity. Inline styles also have a different position from ordinary stylesheet rules. For those reasons, adding more IDs or classes is not a reliable fix until you know which part of the cascade caused the result.

Compare specificity without guessing

For common selectors, compare specificity in groups: ID selectors first, then classes, attributes, and pseudo-classes, then element selectors and pseudo-elements. Compare the groups from left to right; do not treat them as ordinary decimal numbers. The selector #notice has higher specificity than .notice, while .notice has higher specificity than p. Ten element selectors do not outweigh one class selector.

Consider this HTML: <p class="message" id="notice">Check your settings</p>. If the stylesheet contains p { color: gray; }, .message { color: blue; }, and #notice { color: green; }, all three selectors match, but the ID selector wins for color. Moving the p rule below the others does not change that outcome. If you add a second #notice rule with color: purple later in the same stylesheet, that later rule wins over the earlier #notice rule because their specificity is equal.

Specificity belongs to the selector that matches, not to a rule’s visual complexity. A long selector is not necessarily the right solution. Prefer a class that describes the element’s role, and keep selectors as simple as the design allows. Simpler selectors make later changes easier to predict.

Know when inheritance supplies a value

Some CSS properties, including color and font-family, normally inherit from a parent. If a section has color: darkslateblue and a paragraph inside it has no applicable color declaration of its own, the paragraph uses that color. Other properties, such as margin and border, do not normally inherit. A parent’s border does not automatically become a border on every child.

An inherited value does not compete with a declaration applied directly to the child in a specificity contest. Suppose a parent has #panel { color: red; } and its child matches p { color: black; }. The paragraph is black, even though the parent’s ID selector is more specific. The ID rule sets the parent’s color; the p rule sets the child’s color. Inheritance would matter for the child only if it had no winning color declaration of its own.

You can explicitly request inheritance with color: inherit. CSS also has keywords such as initial and unset, but they have different meanings; they are not interchangeable ways to “use the parent’s style.” When debugging, check whether a value is declared on the element or merely inherited from an ancestor.

Debug a conflict in a repeatable order

Use a browser with developer tools and a local HTML page; no library or build tool is needed for these examples. Inspect the element, choose one surprising property, and look at its matched declarations and computed value. A crossed-out declaration often indicates that another declaration took precedence, while an inactive rule may fail to match or may sit inside a condition that is not currently satisfied.

Then work through a short sequence. Confirm which selectors match. Check for !important, inline styles, and cascade layers before comparing specificity. Compare specificity only among declarations still in contention, then check their order. Finally, if there is no direct winning declaration, look up the parent chain to see whether the property inherits. Changing one rule at a time makes the cause clearer than adding another increasingly specific override.

Back to category ↗