Inwerpsel / use-theme-editor

A React theme editor
GNU General Public License v3.0
4 stars 0 forks source link

Reduce style recalculation times when making changes #38

Open Inwerpsel opened 11 months ago

Inwerpsel commented 11 months ago

Whenever you make changes in the editor UI, the CSS rules inside the frame have to be updated, which results in a style recalculation of a portion of the HTML elements.

The most important bottleneck is the amount of elements needing recalculation. Any element that doesn't have visible changes, but is still included in the recalculation, can be seen as "waste".

"Normal CSS" (without custom props) has some waste but generally has good efficiency as you update a rule.

If CSS has many custom properties defined on the root element, each of these will result in a full style recalculation of all elements on the page when they're updated. This is because it's an inheritable property that needs to propagate, and it's very hard for the browser to tell what it's going to be used for or do other optimizations.

Now it's possible that browsers do some degree of optimization, but I didn't observe any meaningful difference in how long any particular full recalc takes. Whether you're swapping out a lot of CSS, or just updating a single custom property, takes about the same amount of time.

Current implementation

At an earlier point, I noticed that style recalc took way less time for Bootstrap, which defines custom props on elements further away from the root. However, some changes to the managed stylesheet probably cause a similar full recalc in many more cases.

I left this problem unaddressed, since the code around selectors will dramatically change anyway. Instead of using an extra sheet, each sheet will be replaced with a "managed" inline style element. That way the changes can affect the smallest scope possible, which should restore the performance.

Optimizing :root scope

On top of this, it's possible to do an optimization specifically for custom props defined in the root scope. Since we're taking full control over the stylesheets, we can keep track of the references and go update each referencing rule individually, leaving the root element unchanged.

While this may seem a bit crazy, the performance difference is night and day and probably not achievable in another way (unless specific optimizations for custom props are done in browsers). Recalculating all elements easily takes 20-40ms on many pages. When properly targeted, this goes down to almost nothing. The result would be something like 20fps going to 100fps.