Skip to content

CSS :where Specificity Rule Not Applying: Why footer a Wins

admin11 min read
Illustration of a CSS :where specificity rule not applying because a zero-weight selector loses to a heavier one

If you are seeing a CSS :where specificity rule not applying to a link, the rule is usually applying fine. It matches the element, but it loses the cascade for the competing declaration. In :where(#content) a, the :where() part contributes zero specificity, even though it contains an ID. Only the trailing a counts, which gives 0-0-1. A plain footer a rule scores 0-0-2 and wins the color conflict.

The input here is a small stylesheet with two rules that match the same link. The useful outcome is being able to predict the winner and choose a deliberate fix. The behavior described follows the Selectors Level 4 draft and MDN, both checked on October 4, 2026. You can write and preview the CSS in the CSS Editor, but its preview uses a fixed sample page, so I will also show where to test with your own markup.

Two balance scales illustrating how a heavy-looking selector can carry zero weight in the cascade

Why the :where() rule matches but still loses

Two separate questions are involved, and mixing them up causes most of the confusion. The first is matching: does this selector apply to this element? The second is the cascade: when two matching rules set the same property on the same element, which declaration wins?

The selector :where(#content) a matches any link that has an ancestor with id="content". The ID still narrows which links qualify. What it does not do is add weight. The Selectors Level 4 draft defines :where() so that its specificity is always replaced by zero, regardless of what the selector list inside it contains. The MDN specificity guide describes the same behavior.

That leaves the type selector a, worth 0-0-1. The competing rule footer a has two type selectors, so it is worth 0-0-2. The second one wins. The red rule is not ignored in general. On any link outside the footer, nothing competes with it, so the link still turns red.

When you debug, replace the sentence “the rule is not applying” with “the rule matches, but another matching declaration outranks it.” That wording points you at the cascade instead of at the selector syntax.

Abstract page layout with one red link and one blue link representing two competing CSS rules

A minimal example where both selectors match

Here is markup in which the footer link matches both rules. The footer sits inside the element with the ID, and a second link sits outside the footer for contrast.

<main id="content">
  <p><a href="/guide">Guide link</a></p>
  <footer>
    <a href="/top">Back to top</a>
  </footer>
</main>

And the stylesheet:

:where(#content) a {
  color: red;
}

footer a {
  color: blue;
}

The “Back to top” link matches both selectors, but it renders blue. The “Guide link” matches only the first rule, so it renders red. Reversing the order of the two rules does not change either result, because the footer rule has the higher specificity.

This outcome assumes both declarations share the same cascade origin, the same importance and the same cascade layer. Those qualifications matter, and I return to them below. For now, notice that the output tells you the rule is working: the red paragraph link proves the :where() selector matched.

Scoring a CSS :where specificity rule not applying

Specificity is usually written as three numbers: IDs, then classes, attributes and pseudo-classes, then type selectors and pseudo-elements. You compare the columns from left to right. A single ID beats any number of classes, and a single class beats any number of type selectors. It is not a base-10 number, so 0-0-11 does not beat 0-1-0.

SelectorSpecificityWhy
:where(#content) a0-0-1:where() counts as zero, so only a is scored.
footer a0-0-2Two type selectors.
#content a1-0-1One ID and one type selector.
:is(#content) a1-0-1:is() takes the specificity of its most specific argument.
:where(#content) footer a0-0-2Only footer and a count, which ties with footer a.

Read the first two rows together and the paradox disappears. The ID is present in the first selector, but the scoring ignores it. The footer rule is simply heavier: 0-0-2 beats 0-0-1 in the last column.

The :is() row is the useful contrast. Both pseudo-classes accept a selector list and match the same elements, but :is() keeps the weight of its argument while :where() drops it to zero. That is the whole difference between them, and it is why swapping one for the other can change which declaration wins.

The last row shows a tie. When two matching selectors have the same specificity, source order decides, and the later rule wins. That is the only situation where moving a rule up or down changes the outcome.

Why moving the rule or adding !important is the wrong first fix

A common reaction is to move the weaker rule later in the file. That does nothing here, because source order is only consulted when everything ranked above it ties. Specificity ranks higher than order, and 0-0-1 against 0-0-2 is not a tie.

Adding !important can force a result, but it changes the importance of the declaration and makes the next override harder. I would not reach for it as a default fix. Treat it as a deliberate decision, not a patch for a specificity surprise.

There are also cascade factors that sit above specificity. Before specificity is even compared, origin, importance and cascade layers can already decide the winner. A declaration in a higher-priority layer can beat a rule with a much higher selector score, and an important declaration reverses the usual ordering. The MDN cascade introduction lays out these steps. That is why my demonstration says the footer link is blue “assuming the same origin, importance and layer.”

The general debugging habit is the same one that helps with other “my declaration is ignored” problems, such as a flexbox card that will not shrink around a long URL. In both cases the declaration exists, and some other rule or default constraint takes precedence. Find the competing rule first, then decide whether to change it or accept it.

Fixing a CSS :where specificity rule not applying on purpose

Which fix is correct depends on what you want the red rule to mean. There are three honest choices.

Make the ID rule outrank footer links. If the content area really should override footer styling, use a selector that counts the ID. #content a scores 1-0-1 and beats 0-0-2. Do this only when higher specificity is intentional, because every later override now has to match or exceed it.

#content a {
  color: red;
}

footer a {
  color: blue;
}

With this stylesheet, the footer link inside #content is red, because 1-0-1 outranks 0-0-2.

Keep the low-specificity default. The reason to write :where(#content) a is that it gives you an easily overridden baseline. If the footer is supposed to be blue, the original CSS is already correct and the footer rule is doing its job. Accepting that result is a legitimate fix.

Create a deliberate tie. A rule like :where(#content) footer a scores 0-0-2, which ties with footer a. The later rule then wins. This works, but it relies on source order, so it is more fragile than choosing a clear score.

You can also swap in :is(#content) a, which scores 1-0-1 like the ID version. Choose between :is() and #content for readability, not for a different score.

How to test it in the CSS Editor and HTML Code Editor

The CSS Editor offers syntax highlighting, property auto-completion, a live preview and beautify or minify. The preview renders on a fixed sample page. That page has a link, but it has no #content element and no footer, so the demonstration selectors cannot reproduce the conflict there by themselves. The editor also does not lint CSS, although some unknown property names may receive error coloring.

For a test that needs your own markup, use the HTML Code Editor. It has a sandboxed live preview and no validation. Put the markup and a <style> block in one document:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Specificity test</title>
  <style>
    :where(#content) a { color: red; }
    footer a { color: blue; }
  </style>
</head>
<body>
  <main id="content">
    <p><a href="/guide">Guide link</a></p>
    <footer><a href="/top">Back to top</a></footer>
  </main>
</body>
</html>
  1. Paste the document into the editor and open the live preview.
  2. Confirm that “Guide link” is red and “Back to top” is blue.
  3. Swap the two CSS rules and confirm nothing changes.
  4. Change the first selector to #content a and confirm both links are now red.
  5. Open your browser’s developer tools on the footer link and look at the styles pane. The losing declaration is normally shown crossed out, which confirms a cascade loss rather than a selector mismatch.

Because neither editor lints, a mistyped selector looks the same as a rule that lost on specificity. To separate the two, temporarily delete the footer a rule. If the link turns red, the :where() rule matched all along and the cascade was the problem.

If your stylesheet is crowded, run it through the CSS Beautifier first so each selector sits on its own line before you compare scores. A beautifier improves layout only. It does not repair invalid CSS or check which rule wins. If you later minify, compare the output with the source, as shown in the guide on a CSS minifier removing space before focus selectors, and keep the readable original.

Browser support and where the zero score stops mattering

MDN currently labels :where() as Baseline Widely available and says it has worked across browsers since January 2021, checked October 4, 2026. You can read how that label is defined on the MDN Baseline compatibility page. That is a statement about current mainstream browsers. It does not establish support in every older browser or embedded web view.

This matters for fallbacks. A browser that does not recognize the pseudo-class treats the selector as invalid and discards the whole rule, so the link would not turn red at all. If you must support old environments, check your audience before relying on :where() for essential styling.

Also remember what this article does not cover. Specificity explains conflicts between matching declarations for the same property on the same element. It does not tell you whether your CSS is valid, whether the markup is accessible, or whether a color has enough contrast. A clean preview proves only that the result looks as intended in that browser at that moment.

Frequently asked questions

Does :where() ever add specificity?

No. The specificity of a :where() pseudo-class is always zero, whatever selector list it contains. Only the selectors outside it are scored. Selectors Level 4 defines this, and MDN describes it in its specificity guide.

Does the ID inside :where(#content) still matter?

Yes, for matching. The rule still applies only to elements inside an element with that ID. What the ID does not do is raise the specificity, so it will not help the rule beat a competing selector such as footer a.

What is the difference between :is() and :where()?

Both accept a selector list and match the same elements. :is() takes the specificity of its most specific argument, so :is(#content) a scores 1-0-1. :where() always scores zero, so :where(#content) a scores 0-0-1. Choose :where() for easily overridden defaults and :is() when the weight should count.

Will placing the :where() rule after footer a fix it?

No. Source order only decides between selectors with equal specificity. Here the scores are 0-0-1 and 0-0-2, so the footer rule wins regardless of order. Order matters only when you create a tie, such as :where(#content) footer a against footer a.

Should I use !important to make the rule win?

I would not use it as the default fix. It changes declaration importance and makes later overrides harder. First decide whether the red rule should have higher specificity, in which case #content a is clearer, or whether the footer override is intended.

Why would anyone write :where(#content) a at all?

It lets you scope a rule to a region of the page while keeping it easy to override. Because the scope adds no specificity, a later single class or a more specific element rule can change the result without a specificity fight. That suits base styles and defaults.

Can the CSS Editor show me which rule wins?

Not for this example by itself. Its live preview renders your CSS on a fixed sample page with a link but no #content element or footer, and it does not lint. Use the HTML Code Editor with your own markup and a style block, then confirm in your browser’s developer tools.

Next steps

A CSS :where specificity rule not applying is almost always a cascade result, not a broken selector. The ID inside :where(#content) limits which links match but adds nothing to the score, so footer a at 0-0-2 beats :where(#content) a at 0-0-1 in either source order. Decide whether you want that low-priority default, or switch to #content a or :is(#content) a and accept the higher score.

Your next step: paste the test document above into the HTML Code Editor, delete the footer rule to prove the match, then restore it and try each fix. For broader context on choosing tools for this kind of work, read about CSS editors in modern web development.

Related tools and resources on HTML Editor Online:

Sources and further reading

Try it in your browser

Free online editors with syntax highlighting. Open one and start coding, no account needed.

All Tools