loading...
Cover image for My Misconceptions about the Universal Selector in CSS

My Misconceptions about the Universal Selector in CSS

clairecodes profile image Claire Parker-Jones Originally published at clairecodes.com ・4 min read

The asterisk * is known as the universal selector in CSS. It matches all elements of any type.

* {
    color: red;
}

This example would change the colour of all the text on the page to red (as long as there wasn't a more specific rule applied to the element).

I don't often use the universal selector, because I assumed that selecting everything would be bad for page performance and difficult to override. After some research, I proved wrong some misconceptions I had about both the universal selector and my understanding of CSS.

* has a specificity of 0

Misconception 1: the universal selector has a high specificity. ❌ Wrong! The specificity of * is zero. In fact, quoting MDN's article on specificity:

Universal selector (*), combinators (+, >, ~, ' ', ||) and negation pseudo-class (:not()) have no effect on specificity.

Therefore, a rule that only uses the universal selector, like * { color: red }, can be overridden by a single element, class or ID selector.

Estelle Weyl's specifishity page is a great visual guide to understanding CSS specificity.

* is inefficient in terms of performance

Misconception 2: the universal selector performs badly compared to other selectors. ✅ I was right about this one.

When talking about CSS selector performance, Steve Souder's work is often quoted, particularly his book Even Faster Websites and popular blog post about CSS selector performance both from 2009. He has a handy CSS test creator on his website for measuring the performance of CSS selectors. The universal selector is one of the worst performers, alongside attribute selectors (like [href="#"]) and pseudo-classes and pseudo-elements (like :hover). IDs and classes are the best for speedy performance. (Harry Roberts summarises the results in a list in this post about efficient CSS selectors from 2011.)

However, these results are for when you use a single selector alone, and not in combination with any others. You can still write inefficient CSS selectors without *. Using lots of child or descendent selectors is one way to achieve this, e.g. div > ul li > a { color: red; }

CSS selectors are read from right to left by the browser ⬅

Misconception 3: browsers process CSS selectors from left to right, in the same order that I would read them. ❌ I was wrong about this which surprised me!

One of these rules runs faster than the other:

/* A */
#id > *.

/* B */
* > #id {
    color: red;
}

The browser checks the rightmost selector first before moving left. Therefore, selector B is quicker, since the matching elements are narrowed down to those matching the ID, instead of every element in the document like in A.

Therefore, when writing selectors, it's best to put something performant like a class or ID at the end.

CSS Selector Performance matters

Misconception 4: it's important to focus on CSS selector efficiency for page performance. ❌ As I read more and more about this subject, the overriding opinion was that selector performance isn't something to worry about. Steve Souder even said as much in the first paragraph of the aforementioned blog on CSS selector performance. This was written 10 years ago, and since then our browsers have become much more powerful.

However, I still wouldn't recommend writing very overqualified selectors, e.g.

div a [type="text"] .foo #bar {
    color: red;
}

Not because of performance (although this will be inefficient) but because it implies that your DOM is structured badly, and CSS written like this will be hard to maintain and override. Instead, write selectors that match on ideally one class.

Would I use *?

The universal selector isn't one that I reach for often, but it can be useful. It's common to see browser-reset type selectors like:

* , *:before, *:after {
    box-sizing: border-box;
}

The universal selector can also be useful to select all children of an element, or in cases where you don't have direct control over the structure of the page, e.g.

.class > * {
    color: red
}

Now I understand how it works more, I wouldn't be afraid to use *, particularly just for page performance concerns. In a world of weighty hero image PNGs, bloated JavaScript bundles and sluggish API calls, there are more strategic parts of a website to optimise for bigger performance gains.

Instead, if I did need to use the universal selector, I would evaluate the reason: could this CSS be written in a clearer more maintainable way? Or could the page structure could be refactored to make it easier to target the required elements?

Do you use the universal selector often or do you avoid it?

Header image by Jeremy Thomas on Unsplash.

