The asteroid currently hitting front end web development

g4k1 pts0 comments

The asteroid currently hitting frontend web development | Read the Tea Leaves

Read the Tea Leaves Software and other dark arts, by Nolan Lawson

Home

Apps

Code

Talks

About

" You can just choose how many bugs you want now

23<br>Aug

The asteroid currently hitting frontend web development

Posted August 23, 2026 by Nolan Lawson in Web. Tagged: AI. 4 Comments

A lot of the educators I admire in the frontend web space seem to be either bowing out or dialing back their efforts: Axel Rauschmayer, Salma Alam-Naylor, Josh W. Comeau, to name a few. Other well-known luminaries like Kent C. Dodds, Addy Osmani, Rachel Nabors, and Lydia Hallie have pivoted from talking about frontend development to talking about… well, take a wild guess.

I never made a living from talking about web dev, but I do have this blog, and I’ve done conference talks and podcasts about stuff I’m excited about. Recently for example, my passion was explaining all the ins and outs of CSS performance: how shadow DOM works, how the browser’s style engine works, the traps of CSS-in-JS, etc.

Just for kicks, I decided to ask Claude Sonnet how it would handle a Chrome trace with high "Style Calculation" costs. This is one of my favorite brain-teasers, because it’s the kind of thing that I saw web devs – even experienced ones! – trip up on over and over again. Claude gave a perfectly admirable answer:

Click to see

Prompt: Answer without searching the internet: you’re looking at a Chrome trace, and you see repeated spans of high Style cost but relatively low Layout cost. What kind of things do you look for in the webapp to optimize? What do you measure next?

What "Style" cost actually represents

The Style (Recalculate Style) phase is where the browser matches CSS selectors against the DOM and computes the final computed style for each affected element. It’s expensive independent of whether anything actually moves or resizes (that’s Layout’s job). So if Style is high but Layout is low, you’re likely paying for selector matching / invalidation over many elements , not for expensive geometry .

What to look for in the webapp

1. Selector complexity and count

Overly complex or deeply nested selectors (.a .b .c >; .d + .e), especially descendant combinators, force the engine to walk more of the DOM/ancestor chain per match attempt.

Universal selectors or attribute selectors ([data-foo="bar"]) applied broadly.

CSS-in-JS libraries that generate huge numbers of unique class names/rules (style recalculation has to consider the whole rule set).

2. Invalidation scope — this is usually the big one

A class/attribute/style toggle high up in the DOM tree (e.g., toggling a class on or a top-level container) can force style recalculation on large subtrees even if only a few elements’ final styles change.

Check whether a single state change (e.g., a "theme" class, a "loading" class, a hover state) is invalidating far more elements than necessary. Chrome’s "Selector Stats" (enable in DevTools Performance settings) will show you which selectors are slow and how many elements they were tested against — this is the single most useful next measurement.

3. Frequency/amplification

Something re-triggering style recalc many times per frame — e.g., reading a style/layout property in a loop right after a class change (forces synchronous recalc), or a JS animation using .classList.toggle() or inline style updates on many nodes per rAF tick instead of batching.

Repeated small DOM mutations each forcing a style pass rather than being batched into one.

4. Inherited/computed property propagation

Changing an inherited property (like font-size or color on a common ancestor) forces recompute of all descendants’ computed styles, even without layout changes (e.g., color, visibility, cursor are Style-only, not Layout-triggering).

Custom properties (CSS variables) changed at a high-level scope — updating a --custom-prop on :root or a shared ancestor recomputes style for every element referencing it.

5. Shadow DOM / component boundaries

If using a framework with lots of shadow roots or scoped styles, check whether style recalculation is being needlessly repeated per component instance due to non-shared stylesheets.

What to measure next

Enable "Selector Stats" in the Performance panel (gear icon → Enable selector stats). Re-record the trace. This gives you, per Recalculate Style event, the top slow selectors and match counts — that tells you directly which CSS rules and how many elements are responsible.

Check the "Recalculate Style" event’s initiator/call stack in the trace to see what JS triggered it — a class toggle, a style.setProperty, an animation frame callback, etc.

Count how many elements are affected per recalc — DevTools shows "Elements Affected" per Recalculate Style event in the summary pane. Compare that number to how many elements actually changed appearance — a big gap indicates over-broad invalidation.

Look at where in the DOM the triggering mutation...

style elements class layout selectors high

Related Articles