When two CSS rules target the same element with conflicting values, the browser has to decide which one wins. That decision is governed by two mechanisms working together: the cascade (which considers source order and origin) and specificity (a scoring system based on how precisely a selector targets an element). Understanding both is the difference between confidently predicting how your CSS will render and randomly adding !important until something works.

How Specificity Is Calculated

Every selector gets a specificity score based on what it contains, roughly in this order of weight, highest to lowest: inline styles (written directly in a style attribute), ID selectors, class/attribute/pseudo-class selectors, and element/pseudo-element selectors. A higher-weight selector always beats a lower one, no matter how many of the lower one you stack up — one ID always beats any number of classes.

/* Specificity: 0 element, 1 class, 0 ids -> wins over the rule below */
.card-title { color: blue; }

/* Specificity: 1 element, 0 classes, 0 ids -> loses */
h2 { color: red; }

<h2 class="card-title">Hello</h2>

This heading renders in blue. Even though the h2 rule appears later in the stylesheet, the class selector has higher specificity, and specificity beats source order whenever there's a conflict.

When Specificity Ties: Source Order Wins

.card-title { color: blue; }
.card-title { color: green; }

Both rules have identical specificity (one class each), so the later rule in the stylesheet wins — the text renders green. This is the actual "cascading" part of Cascading Style Sheets: when specificity is tied, whichever rule the browser reads last takes priority.

A Common Real-World Conflict

#sidebar .widget-title { font-size: 14px; }
.widget-title { font-size: 20px; }

Even though the second rule comes later, the first one wins — it combines an ID selector with a class, giving it far higher specificity than the lone class selector below it. This is a classic source of CSS bugs: a developer adds a new rule, sees it doesn't apply, and can't figure out why — usually because an earlier, more specific selector is quietly winning.

The !important Escape Hatch — and Why to Avoid It

.widget-title { font-size: 20px !important; }

!important overrides normal specificity rules entirely — a rule marked important wins regardless of how weak its selector is, unless another rule is also marked important and has higher specificity. It solves the immediate problem, but it also makes your CSS harder to reason about and override later, since the normal rules no longer apply. Treat it as a last resort, not a first instinct — the better fix is almost always to simplify the conflicting selector or restructure the CSS so the conflict doesn't exist in the first place.

A reliable way to keep specificity manageable across a whole project is leaning on class selectors consistently and avoiding ID selectors for styling altogether — IDs are better reserved for JavaScript hooks and anchor links.