Hacker News new | past | comments | ask | show | jobs | submit login
256 CSS Classes Can Override an #id (codepen.io)
412 points by alpb on Aug 15, 2012 | hide | past | favorite | 90 comments



Webkit source: http://trac.webkit.org/browser/trunk/Source/WebCore/css/CSSS...

Specifically:

  69	    case Id:
  70	        s += 0x10000;
  73	    case Class:
  88	            s += 0x100;
id selectors are worth (0x10000/0x100) == 0x100 == 256 class selectors.

This seems to be the mozilla source: http://hg.mozilla.org/mozilla-central/file/17c65d32c7b8/layo...

  521   nsAtomList* list = mIDList;
  522   while (nsnull != list) {
  523     weight += 0x010000;
  524     list = list->mNext;
  525   }
  526   list = mClassList;
  527   while (nsnull != list) {
  528     weight += 0x000100;
  529     list = list->mNext;
  530   }
Same weights, Id = 0x10000, Class = 0x100


For some reason, based on the w3 documentation, I was under the impression that an id had an order of magnitude more specificity than a class.

http://www.w3.org/TR/CSS2/cascade.html#specificity


From that spec:

> Concatenating the four numbers a-b-c-d (in a number system with a large base) gives the specificity.

So webkit is using 256 as "a large base".

I only just read this spec a few months ago. For years I was under the impression that the spec was "1 point for an element, 5 for a class, 10 for an id", and the selector with the highest point value won...


Actually, me too. Where does this misconception come from?


Under the same impression too, I definitely remember reading this on a W3C advice/info blog (dark brown background, white and blue header - useful memory).


I think it has _2_ (base 10) orders of magnitude more specificity. That's 10 times more specificity (than a single order)!


One might even say it has an order of magnitude more specificity than a single order.


Did they mean to fall through to case Class in line 73?


He elided some lines, there is a break.


[1] http://coding.smashingmagazine.com/2007/07/27/css-specificit...

This is a feature, not a bug. Its called CSS specificity.

Although, the link[1] seems to suggest it should be 100, not 256... that might be a bug.


Read the spec instead. It says "in a number system with a large base". I suspect the unspecific wording is on purpose (to allow optimisation for sane conditions), but it should be fairly clear that the intent is that classes cannot override ids.


"255 classes should be enough for everyone"?


Seriously!


Stranger, perhaps, this also works with the same class 256 times. That is <div class="c"> with a style def .c.c[...256...].c {}.

http://codepen.io/anon/pen/zbcxs


if you change it to #id#id it needs another 256 .c classes, interesting!


That is perplexing! Is there a stack of some sort in play here?


I guess the algorithm for specificity in all modern browsers involves a base-256 number. Why? I believe there are 4 slots for specificity - class, id, !important, and styles defined in the tag. 256^4 = 2^32, the size of an integer. Unfortunately we get this is as a side effect.


According to the CSS 2.1 spec, “Concatenating the four numbers a-b-c-d (in a number system with a large base) gives the specificity.” Base-256 is the obvious way to encode each digit efficiently, and I suppose all browsers that do this are still in compliance with the spec.


It still works with > 256 classes. I was expecting it to overflow and go back to blue when I added a 257th class.


If it is adding numbers, it may be losing the ID altogether.

  spec = classes + (id>>8) + (important>>16) + (styles>>24);
If the assumption was that classes would "probably" be under 256 and wasn't put in a char, it could be messing with the whole value.


Can you override an !important with 256 nested ids?


Nope, although I did find once you hit a certain number of ID's, it was something over 1000, not sure of exact number, it just ignores the rule altogether.


1024?


In the example, switch the order of the rules and the background will switch -- last rule wins, all things being equal. Not surprising...

  $ python
  >>> CLASS = 0x100
  >>> ID = 0x10000
  >>> CLASS * 256 == ID
  True


This seems to be taken from this answer on a question about HTML/CSS specificity

http://stackoverflow.com/a/11934505/950912


This is where this whole thing came from today. Somebody submitted this earlier, but it never got to the front page. I suggest a detailed readthrough of the question and all the answers for a great overview of how this works.


