Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How do CSS cascade layers change which rules take precedence?

Quick answer

For normal declarations, later layers beat earlier layers, and unlayered declarations beat layered ones. For !important declarations, that order reverses: earlier layers beat later layers, and layered important declarations beat unlayered important ones. Layer precedence is checked before selector specificity, so a more specific selector cannot overcome a losing layer. Specificity and then source order help decide conflicts within the winning layer priority. [S1][S2]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

I have CSS from several parts of a project, and specificity is not explaining which rule wins. How do layers change the cascade?

Geez

Think of the cascade as choosing a layer priority before comparing selector specificity. For normal declarations, later layers have higher priority than earlier layers. Normal declarations outside any layer outrank normal declarations in every layer. For !important declarations, the layer order flips: earlier layers outrank later ones, and layered important declarations outrank unlayered important declarations. [S1]

A compact diagram, assuming the layers are declared in this order:

Normal declarations (lower → higher): base → components → overrides → unlayered

!important declarations (lower → higher): unlayered → overrides → components → base

This describes layer priority when comparing declarations at the same relevant cascade origin. A rule in a higher-priority layer can win even if the competing rule has a more specific selector in a lower-priority layer.

Follow-up question

Can you show a small stylesheet where a less-specific selector beats a more-specific one?

Geez

Assume this stylesheet is the only relevant source of competing author rules:

@layer base, components, overrides;

@layer base {
  #panel.notice { color: darkred; }
}

@layer components {
  .notice { color: navy; }
}

.notice { color: seagreen; }

For a <div id="panel" class="notice">, the normal declaration outside a layer wins, so the text color is seagreen. It beats both layered declarations before specificity is compared. Among the two layered declarations, components outranks base; that layer ordering also takes priority over the fact that #panel.notice is more specific than .notice. [S1]

The initial @layer statement sets the order; the later blocks put rules into those named layers. This example is illustrative: other relevant declarations in the page could affect the result.

Follow-up question

What changes if the declarations use !important? And does moving a rule later in the file fix a layer conflict?

Geez

With !important, the layer priority reverses. For example, if the base and components color declarations above both became important, base would win over components, despite appearing earlier in the stylesheet’s layer-order declaration in the opposite direction for normal rules. If the unlayered .notice declaration also became important, the important declaration in a named layer would outrank that unlayered important declaration. [S1]

Moving a rule later in the file does not let it override a declaration from a higher-priority layer. Source order is a later tie-breaker: after the relevant cascade priority and specificity have been considered, order of appearance can decide a remaining tie. [S2] So first check whether the rules are layered and which layer has priority; only then use specificity and source order to investigate a conflict.

Follow-up question

How should I debug my own conflict without adding more overrides?

Geez

Use a short comparison rather than immediately increasing selector specificity:

  1. Identify the two declarations that set the same property on the same element.
  2. Check whether either declaration is !important.
  3. For normal declarations, compare layer priority: later layer first, then unlayered rules above layered rules. For important declarations, reverse the layer order; layered important rules outrank unlayered important rules. [S1]
  4. If the declarations remain at the same relevant layer priority, compare specificity; if that also ties, check source order. [S2]
  5. Change the layer assignment or the intended declaration, then recheck the result.

A useful note to leave beside a bug is: “Property: [property]. Competing selectors: [selector A] and [selector B]. Layer/order: [layer A] vs [layer B]. Importance: [normal or !important]. Expected winner: [rule].” This records the reason for the fix rather than just preserving another override. The comparison here does not account for every possible cascade factor, such as declarations from different origins; use it for the layer, specificity, and source-order conflict you are investigating.

A practical checklist

  • Find the exact competing declarations for the same property.

  • Record each declaration's layer, or mark it unlayered.

  • Check whether either declaration is marked !important.

  • Compare layer priority before comparing selector specificity.

  • Use source order only after the relevant priority and specificity tie.

A few more useful answers

Can a highly specific selector in an earlier normal layer beat a simpler selector in a later layer?

No. Layer priority is resolved before selector specificity, so the later normal layer wins that cross-layer comparison. [S1]

Do unlayered rules always win?

No. Unlayered normal declarations outrank layered normal declarations, but layered !important declarations outrank unlayered !important declarations. [S1]

When does source order matter?

Source order can decide between declarations that remain tied at the relevant cascade priority and specificity. It cannot override a higher-priority layer. [S2]

Sources

References used for this guide.

  1. [S1] Cascade layers — Learn web development | MDN
    developer.mozilla.org · Checked
  2. [S2] Specificity — CSS | MDN
    developer.mozilla.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work