Speedier tunes

I wrote a little while back about improving performance on The Session by reducing runtime JavaScript in favour of caching on the server. This is on the pages for tunes, where the SVGs for the sheetmusic are now inlined instead of being generated on the fly.

It worked. But I also wrote:

A page like that with lots of sheetmusic and plenty of comments is going to have a hefty page weight and a large DOM size. I’ve still got a fair bit of main-thread work happening, but now the bulk of it is style and layout, whereas previously I had the JavaScript overhead on top of that.

Take a tune like Out On The Ocean. It has 27 settings. That’s a lot of SVG markup that needs to be parsed, styled and rendered, even if it’s inline.

Then I remembered a very handy CSS property called content-visibility:

It enables the user agent to skip an element’s rendering work (including layout and painting) until it is needed — which makes the initial page load much faster.

Sounds great! But there are two gotchas.

The first gotcha is that if a browser doesn’t paint the element, it doesn’t know how much space the element should take up. So you need to provide dimensions. At the very least you need to provide a height value. Otherwise when the element comes into view and gets rendered, it pushes down on the content below it. You’d see a sudden jump in the scrollbar position.

The solution is to provide a value for contain-intrinsic-size. If your content is dynamic—from, say, a CMS—then you’re out of luck. You don’t know how long the content is.

Luckily, in my case, I could take a stab at it. I know how many lines of sheetmusic there are for each tune setting. Each line takes up roughly the same amount of space. If I multiply that amount of space by the number of lines then I’ve got a pretty good approximation of the height of the sheetmusic. I apply this with the contain-intrinsic-block-size property.

So each piece of sheetmusic has an inline style attribute with declarations like this:

content-visibility: auto;
contain-intrinsic-block-size: 380px;

It works a treat. I did a before-and-after check with pagespeed insights on the page for Out On The Ocean. The “style and layout” part of the main thread work went down considerably. Total blocking time went from more than 600 milliseconds to less than 400 milliseconds.

Not a bad result for a little bit of CSS!

I said there was a second gotcha. That’s browser support.

Right now content-visibility is only supported in Chrome and Edge. But that’s okay. This is a progressive enhancement. Adding this CSS has no detrimental effect on the browsers that don’t understand it (and when they do ship support for it, it’ll just start working). I’ve said it before and I’ll say it again: the forgiving error-parsing in HTML and CSS is a killer feature of the web. Browsers just ignore what they don’t understand. That’s what makes progressive enhancement like this possible.

And actually, there’s something you can do for all browsers. Even browsers that don’t support content-visibility still understand containment. So they’ll understand contain-intrinsic-size. Pair that with a contain declaration like this to tell the browser that this chunk of content isn’t going to reflow or get repainted:

contain: layout paint;

Here’s what MDN says about contain:

The contain CSS property indicates that an element and its contents are, as much as possible, independent from the rest of the document tree. Containment enables isolating a subsection of the DOM, providing performance benefits by limiting calculations of layout, style, paint, size, or any combination to a DOM subtree rather than the entire page.

So if you’ve got a chunk of static content, you might as well apply contain to it.

Again, not bad for a little bit of CSS!

Have you published a response to this? :

Responses

1 Like

# Liked by nrk 9819 on Monday, February 19th, 2024 at 4:42pm

Related posts

Progressively enhancing maps

How I switched to high-resolution maps on The Session without degrading performance.

Supporting logical properties

Using the CSS trinity of feature queries, logical properties, and unset.

Let’s get logical

Let me hear your blocky talk.

Media queries with display-mode

I never would’ve known about the `display-mode` media feature if I hadn’t been writing about it.

Programming CSS to perform Sass colour functions

Combining custom properties, hsl(), and calc() to get cascading button styles.

Related links

abc to SVG | CSS-Tricks

Aw, this is so nice! Chris points to the way that The Session generates sheet music from abc text:

The SVG conversion is made possible entirely in JavaScript by an open source library. That’s the progressive enhancement part. Store and ship the basic format, and let the browser enhance the experience, if it can (it can).

Here’s another way of thinking of it: I was contacted by a blind user of The Session who hadn’t come across abc notation before. Once they realised how it worked, they said it was like having alt text for sheet music! 🤯

Tagged with

Performance-Optimized Video Embeds with Zero JavaScript – Frontend Masters Blog

This is a clever technique for a CSS/HTML only way of just-in-time loading of iframes using details and summary.

Tagged with

JS-heavy approaches are not compatible with long-term performance goals

Frameworks like React are often perceived as accelerators, or even as the only sensible way to do web development. There’s this notion that a more “modern” stack (read: JS-heavy, where the JS ends up running on the user’s browser) allows you to be more agile, release more often with fewer bugs, make code more maintainable, and ultimately launch better sites. In short, the claim is that this approach will offer huge improvements to developer experience, and that these DevEx benefits will trickle down to the user.

But over the years, this narrative has proven to be unrealistic, at best. In reality, for any decently sized JS-heavy project, you should expect that what you build will be slower than advertised, it will keep getting slower over time while it sees ongoing work, and it will take more effort to develop and especially to maintain than what you were led to believe, with as many bugs as any other approach.

Where it comes to performance, the important thing to note is that a JS-heavy approach (and particularly one based on React & friends) will most likely not be a good starting point; in fact, it will probably prove to be a performance minefield that you will need to keep revisiting, risking a detonation with every new commit.

Tagged with

The Main Thread Is Not Yours — Den Odell

Every millisecond you spend executing JavaScript is a millisecond the browser can’t spend responding to a click, updating a scroll position, or acknowledging that the user did just try to type something. When your code runs long, you’re not causing “jank” in some abstract technical sense; you’re ignoring someone who’s trying to talk to you.

This is a great way to think about client-side JavaScript!

Also:

Before your application code runs a single line, your framework has already spent some of the user’s main thread budget on initialization, hydration, and virtual DOM reconciliation.

Tagged with

NoLoJS: Reducing the JS Workload with HTML and CSS - Web Performance Calendar

You might not need (much) JavaScript for these common interface patterns.

While we all love the power and flexibility JS provides, we should also respect it, and our users, by limiting its use to only what it needs to do.

Yes! Client-side JavaScript should do what only client-side JavaScript can do.

Tagged with

Previously on this day

3 years ago I wrote These were my jams

Reminiscing about This Is My Jam.

5 years ago I wrote Design engineer

It’s snappier than front-of-the-front-end developer.

7 years ago I wrote Interaction 19

Making the best of a so-so conference.

10 years ago I wrote Enhance! Conf!

The first conference dedicated to progressive enhancement.

10 years ago I wrote New edition

The second edition of HTML5 of Web Designers

16 years ago I wrote Music::Business

Three ages of lunacy.

17 years ago I wrote Rambling

Speaking and travelling.

18 years ago I wrote Fanning the flames

Version targeting again. And again. And again.

22 years ago I wrote iLove the iLife I iLive

On Monday, I placed an order at the Apple Store online. The delivery time was estimated at three to seven working days. My order showed up within 48 hours.

23 years ago I wrote Planes, trains and broadband

This live, literally on-air, description of Lufthansa’s experimental flights with broadband access mirrors my own frustrations with the ludicrous idea of using a pop-up window as a control mechanism:

24 years ago I wrote Ch-ch-ch-changes

I think it’s high time we had a new CSS theme here to brighten the place up a little.

24 years ago I wrote Mac voyeurs

ZDNet has created a monster.