For your sake, I hope this wasn't an actual bug you ran into :)


Opera 12 displays a blue box while Chrome 21 and IE 9 show a red one. Another cross-browser incompatibility that we have to deal with...


Yay Opera!

Also, if you're really using >= 256 classes in your CSS you may have more systemic problems than just cross-brwoser compatibility.


What base does Opera use for specificity?


That's the sad thing about closed source software. Their implementation might be drastically different. I think they should write a blog post or at least a comment here about it though, I'm going to bug them now.


We use base 24, actually, but since we truncate before concatenating, we behave as expected here, unlike Gecko/WebKit. Obviously, a different TC that depends on one selector having more class names than another ("c" in CSS 2.1 6.4.3) will fail in Opera; it passes in GeckKit. See attachment at http://lists.w3.org/Archives/Public/www-style/2012Aug/0493.h... for an illustration.


Oops, should be base 25, obvs.


Probably a large one.


https://twitter.com/patrick_h_lauke/status/23603022452819148...

and from irc: "in other words, its the size of the storage which matters and not really the base, other browsers seem to use 8 bits, we use 16" @shwetank


Can someone explain the thinking behind this? Wouldn’t it be much simpler to have #id always override a .class?

The way it is set up currently that’s basically always the case (since 256 .classes are rarely used), but there is this weird edge case when as many .classes are used.

I don’t think it’s possible to meaningfully work with a rule like that. It just seems so non-sensical.


As far as I can tell from the specification posts here, it's entirely to make implementation more feasible. Rather than some complicated calculation of precedence, you can just assign each rule a single score. As long as the weird exception essentially never comes up (which is the point of specifying "a number system with a large base"), it's an efficient and sensible way to implement the desired rule.


The issue isn't in the base case but derived cases to determine which overrides the other. A div with an id that has another div with a class attribute should override anything done on just the div with the id? But when does the specificity of any chain of just classes become more specific than an id. That is why they have weights.


I suppose someone should update this question then, because it looks like to me the limit is 255...

http://stackoverflow.com/questions/4354921/css-is-there-a-li...


I don't think the class limit is 255, it is still a fuzzy number as the post declares. Here, I've modified the codepen to include another 256th class, and the CSS declarations on that class are applied to the element. http://codepen.io/anon/pen/cKuBx


It's not a bug, OP found out about it from my question on Stack Overflow: http://stackoverflow.com/questions/2809024/points-in-css-spe...


I recommend not using IDs due to selector specificity and the scenario where you most likely will run into annoying #ID issues and think about using `!important`.

I don't think it's absolute blasphemy to use #ids, but try to never use them. Think of it that way. I tend to use them only for #js-functionality hooks, which is also a good-practice to tell someone "don't freaking change this ID or you break the JS."

Tips from http://www.betterfrontend.com


I'm obviously lacking in imagination here. How would using an ID for styling a "clearly one-per-page" element lead to problems? I'm thinking of things like the main page title, or some sort of primary navigation block. The whole point there is that I want the specific rules for those elements to override the defaults, right?


It may be "clearly one-per-page" now, but it may not be. You don't really gain anything by using an ID. Keep the selector specificity simple so it doesn't bite you in the ass and cause a refactor.

.page-wrapper .page-main %article.main

There's no difference using that to

#page-wrapper #page-main %article#main

Psuedo example, those can be mix and matched with classes and IDS but doing so just complicates the selectors and the cascade.

There is of course exceptions and things that may just make total sense, but overall, it leads to complications. You don't gain any real benefits unless it's a JS hook for #id selector performance.


For example, styling the same unique block based on context. If it's a mainpage, I want it red, otherwise it's green.


I follow the same line of thought to an extent. I like to put an id on the body tag for global navigation purposes, but aside from that I keep the id for js work.


Yep, exactly this!


Does anyone else get kicked to github from the Betterfrontend website? Probably supposed to be a button but ended being a frame-breaking Github 404.

That's not very nice.


I'm experiencing that as well - it looks like any (unofficial) github buttons need to have their domain changed from github.com to ghbtns.com. The commit message seems to confirm this.

[0]: https://github.com/markdotto/github-buttons/blob/master/READ...