Posted on by:

Discussion

pic
Editor guide
 

Thank you for writing about this! I learned a lot from it. I still wouldn't use the * selector (for the reasons you stated in the article) unless I intend to use it with the box-sizing property. For optimization, I'd much rather restructure my DOM than to use a "universal solution".

 

The universal selector can be very useful if you are creating a 3rd party widget for any other website to use and you don't want their css to affect you. Then you can do something like:

#my-widget > *
{
    position: relative !important;
    padding: 0 !important;
    margin: 0 !important;
    .. etc ..
}

Which can act like css reset just for the elements contained within the widget you are creating.

 

Nice, that's a really good workaround for specificity issues!

 

What about shadow-dom?

 

When you have a few elements in your document the performance consideration of * is mostly irrelevant. In your shadow Dom you're in a document fragment and the same applies. Now, if you have lots of elements in your shadow Dom, you likely should reconsider how you split your components.

 

Yes, but I was more commenting on how '*' doesn't apply to everything since it doesn't pierce shadow-roots. ie 'Universal' itself, is sort of a misconception. It is sort of obvious, but it's worth pointing out, maybe.

Ugh, got it :-)

But that actually depends: many CSS properties are inherited from the host node into the shadow DOM, if not overwritten in the shadow DOMs style. So in many cases '*' will apply to everything, though it will not select everything. E.g. querySelectorAll will not return stuff from document fragments, but a CSS rule with that selector may still apply to stuff in the fragments through inheriting.

No idea what that does to performance, though :D

Oh, one more comment on this:
Currently web components are predominantly used as a way of organizing stuff in web applications. In these cases the question is of little relevance since such applications usually have little global style and a lot compartementalized styles.
However, in the future web components are envisioned to also or primarily serve as independent re-usable widgets. Now if you have dozens of independent web components on a page and then mess around with '*' on the top level, you are probably asking for all kinds of trouble plus a potential performance impact.

Well, that is interesting. I just tried this on one of my apps by setting font-style to italics, and it affected some stuff inside my elements. I wonder how I missed that...I wonder if it was like that in v0 shadow-dom.
Yeah, afaik css custom properties have been the recommended way of changing style inside shadow-dom - I wonder if that performs better...and there's upcoming features more applicable to theming, right? (I've been out of the loop for a little while).

Yes, shadow parts. w3.org/TR/css-shadow-parts-1/
But that looks like it will take some time. There was another proposal for theming, @apply I think, but it turned out, that that doesn't really fly. Hence the delay. Shadow parts seems pretty clever and comprehensive, though. Let's hope :)

Right, I use @apply quite a lot since it's part of Polymer - v1 at least. It works as far as it goes, but I'm not sure it completely satisfies the theming use-case - well, not on its own anyway. Vaadin have a theming system which sounded quite clean - they did a presentation on that prior to the last Polymer summit - and I think there was a presentation on an alternative in the summit itself, iirc (perhaps it was the shadow parts thing, I forget).
We'll see :)

IIRC correctly Vaadin basically went ahead and support shadow parts for their components, but I'm not sure, quite some time, since I looked at it. The problem with apply is, that it gives the consumer no way to discern styles for stuff like :hover or parent classes. The comment developer has to invest a lot of thought and development to allow for such use cases.

Very interesting, thanks.

 

The main reason why I use the * selector is for quick-and-dirty debugging of a page to figure out why some nesting isn't behaving the way I thought it would (using e.g. * { background-color: rgba(255,0,0,0.1); } or * { border: solid red 1px; } or the like), although now that all of the browsers have a good element inspector that's a lot less useful.

 

I avoid the global selector. In the past I made something like

* {
padding:0;
margin:0;
}

Now I use body and have the wished effect.

 

Nice research! Thank you

 

Really nice post Claire, informative, short and neat!

 

id > *. A typo here. And then I believe this is superfluous. You only need #id * the cascade does the work. Last, * > #id same thing, you only need #.