Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using universal selector (*) on custom property definitions causes all style recalculations on stackoverflow.com to take at least twice as long #1842

Open
Inwerpsel opened this issue Oct 16, 2024 · 0 comments

Comments

@Inwerpsel
Copy link

Inwerpsel commented Oct 16, 2024

This bug report is about a performance issue in the implementation on stackoverflow.com, not about incorrect behavior. However it has a huge impact and is also easy to address with a simple change. I first considered using SO's internal feedback system, but found this repo to be a much better place for this kind of problem.

The problem

Due to how browsers (at least Chromium) process custom properties, using the universal selector when defining them on the html or body element slows down every recalculation by a factor of at least 2. On some complex pages I found it to make it take almost 4 times longer (~240ms vs ~60ms).

Consider the following performance recording of the home page:
SOslow

Observe that, by far, most time is spent on rendering. In fact you're waiting much, much longer on rendering than on the network requests. Clearly this is the main bottleneck and any improvement to it directly translates to a faster page load. For all SO pages I tested, the vast majority of rendering tasks are style recalculations (e.g. the final large purple bar in the recording is a single style recalculation taking around 200ms).

I found that these recalculation times can be slashed in (at least) half, just by changing the following selector in primary.css

body.unified-theme, body.unified-theme * {
  --theme-base-primary-color-h: 27;
  // ...

to be only

body.unified-theme {
  --theme-base-primary-color-h: 27;
  // ...

On the StackExchange GitHub org I found a source sheet with the same pattern.

Removing the universal selector does not change the visual result, because every element still inherits the values from body. From my own experience I can assure that this is a change you can safely make, and I also confirmed on live pages that the second part of the rule is truly not needed.

Why is this so slow?

While I'm not fully sure about it, it's safe to say that using * to define custom properties puts the browser into a case that it's not optimized for at the moment. I guess it's not optimized as a pattern because it's not needed, since inheritance is exactly made to do this and enabled by default on custom properties.

While there's a chance that the slow performance is an oversight on the browser's end, if the pattern is truly unnecessary here then removing it seems the logical thing to do.

Expected improvement

As a simple POC, I downloaded a random answer page stripped of all scripts. I verified that the local copy had similar recalculation times as the original live page.

I then made another local copy of that page where the only difference is the aforementioned rule change in primary.css.

Here's a screenshot of the recordings side by side.

SObeforeafter

With the fix, time spent rendering went from 326ms to 194ms. A bit harder to see in the screenshots, every individual recalculation in this case was about 3 times faster (first one went from 94ms to 28ms).

That's over 100ms sliced off, and on the live page, which has more recalculations triggered by scripts, the improvement will be bigger, probably several hundreds ms on a desktop.

On lower end devices, I expect this improvement to be much bigger, even 1 second or more on big pages.
You can imagine how such an improvement will reflect in metrics like bounce rate.

Verifying this improves performance on every page

The simplest way to verify this is:

  • trigger a recalculation manually on any SO page and record using dev tools
    You can run following snippet in the console right before pressing the record button:
setTimeout(() => {
    document.documentElement.style.setProperty('--theme-button-color', `blue`)
}, 3000)
  • reload the page so the snippet doesn't set the same value twice (you can also swap out the color to avoid reloading)
  • use dev tools to remove the second part of the rule as mentioned above (inspect any element to find this rule, it's a large list of striked through custom properties repeated for every parent element of any element)
  • run the snippet again and record
  • compare recalculation times in the recordings
@Inwerpsel Inwerpsel changed the title Using universal selector (*) on custom property definitions causes all style recalculations on stackoverflow.com to take at least twice as long Using universal selector (*) on custom property definitions causes all style recalculations on stackoverflow.com to take at least twice as long Oct 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant