skip to content

In CSS, when two declarations set the same property on the same element, what does the browser compare, and in what order, to decide which one applies?

level: middleimportance: must knowfreq 72%

answer

  1. specificity is not step one
  2. a list of tiebreakers, consulted in order
  3. origin and importance sort first
  4. layers come before specificity
  5. source order is the last tiebreak

basics

~20 s

The cascade compares origin and importance first, then shadow-tree context, then inline style attribute, then cascade layer, then specificity, and finally source order. Specificity is only reached when every earlier criterion ties, which is why a low-specificity important declaration still wins.

solid answer

~50 s

The cascade is a sorted list of tiebreakers, and specificity sits near the bottom. The browser gathers every declaration that targets the element and property, then sorts: first by **origin and importance** (user-agent, user, author, and the important variants, which are ordered in reverse); then by **context**, which only matters with shadow DOM; then by whether the declaration came from the element's `style` attribute; then by **cascade layer** order; then by **specificity**; and only if all of that ties, by **order of appearance** — last one wins. Each step is only consulted when the previous one produced a tie. That is why `!important` on a single type selector beats a normal declaration written with three IDs: importance is settled long before anyone counts selectors. This is the sort defined by CSS Cascading and Inheritance Level 5.

code

css · 2 lines
css
#main #content p { color: blue; }
p { color: green !important; }

go deeper

for a junior

Be able to name the everyday tiebreakers in order: importance, then specificity, then whichever declaration comes later in the file. Say plainly that the last rule only wins when specificity ties.

for a middle

Explain the full sort — origin and importance, context, inline style attribute, layer, specificity, source order — and stress that each step is only consulted when the one above it tied. Show how that explains a low-specificity winner.

for a senior

Demonstrate diagnosing with it: walk a real override failure down the list in devtools rather than reaching for !important, and explain the per-property, per-element nature when a shorthand and a longhand collide.

for a principal

Own the consequence for a codebase: which steps you allow teams to use as a lever (layers, source order) and which you treat as an escape hatch, so that overrides stay predictable as the stylesheet and the number of contributors grow.

## The cascade is a sort, not a single rule Every element/property pair — say the `color` of one particular `<p>` — can be targeted by many declarations at once: the browser's own defaults, a stylesheet you wrote, a rule in a component file, an inline attribute. The **cascade** is the algorithm that puts all of those candidates in a total order and takes the winner. Specificity is one step in that order, not the whole thing, and it is a fairly late step. The steps, from first consulted to last, as defined by CSS Cascading and Inheritance Level 5: 1. **Origin and importance.** 2. **Context** (the shadow tree a declaration came from; only relevant when shadow DOM is involved). 3. **Element-attached styles** — declarations in the element's `style` attribute beat declarations from style rules at the same origin and importance. 4. **Cascade layers** — the order established by `@layer`. 5. **Specificity** — the selector's weight. 6. **Order of appearance** — the declaration that comes later wins. A step is only consulted when everything above it tied. That single sentence explains most cascade surprises. ## Step 1: origin and importance Declarations arrive from three **origins**: the *user-agent* stylesheet (the browser's built-in defaults), the *user* stylesheet (browser settings and user styles), and the *author* stylesheet (yours). Each origin has a normal and an important half, and the important halves are ordered in reverse. Written weakest to strongest, with the two animation-related origins in place: ``` user-agent normal user normal author normal CSS animations (@keyframes) author !important user !important user-agent !important CSS transitions ``` So `!important` does not "just win": it moves a declaration into a different, inverted band. An author `!important` beats every normal declaration but loses to a user `!important` — that inversion is deliberate, so a user's accessibility override cannot be defeated by a site. ## Step 3: the style attribute `<p style="color: red">` is an author-origin, normal declaration, but it is *element-attached*, so it beats normal author declarations from style rules regardless of how those rules are written. It does not beat an author `!important` in a stylesheet, because importance was already settled at step 1. And an inline declaration that is itself marked `!important` sits in the important-author band, where it again outranks important declarations from style rules. ## Step 5: specificity, and why it feels unreliable Specificity compares the selector's weight, so `#nav a` outranks `.link`. It feels unreliable to people only because they reach for it first. When a two-ID rule loses to a one-class rule, the explanation is almost always a step above: the winner was important, or inline, or in a layer that outranks the loser's layer. ## Step 6: order of appearance The final tiebreak is document order — the later declaration wins. "Later" means later in the flattened set of stylesheets: link order matters, and a rule at the bottom of a file beats an identical one at the top. ```css p { color: blue; } p { color: green; } /* wins: same origin, same layer, same specificity */ ``` ## Two more facts that keep the model honest The cascade runs **per property, per element** — it does not pick a winning *rule*. One rule can supply `color` while another supplies `background-color` on the same element, and each property is resolved independently. Shorthands are expanded to their longhands first, so `background: red` and a later `background-color: blue` are competing on `background-color` alone. And the cascade only runs when there *are* competing declarations. If no declaration targets the property, an inherited property takes its parent's computed value and a non-inherited one takes its initial value; that is a separate step of the value-resolution pipeline, not a cascade tiebreak. ## Diagnosing with the model When a declaration is not applying, walk the list top-down instead of adding `!important`. Is something important beating me? Is the winner inline? Is my rule in a weaker layer? Only then: is it less specific? Only then: does it appear earlier? Devtools shows the losing declaration struck through and lists the winner directly above it, which usually names the step for you. One practical note: cascade layers (`@layer`) are the newest step in this list, supported in all major browsers since 2022, so on any modern browser the layer comparison is real and happens before specificity.

  • If the cascade picks a winner per property, what happens when one rule sets a shorthand and a later rule sets one of its longhands?
    Shorthands are expanded into their longhand components before the cascade runs, so `background: red` becomes a set of longhand declarations including `background-color: red`. A later `background-color: blue` competes only on that one longhand and wins it; the other longhands the shorthand set — `background-image`, `background-position` and the rest — are untouched and keep the shorthand's values.
  • The cascade found no declaration at all for a property on an element. What value does the element get?
    Nothing was cascaded, so value resolution falls through to defaulting. If the property is inherited, the element takes its parent's computed value; if it is not inherited, it takes the property's initial value from the specification. This is a separate stage from the cascade — it never fires when a declaration, even a losing one, won the sort.
  • Two identical rules live in two different linked stylesheets. Which one wins?
    Assuming the same origin, importance and layer, and equal specificity, order of appearance decides — and order is taken across the flattened set of stylesheets, so the rule in the `<link>` that appears later in the document wins. Load *completion* order is irrelevant; it is the position of the link or `@import` in the source that counts.

saying these in an interview costs you the question

  • Says the browser checks specificity first
  • Thinks !important is a specificity value rather than an importance flag
  • Believes the cascade picks a winning rule, not a winning declaration per property
  • Claims inline styles can never be overridden
  • Thinks a later stylesheet always wins regardless of specificity

context