The truth about CSS selector performance
blogs.windows.com
blogs.windows.com
This is cool. The rest of the article IMHO is just an example runthrough of micro-optimisations that nobody reading should adopt into their own practice _unless_ their own perf analysis indicates a problem. It's incredibly unlikely that real-user-experienced UI slowdowns are due to stuff like this.
It’s not trying to say everyone should adopt those optimizations, but intended to be a hands-on “try it out” without the reader needing to have their own app handy :)
To my original comment, I think I’m just naturally very wary of people losing themselves in the weeds without real cause. If there’s genuine slowdowns being experienced, absolutely dive into it. There are cautionary tales here though. Like when one makes an optimisation based on current browser implementations, only to have it be slower within a couple release cycles because the browsers have updated their implementations. Lots of this occurred with js looping idioms when es5 gained broader usage. All wasted time, in the end. IMHO it’s best to stick to idiomatic patterns and those espoused in the specs themselves. 99% of the time, that’ll cover you.
It’s a question of respect: if you think CSS is a toy language for your inferiors, you probably aren’t going to learn it very well because you’re not going to be receptive to learning the designers’ intent.
"Here's a new dev tool that can point to problems, and here's how you fix it"
> In practice, does it matter? Maybe. This heavily depends on the web page
Don't do this in your codebase. This is maybe the most toxic and chaotic CSS change you can make to a codebase. Some devs will realize it's a box model thing right away. But the others will suffer in silence and confusion for quite a while. Both groups will hate you for this practice.
While I'm sure there are many optimisations already in place, at least we could avoid this anti-pattern that everybody's doing on web these days.
His talk here is also a great deep dive - https://nolanlawson.com/2023/01/17/my-talk-on-css-runtime-pe...
I think Chromium specifically got rid of a tool like this in "Drop the CSS selector profiler"
https://bugs.chromium.org/p/chromium/issues/detail?id=265486
I always feel like browser devs regularly look at the web naively and think "yeah nobody should waste time optimizing for that", but then you get sites having 800kb of CSS and lots of terrible selectors and you have no tools to analyze them besides generic coverage.
Personally, I've never felt overwhelmed by choice when it comes to options regarding optimization. It's fine if it's hidden behind a flag, but "nobody could ever need this" feels weird.
You're right, "I need an OS written in JS to run before I can toggle that menu" dwarfs all of it. I'm lucky in that regard, I usually deal with jQuery-based things; you can still trim 95% of the code if you got rid of it (carousels are stopping me, people love carousels), but even if you don't, you land at like 100kb JS and you can defer it.
What I'd really like is something that'll run a performance recording and allow me to step into it at any given time to see what HTML + CSS is currently in effect, those things have been the most annoying to debug when styles are applied while content is still being parsed and flex is resizing when new columns appear as nodes are added and causing layout shifts.
First assemble all the CSS rules into a tree, where each non-leaf node represents a selector and following a path down the tree corresponds to reading a selector from left to right. For example, if there are two rules, ".foo .bar" and ".foo .baz", the tree root would be ".foo", and it would have children ".bar" and ".baz".
Then give each DOM element a pointer to a style tree node. When an element is inserted into the document, set its style tree node by looking for a matching selector among the children of the parent element's style tree node. For example, <div class="foo"> would be associated with the ".foo" style tree node; if <div class="bar"> is created underneath that, the browser looks up ".bar" among that node's children. If there is no match, then use the same style tree node from the parent.
That way you can find the matching CSS rules for each element at the cost of a single lookup per element, regardless of how deeply the rules are nested.
In reality it's more complicated than that, because an element might actually need multiple style tree positions. If you have ".foo .bar" and "div .bar", ".foo" and "div" would naively be two separate tree nodes (each with a ".bar" child node), but <div class="foo"> would have to be associated with both nodes. In other words, the style tree is an NFA. But just like with regular expression matching, it might be faster to convert it to a DFA in advance, i.e. duplicate nodes as necessary so that each element does only need one position in the tree.
And the need to support efficient incremental updates (e.g. an element could change class name, or style rules themselves could be added/removed) makes everything even more complicated.
Still, if you asked me before today to guess how browsers implemented CSS rule matching, that's the algorithm I would have thought of… I wonder why they don't do this. Maybe deeply nested CSS rules are not that common in practice.