Thank you, it's been fixed! Clear cache if you still have issues.


Thanks. That wasn't nice how they did it. Now it's leading to a caching problem for users who already viewed it. I'm not sure what to do. Fuuuu.

Thanks for the heads up. It should be fixed now. Clear cache the "hard" way if you have to.


Not a bug, probably just CSS's ridiculous selector algorithm. IIRC, the algorithm looks something like (100xid's) + (10xclasses) ...


The algorithm looks like that "in a number system with a large base". It is arguably not what the spec intended but probably acceptable in exchange for performance.


EDIT: i'm wrong


Interesting current versions of the following on windows fail

- Firefox - Chrome - Internet Explorer 9

Opera shows the expected result though.


I got a red box, chrome 21 on win 7.


Red box on Chrome 21 on Snow Leopard


I get a red box in everything but opera on linux. Seems to not be platform dependant at least.


I get a blue box on Opera for Windows.


Seeing the same behavior on IE10 RTM as well.


red box on Safari 6.0 on OS X Mountain Lion


red box firefox linux


256 nested ids seems to create an overflow. If you remove the #i000 at the beginning of the rule on this: http://codepen.io/anon/pen/zefGg it turns red, but otherwise it stays blue.


Here is a better example of ids overflowing: http://codepen.io/anon/pen/Khwon


Might depend on the browser, but Webkit-based browsers, for example, use a 0xffffff mask on the resulting specificity. And so...

  $ python
  >>> MASK = 0xffffff
  >>> MASK
  16777215
  >>> ID = 0x10000
  >>> ID * 256
  16777216
  >>> ID * 256 & MASK
  0


Awesome! I can't believe this is trending on HN. But that is very cool..

Now, how is this useful?


Knowing CSS's selector algorithm is useful as it helps rule out possibilities for CSS override situations.

Edit: punctuation


Here's some of the specificity code from Webkit (right at the top of the file):

http://trac.webkit.org/browser/trunk/Source/WebCore/css/CSSS...

Basically, selectors are summed together with class selectors given a weight of 0x100, and id selectors given a weight of 0x10000, so this is a simple overflow. It's worth noting that a mask of 0xffffff is used, so using 256 #id's should give a specificity of zero.


I'm guessing that the implementation uses a 32-bit integer for calculating rule strengths as they apply to a selection. With that in mind, 8 bits used for each variable.. so it would make sense that 256 would overflow into the next value. see: http://www.webteacher.ws/2008/05/19/tip-calculate-the-specif...


I had the impression that class specificity (weight: 10) times 11 would weigh more than a single id (weight: 100), but I guess I'm wrong.


Weight 10 in a number system with a large enough base (e.g. webkit is using base 256).

I think the intent of the specification is that 1 id is always more specific than classes for any practical number of classes.


http://code.google.com/edu/submissions/html-css-javascript/

Look at the css section. id's, classes, and selectors have weights which are used to determine which rule wins in the event of a conflict.


I just tried doing this with nested ids and it didn't work so perhaps this is for classes only. Might be worth trying layers of HTML tags also - see http://pastebin.com/7BhQdNAn


Pretty awesome find. I imagine it will have some outrageous use some day for someone.


Or a way to alter the webpage when you get an XSS exploit..


There are easier ways that will work, so I can't see this being useful, only interesting.



Interestingly, it doesn't seem like the classes need to be different for it to work, at least on Safari 6.0 if you change all the classes to .c000 it works just as well as having different ones.


Don't worry guys, crisis averted:

#id { background: blue ! important;}


Add !important to the 256 classes too and they win.


It doesn't seem to work when you switch the order of the css selectors.


Both selectors have the same specificity, so the last one is applied. Add another class and the order won't matter.


Confused me for a second, but yeah your right.


Are you sure this isn't a bug with Codepen.io?


It's very unlikely. I can't think of a scenario where it could be changing the CSS and/or HTML that still leaves it syntactically and semantically valid.


Nice one. That's a bug I guess.


does that happen on all browsers?


obscure++


it's a feature not a bug. lol


Oh nice! Bug at international level :-) Looks like it is some kind of stack and overflow in play.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: