2

I find it very illogical that the selector for an element inside an #id has precedence over the #id of that element.

Disclaimer: I am aware there are many similar questions floating around. I have also read up on CSS precedence and specificity documentation. However none of these questions and documentation seem to cover this specific problem I have, not to mention explaining why. Maybe I'm just bad at searching; in that case, please guide me. But I digress.

For example, take the following HTML:

<style>
  #top button {
    padding: 0;
  }
  #myButton {
    padding: 2em;
  }
</style>
<header id="top">
  <button type="button" id="myButton">Button</button>
</header>

For some reason, the button comes out with no padding. Inspect element shows that the style of #myButton is overridden.

Inspect Element Overridden

Isn't #myButton, which always selects a unique element, more specific than #top button, which selects multiple buttons under a unique element?

Is this a bug or an intended feature? How standard is this behavior across browsers? And if a feature, why?

Tested with Chrome 78 and Edge 44 (EdgeHTML 18) on Windows 10.

cyqsimon
  • 1,466
  • 10
  • 27
  • For now I have to settle with a makeshift patchy solution - `#myButton#myButton` seems to override `#top button` by hoisting its specificity. But obviously this is very ugly and I would like to avoid doing this in the long run. I would still like to know the "why" as stated in the question. – cyqsimon Nov 06 '19 at 03:24

2 Answers2

3

Basically #id1 element is more specific than #id2 on it's own.

Specificity is additive, the more you add (in general) the more specific the selector gets.

In your example #top and #button have the same specificty, as soon as you add more information to one of those selectors it becomes more specific. Therefore #top button is more specific than #button on it's own.

Here is a great article on specificity complete with great cheat sheet.

As an example, lets say you have and article where you want the color to be blue. But for all buttons in the article you want the color to be red.

#article1 {color:blue;}
#article1 button {color:red;}
<article id="article1">
  <p>The quick brown fox jumps over the lazy dog</p>
  <button>Button</button>
</article>

One option in your instance is to use the element itself

  #top button {
    padding: 0;
    color:red;
  }
  button#myButton {
    padding: 2em;
    color:blue;
  }
<header id="top">
  <button type="button" id="myButton">Button</button>
</header>

In this example #top button and button#myButton have the same specificity. However because button#myButton is declared last, it is applied.

You have also inadvertently stumbled upon one of the reasons why a lot of developers recommend you don't use ids for styling and should basically be using classes instead.

Jon P
  • 17,053
  • 7
  • 44
  • 64
  • If I understood correctly, specificity is always additive even when the CSS selector is looking for a nested structure? I.e. it makes sense to me that in `#id1 elmt1`, the specificity of `#id1` **should not** count toward the overall specificity of the selector (since the target is ultimately `elmt1` not `#id1`), however in actuality the specificity of `#id1` gets added in anyway. – cyqsimon Nov 06 '19 at 04:09
  • I assume this is a language design decision? Why is it done this way then? – cyqsimon Nov 06 '19 at 04:09
  • 1
    "specificity is always additive even when the CSS selector is looking for a nested structure" - no, you are incorrect here. Nesting has nothing to do with it. The css engine will not check if `#myButton` is nested or not it will purely check which most specific rule applies to it. The W3C needed to come up with rules on how CSS would be applied and how "tie breaks" would be resolve. Why the chose this? You'd have to ask them. But it works, and once you know the rules they are easy to apply. – Jon P Nov 06 '19 at 04:15
  • 1
    @cyqsimon: The calculation has to take every represented element into account, otherwise you wouldn't be able to perform such an override *if you wanted to*. The decision was made for simplicity's sake. It is, nevertheless, a decision the CSSWG regrets, since this behavior understandably doesn't scale well. – BoltClock Nov 06 '19 at 04:55
  • Ok I see. Thanks to all for the help. I will probably avoid using id for styling afterwards. – cyqsimon Nov 06 '19 at 06:43
0

Now that I understand CSS specificity slightly better, I simply want to directly answer some of my original questions which were implicitly answered by Jon P, in hopes of making lives slightly easier for future readers.

Isn't #myButton, which always selects a unique element, more specific than #top button, which selects multiple buttons under a unique element?

Logically, yes, it should be more specific. But CSS interpreter doesn't see it this way: it simply adds the specificity of each individual selector and doesn't care about the structure of the elements at all. Read Jon P's answer for details. "#top"="#myButton", therefore "#top button" > "#myButton".

Is this a bug or an intended feature?

An intended feature, although a questionable one. CSS specificity is designed this way for simplicity.

How standard is this behavior across browsers?

Presumably, very. Since it's a part of specification, correctly implemented interpreters should all behave this way.

And if a feature, why?

Again, it's designed this way for simplicity. However this choice is one that the CSS Working Group regrets according to BoltClock♦ due to complications it created. But there's no time machine so we're stuck with this imperfect system now. To avoid this mess, simply don't use id for styling.

cyqsimon
  • 1,466
  • 10
  • 27