performance – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Tue, 07 Feb 2023 15:59:42 +0000 en-US hourly 1 https://wordpress.org/?v=6.2.2 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 performance – CSS-Tricks https://css-tricks.com 32 32 45537868 The truth about CSS selector performance https://css-tricks.com/the-truth-about-css-selector-performance/ Tue, 07 Feb 2023 15:59:35 +0000 https://css-tricks.com/?p=376659 Geez, leave it to Patrick Brosset to talk CSS performance in the most approachable and practical way possible. Not that CSS is always what’s gunking up the speed, or even the lowest hanging fruit when it comes to improving …


The truth about CSS selector performance originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Geez, leave it to Patrick Brosset to talk CSS performance in the most approachable and practical way possible. Not that CSS is always what’s gunking up the speed, or even the lowest hanging fruit when it comes to improving performance.

But if you’re looking for gains on the CSS side of things, Patrick has a nice way of sniffing out your most expensive selectors using Edge DevTools:

  • Crack open DevTools.
  • Head to the Performance Tab.
  • Make sure you have the “Enable advanced rendering instrumentation” option enabled. This tripped me up in the process.
  • Record a page load.
  • Open up the “Bottom-Up” tab in the report.
  • Check out your the size of your recalculated styles.
DevTools with Performance tab open and a summary of events.

From here, click on one of the Recalculated Style events in the Main waterfall view and you’ll get a new “Selector Stats” tab. Look at all that gooey goodness!

Now you see all of the selectors that were processed and they can be sorted by how long they took, how many times they matched, the number of matching attempts, and something called “fast reject count” which I learned is the number of elements that were easy and quick to eliminate from matching.

A lot of insights here if CSS is really the bottleneck that needs investigating. But read Patrick’s full post over on the Microsoft Edge Blog because he goes much deeper into the why’s and how’s, and walks through an entire case study.

To Shared LinkPermalink on CSS-Tricks


The truth about CSS selector performance originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
376659
Setting Up CloudFront to Host Your Web App https://css-tricks.com/setting-up-cloudfront-to-host-your-web-app/ https://css-tricks.com/setting-up-cloudfront-to-host-your-web-app/#comments Thu, 28 Apr 2022 14:42:48 +0000 https://css-tricks.com/?p=365413 In my last article, we went over how to set up a web app that serves chunks and bundles of CSS and JavaScript from CloudFront. We integrated it into Vite so that when the app runs in a browser, …


Setting Up CloudFront to Host Your Web App originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
In my last article, we went over how to set up a web app that serves chunks and bundles of CSS and JavaScript from CloudFront. We integrated it into Vite so that when the app runs in a browser, the assets requested from the app’s root HTML file would pull from CloudFront as the CDN.

While CloudFront’s edge caching does offer benefits, serving your app’s resources from these multiple locations is not without a cost of its own. Let’s take a look at a WebPageTest trace of my own web app, running with the configuration from the last blog post.

Notice the large connection times for lines 2-4. Line 1 is our HTML entry point. That HTML is parsed, the browser sees script and link tags for the JavaScript and CSS assets that reside on the CDN, and requests them. This causes a new connection to be set up which, as you can see, takes time.

This post will show you how to get around this. We’ll walk through how to host the entire web app on CloudFront and have CloudFront forward — or “proxy” — non-cacheable requests for data, auth, etc., onto our underlying web server.

Note that this is substantially more work than what we saw in the last article, and the instructions are likely to be different for you based on the exact needs of your web app, so your mileage may vary. We’ll be changing DNS records and, depending on your web app, you may have to add some cache headers in order to prevent certain assets from ever being cached. We’ll get into all of this!

You may be wondering whether the setup we covered in the last article even offers any benefits because of what we’re doing here in this article. Given the long connection time, would we have been better off forgoing the CDN, and instead serve all our assets from the web server to avoid that longer wait? I measured this with my own web app, and the CDN version, above, was indeed faster, but not by a lot. The initial LCP page load was about 200-300ms faster. And remember, that’s just for the initial load. Once this connection has been set up, edge caching should add much more value for all your subsequent, asynchronously loaded chunks.

Setting up our DNS

Our end goal is to serve our entire web app from CloudFront. That means when we hit our domain, we want the results to come from CloudFront instead of whatever web server it’s currently linked to. That means we’ll have to modify our DNS settings. We’ll use AWS Route 53 for this.

I’m using mydemo.technology as an example, which is a domain I own. I’ll show you all the steps here. But by the time you read this, I’ll have removed this domain from my web app. So, later when I start showing you actual CNAME records, and similar, those will no longer exist.

Go to the Route 53 homepage, and click on hosted zones:

Showing the hosted zone configuration screen in the CloudFront settings.

Click Create hosted zone and enter the app’s domain:

Now, take note of the name servers listed in the next screen. They should look something like this.

We haven’t really accomplished anything yet. We told AWS we want it to manage this domain for us, and AWS gave us the name servers it’ll route our traffic through. To put this into effect, we need to go to wherever our domain is registered. There should be a place for you to enter in your own custom name servers.

Note that my domain is registered with GoDaddy and that is reflected in the screenshots throughout this article. The UI, settings, and options may differ from what you see in your registrar.

Warning: I recommend writing down the original name servers as well as any and all DNS records before making changes. That way, should something fail, you have everything you need to roll back to how things were before you started. And even if everything works fine, you’ll still want to re-add any other records into Route 53, ie MX records, etc.

Setting up a CloudFront distribution

Let’s make a CloudFront distribution to host our web app. We covered the basics in the last post, so we’ll get right to it. One big change from last time is what we enter for the origin domain. Do not put the top-level domain, e.g. your-app.net. What you need is the underlying domain where your app is hosted. If that’s Heroku, then enter the URL Heroku provides you.

Next, be sure to change the default protocol if you plan to use this site over a secure HTTPS connection:

This part is crucial. If your web app is running authentication, hosting data, or anything else, be sure to enable other verbs besides GET. If you skip this part, then any POST requests for authentication, mutating data, etc., will be rejected and fail. If your web app is doing nothing but serving assets and all those things are handled by external services, then outstanding! You have a great setup, and you can skip this step.

We have to make quite a few changes to the cache key and origin requests settings compared to last time:

We need to create a cache policy with a minimum TTL of 0, so non-caching headers we send back will are properly respected. You may also want to enable all query strings. I was seeing weird behavior when multiple GraphQL requests went out together with different query strings, which were ignored, causing all these requests to appear identical from CloudFront’s perspective.

My policy wound up looking like this:

For an origin request policy, if needed, we should make sure to send query strings and cookies for things like authentication and data queries to work. To be clear, this determines whether cookies and query strings will be sent from CloudFront down to your web server (e.g. Heroku, or similar).

Mine looks like this:

Lastly, for response headers policy, we can select “CORS With Preflight” from the list. In the end, your first two will have different names depending on how you set them up. But mine looks like this:

Let’s connect our domain, whatever it is, to this CloudFront distribution. Unfortunately, this is more work than you might expect. We need to prove to AWS that we actually own the domain because, for all Amazon knows, we don’t. We created a hosted zone in Route 53. And we took the nameservers it gave us and registered them with GoDaddy (or whoever your domain is registered with). But Amazon doesn’t know this yet. We need to demonstrate to Amazon that we do, in fact, control the DNS for this domain.

First, we’ll request an SSL certificate.

Next, let’s request the certificate link:

Now, we’ll select the option to request a public certificate option:

We need to provide the domain:

And, in my case, the certificate is pending:

So, I’m going to click it:

This proves that we own and control this domain. In a separate tab, go back to Route 53, and open our hosted zone:

Now we need to create the CNAME record. Copy the first part for the Record name. For example, if the CNAME is _xhyqtrajdkrr.mydemo.technology, then put the _xhyqtrajdkrr part. For the Record value, copy the entire value.

Assuming you registered the AWS name servers with your domain host, GoDaddy or whomever, AWS will soon be able to ping the DNS entry it just asked you to create, see the response it expects, and validate your certificate.

It can take time for the name servers you set at the beginning to propagate. In theory, it can take up to 72 hours, but it usually updates within an hour for me.

You’ll see success on the domain:

…as well as the certificate:

Whew! Almost done. Now let’s connect all of this to our CloudFront distribution. We can head back to the CloudFront settings screen. Now, under custom SSL certificate, we should see what we created (and any others you’ve created in the past):

Then, let’s add the app’s top-level domain:

All that’s left is to tell Route 53 to route our domain to this CloudFront distribution. So, let’s go back to Route 53 and create another DNS record.

We need to enter an A record for IPv4, and an AAAA record for IPv6. For both, leave the record name empty since we’re only registering our top-level domain and nothing else.

Select the A record type. Next, specify the record as an alias, then map the alias to the CloudFront distribution. That should open up an option to choose your CloudFront distribution, and since we previously registered the domain with CloudFront, you should see that distribution, and only that distribution when making a selection.

We repeat the exact same steps for the AAAA record type we need for IPv6 support.

Run your web app, and make sure it actually, you know, works. It should!

Things to test and verify

OK, while we’re technically done here, chances are there are still a few things left to do to meet the exact needs of your web app. Different apps have different needs and what I’ve demonstrated so far has walked us through the common steps to route things through CloudFront for better performance. Chances are there are things unique to your app that require more love. So, for that, let me cover a few possible additional items you might encounter during setup.

First off, make sure any POSTs you have are correctly sent to your origin. Assuming CloudFront is correctly configured to forward cookies to your origin, this should already work but there’s no harm in checking.

The bigger concern are all other GET requests that are sent to your web app. By default, any GET requests CloudFront receives, if cached, are served to your web app with the cached response. This can be disastrous. Any data requests to any REST or GraphQL endpoints sent with GET are cached by the CDN. And if you’re shipping a service worker, that will be cached too, instead of the normal behavior, where the current version is sent down in the background and updated if there are changes.

In order to tell CloudFront not to cache certain things, be sure to set the "Cache-Control" header to "no-cache" . If you’re using a framework, like Express, you can set middleware for your data access with something like this:

app.use("/graphql", (req, res, next) => {
  res.set("Cache-Control", "no-cache");
  next();
});
app.use(
  "/graphql",
  expressGraphql({
    schema: executableSchema,
    graphiql: true,
    rootValue: root
  })
); 

For things like service workers, you can put specific rules for those files before your static middleware:

app.get("/service-worker.js", express.static(__dirname + "/react/dist", { setHeaders: resp => resp.set("Cache-Control", "no-cache") }));
app.get("/sw-index-bundle.js", express.static(__dirname + "/react/dist", { setHeaders: resp => resp.set("Cache-Control", "no-cache") }));
app.use(express.static(__dirname + "/react/dist", { maxAge: 432000 * 1000 * 10 }));

And so on. Test everything thoroughly because there’s so much that can go wrong. And after each change you make, be sure to run a full invalidation in CloudFront and clear the cache before re-running your web app to test that things are correctly excluded from cache. You can do this from the Invalidations tab in CloudFront. Open that up and put /* in for the value, to clear everything.

A working CloudFront implementation

Now that we have everything running, let’s re-run our trace in WebPageTest:

And just like that, we no longer have setup connections like we saw before for our assets. For my own web app, I was seeing a substantial improvement of 500ms in LCP. That’s a solid win!


Hosting an entire web app on a CDN can offer the best of all worlds. We get edge caching for static resources, but without the connection costs. Unfortunately, this improvement doesn’t come for free. Getting all of the necessary proxying correctly set up isn’t entirely intuitive, and then there’s still the need to set up cache headers in order to avoid non-cacheable requests from winding up in the CDN’s cache.


Setting Up CloudFront to Host Your Web App originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/setting-up-cloudfront-to-host-your-web-app/feed/ 1 365413
7 Fresh Links on Performance For March 2022 https://css-tricks.com/performance-links-february-2022/ https://css-tricks.com/performance-links-february-2022/#respond Wed, 02 Mar 2022 21:26:50 +0000 https://css-tricks.com/?p=364350 I have a handful of good links to articles about performance that are burning a hole in my bookmarks folder, and wanna drop them here to share.

The new WebPageTest website design


7 Fresh Links on Performance For March 2022 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I have a handful of good links to articles about performance that are burning a hole in my bookmarks folder, and wanna drop them here to share.

Screenshot of the new WebPageTest homepage, a tool for testing performance metrics.
The new WebPageTest website design

7 Fresh Links on Performance For March 2022 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/performance-links-february-2022/feed/ 0 364350
Using the CSS Me Not Bookmarklet to See (and Disable) CSS Files https://css-tricks.com/css-me-not-bookmarklet/ https://css-tricks.com/css-me-not-bookmarklet/#comments Sat, 15 Jan 2022 00:06:56 +0000 https://css-tricks.com/?p=360928 Stoyan is absolutely correct. As much as we all love CSS, it’s still an important player in how websites load and using less of it is a good thing. He has a neat new bookmarklet called CSS Me Not …


Using the CSS Me Not Bookmarklet to See (and Disable) CSS Files originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Stoyan is absolutely correct. As much as we all love CSS, it’s still an important player in how websites load and using less of it is a good thing. He has a neat new bookmarklet called CSS Me Not to help diagnose unnecessary CSS files, but we’ll get to that in a moment.

The [problem] is that CSS is in the critical path, it blocks rendering and often even JavaScript execution. We love CSS, it’s magic, it can do unbelievable feats and fix broken UIs and manipulate images and draw amazing pictures. We love CSS. We just want… less of it, because of its inherently blocking nature.

Sometimes our sites use entire stylesheets that are simply unnecessary. I hate to admit it, but WordPress is a notorious offender here, loading stylesheets for plugins and blocks that you might not even really be using. I’m in that position on this site as I write. I just haven’t found the time to root out a couple of little stylesheets I don’t need from loading.

Stoyan created a quick bookmarklet called CSS Me Not to see all those CSS files. The big benefit, of course, is that it lets you know what you’re up against.

You could find these stylesheets in DevTools as well, but the CSS Me Not bookmarklet makes it extra easy and has a killer bonus feature: turning off those stylesheets. Testing the bookmarklet here on CSS-Tricks, I can see four stylesheets that WordPress loads (because of settings and plugins) that I know I don’t need.

Screenshot of a Chrome browser window showing the CSS Me Not bookmarklet circled in red just below the address bar, Below that is a table injected above the CSS-Tricks website showing six stylesheets including an action to disable a sheet, the sheet's media, the sheet's host, and the sheet's name.

If you wanted to do this in DevTools instead, you could filter your Network requests by CSS, find the stylesheet that you want to turn off, right-click and block it, and re-load.

DevTools window screenshot with the Network panel open and the select menu open on a listed stylsheet with the option to block the request URL highlighted in bright blue.

I’ve been fighting this fight for ages, dequeuing scripts and styles in WordPress that I don’t want.

Removing totally unused stylesheets is an obvious win, but there is the more squirrely issue of removing unused CSS. I mention in that post the one-true-way of really knowing if any particular CSS is unused, which is attaching a background-image to every selector and then checking the server logs after a decent amount of production time to see which of those images were never requested. Stoyan corroborates my story here:

UnCSS is sort of a “lab”. The “real world” may surprise you. So a trick we did at SomeCompany Inc. was to instrument all the CSS declarations at build time, where each selector gets a 1×1 transparent background image. Then rummage through the server logs after a week or so to see what is actually used.


Using the CSS Me Not Bookmarklet to See (and Disable) CSS Files originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/css-me-not-bookmarklet/feed/ 5 360928
Links on Performance V https://css-tricks.com/links-on-performance-v/ https://css-tricks.com/links-on-performance-v/#comments Tue, 21 Dec 2021 18:42:43 +0000 https://css-tricks.com/?p=359146
  • Does shadow DOM improve style performance? — Nolan Lawson covers how, because of the inherent encapsulation of the shadow DOM, the styling gets applied a bit faster than it would if those styling rules were relevant to the entire page. But

  • Links on Performance V originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
  • Does shadow DOM improve style performance? — Nolan Lawson covers how, because of the inherent encapsulation of the shadow DOM, the styling gets applied a bit faster than it would if those styling rules were relevant to the entire page. But as ever, it depends, and it turns out that classes and IDs are actually faster outside of it (?!), so if you just style with those, that’s the way to go.
  • HTML-first, JavaScript last: the secret to web speed! — Miško Hevery is talking about Qwik and one of the things that makes it fast. I don’t know much about Qwik, but it looks like a JavaScript framework that is pretty SSR-friendly as it keeps state directly in the HTML/DOM (e.g. div ::app-state="./AppState" app-state:1234="{count: 321}">), which you would think is slow, but apparently isn’t.
  • Small Bundles, Fast Pages: What To Do With Too Much JavaScript — Some thoughts from Ben Schwarz on what to do with those problematic big bundles. Make ’em smaller is the main game, then constantly keep an eye on them.
  • Getting Your head Straight: A New CSS Performance Diagnostics Snippet — We covered Harry’s super clever snippet a while back, but Vitaly had beaten us to it and it’s worth re-sharing because it’s awfully cool.
  • New HTTP standards for caching on the modern web — Tim Perry gets into two brand new (proposed) caching headers: Cache-Status and Targetted Cache-Control. “These are designed to update HTTP standards to match the reality of the CDN-powered web that exists today, creating specifications that formalize existing practices from popular CDNs.” Feels like good stuff where web standards can come in and fix up a gnarly situation.
  • Optimizing resource loading with Priority Hints — Leena Sohoni, Addy Osmani, and Patrick Meenan talk about the Priority Hints…. uh… I’d call it an API except it’s really just an importance attribute in HTML, or a JavaScript param in other APIs. Here’s the draft spec. It’s one of those things, like responsive images with srcset/sizes or will-change, where you, the-author, know more than the browser does and allows you to give the browser information it needs to perform better.
  • Have Core Web Vitals made the web faster? — CWVs were announced in May 2020 and then we were told by May 2021 they would become a factor in SEO. Barry Pollard checks in to see if that big carrot (or stick?) has enticed us all to make sites faster en masse. The answer is complicated because the way CWVs are measured has changed in that time. Some measurements say yes, things are better. But if you use steady alternate measurements from before/after, things look worse. I all depends on what slice of the web you’re looking at.
  • Improving performance with Islands Architecture and PostCSS — Astro has a cool feature for components—like <Sidebar client:media={"(min-width: 768px)"} />—where that element isn’t loaded at all unless that media query matches. I might argue that’s only a good idea if it’s a heavy component or has JavaScript it requires. I say that because it seems like using it for something like static HTML would actually offset any potential savings, as extra JavaScript has to run to test the media conditions and then conditionally fetch assets. But anyway, Oliver Turner demonstrates how to share the media queries between your JavaScript and CSS (via PostCSS).
  • Image Optimizer — This is an Electron-powered (but macOS only) drag-and-drop image optimizer. Like ImageOptim, I suppose. Both are free.

  • Links on Performance V originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/links-on-performance-v/feed/ 1 359146
    Low framerate in Safari on M1 Mac https://css-tricks.com/low-framerate-in-safari-on-m1-mac/ https://css-tricks.com/low-framerate-in-safari-on-m1-mac/#comments Fri, 03 Dec 2021 21:44:40 +0000 https://css-tricks.com/?p=358206 John James Jacoby:

    I recently noticed that animations in Safari were stuttering pretty badly on my M1 powered 2020 MacBook Air, and dove in to figure out why.

    The why:

    This wasn’t a bug. This was a feature.


    Low framerate in Safari on M1 Mac originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    John James Jacoby:

    I recently noticed that animations in Safari were stuttering pretty badly on my M1 powered 2020 MacBook Air, and dove in to figure out why.

    The why:

    This wasn’t a bug. This was a feature.

    By default, macOS Monterey enables “Low power mode” on Battery power and disables it when using a Power Adapter. Safari, it seems, is programmed to interpret this setting to mean that it should reduce the number of times it paints to the screen to prolong battery life.

    On my MacBook Air, that means from 60fps to 30fps.

    To Shared LinkPermalink on CSS-Tricks


    Low framerate in Safari on M1 Mac originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/low-framerate-in-safari-on-m1-mac/feed/ 5 358206
    When is it “Right” to Reach for contain and will-change in CSS? https://css-tricks.com/when-is-it-right-to-reach-for-contain-and-will-change-in-css/ https://css-tricks.com/when-is-it-right-to-reach-for-contain-and-will-change-in-css/#comments Fri, 26 Nov 2021 20:43:05 +0000 https://css-tricks.com/?p=357271 I’ve got some blind spots in CSS-related performance things. One example is the will-change property. It’s a good name. You’re telling the browser some particular property (or the scroll-position or content) uh, will, change:

    .el {
      will-change: opacity;
    


    When is it “Right” to Reach for contain and will-change in CSS? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    I’ve got some blind spots in CSS-related performance things. One example is the will-change property. It’s a good name. You’re telling the browser some particular property (or the scroll-position or content) uh, will, change:

    .el {
      will-change: opacity;
    }
    .el.additional-hard-to-know-state {
      opacity: 0;
    }

    But is that important to do? I don’t know. The point, as I understand it, is that it will kick .el into processing/rendering/painting on the GPU rather than CPU, which is a speed boost. Sort of like the classic transform: translate3d(0, 0, 0); hack. In the exact case above, it doesn’t seem to my brain like it would matter. I have in my head that opacity is one of the “cheapest” things to animate, so there is no particular benefit to will-change. Or maybe it matters noticeably on some browsers or devices, but not others? This is front-end development after all.

    There was a spurt of articles about will-change around 2014/2015 that warn about weird behavior, like unexpected changes in stacking contexts and being careful not to use it “too much.” There was also advice spreading around that you should never use this property directly in CSS stylesheets; you should only apply it in JavaScript before the state change, then remove it after you no longer need it.

    I have no idea if any of those things are still true. Sorry! I’d love to read a 2022 deep dive on will-change. We’re capable of that kind of testing, so I’ll put it in the idea pile. But my point is that there are things in CSS that are designed explicitly for performance that are confusing to me, and I wish I had a more full understanding of them because they seem like Very Big Deals.

    Take “How I made Google’s data grid scroll 10x faster with one line of CSS” by Johan Isaksson. A 10✕ scrolling performance improvement is a massive deal! Know how they fixed it?

    […] as I was browsing the “Top linking sites” page I noticed major scroll lag. This happens when choosing to display a larger dataset (500 rows) instead of the default 10 results.

    […]

    So, what did I do? I simply added a single line of CSS to the <table> on the Elements panel, specifying that it will not affect the layout or style of other elements on the page

    table {
      contain: strict; 
    }

    The contain property is another that I sort of get, but I’d still call it a blind spot because my brain doesn’t just automatically think of when I could (or should?) use it. But that’s a bummer, because clearly I’m not building interfaces as performant as I could be if I did understand contain better.

    There’s another! The content-visibility property. The closest I came to understanding it was after watching Jake and Surma’s video on it where they used it (along with contain-intrinsic-size and some odd magic numbers) to dramatically speed up a long page. What hasn’t stuck with me is when I should use it on my pages.

    Are all three of these features “there if you need them” features? Is it OK to ignore them until you notice poor performance on something (like a massive page) and then reach for them to attempt to solve it? Almost “don’t use these until you need them,” otherwise you’re in premature optimization territory. The trouble with that is the classic situation where you won’t actually notice the poor performance unless you are very actively testing on the lowest-specced devices out there.

    Or are these features “this is what modern CSS is and you should be thinking of them like you think of padding” territory? I kind of suspect it’s more like that. If you’re building an element you know won’t change in certain ways, it’s probably worth “containing” it. If you’re building an element you know will change in certain ways, it’s probably worth providing that info to browsers. If you’re building a part of page you know is always below the fold, it’s probably worth avoiding the paint on it. But personally, I just don’t have enough of this fully grokked to offer any solid advice.


    When is it “Right” to Reach for contain and will-change in CSS? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/when-is-it-right-to-reach-for-contain-and-will-change-in-css/feed/ 7 357271
    Building an Angular Data Grid With Filtering https://css-tricks.com/building-an-angular-data-grid-with-filtering/ https://css-tricks.com/building-an-angular-data-grid-with-filtering/#comments Thu, 28 Oct 2021 14:03:11 +0000 https://css-tricks.com/?p=354782 (This is a sponsored post.)

    Kendo UI makes it possible to go from a basic idea to a full-fledged app, thanks to a massive component library. We’re talking well over 100 components that are ready for you to drop …


    Building an Angular Data Grid With Filtering originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    (This is a sponsored post.)

    Kendo UI makes it possible to go from a basic idea to a full-fledged app, thanks to a massive component library. We’re talking well over 100 components that are ready for you to drop into your app at will, whether it’s React, Angular or Vue you’re working in — they just work. That is because Kendo UI is actually a bundle of four JavaScript libraries, each built natively for their respective framework. But more than that, as we’ve covered before, the components are super themeable to the extent that you can make them whatever you want.

    But here’s the real kicker with Kendo UI: it takes care of the heavy lifting. The styling is great and all, but what separates Kendo UI from other component frameworks is the functionality it provides right out of the box.

    Case in point: data. Rather than spending all your time figuring out the best way to bind data to a component, that’s just a given which ultimately allows you to focus more of your time on theming and getting the UI just right.

    Perhaps the best way to see how trivial Kendo UI makes working with data is to see it in action, so…

    Let’s look at the Angular Grid component

    This is the Kendo UI for Angular Data Grid component. Lots of data in there, right? We’re looking at a list of employees that displays a name, image, and other bits of information about each person.

    Like all of Kendo UI’s components, it’s not like there’s one data grid component that they square-pegged to work in multiple frameworks. This data grid was built from scratch and designed specifically to work for Angular, just as their KendoReact Grid component is designed specifically for React.

    Now, normally, a simple <table> element might do the trick, right? But Kendo UI for Angular’s data grid is chockfull of extras that make it a much better user experience. Notice right off the bat that it provides interactive functionality around things like exporting the data to Excel or PDF. And there are a bunch of other non-trivial features that would otherwise take a vast majority of the time and effort to make the component.

    Filtering

    Here’s one for you: filtering a grid of data. Say you’re looking at a list of employees like the data grid example above, but for a company that employees thousands of folks. It’d be hard to find a specific person without considering a slew of features, like search, sortable columns, and pagination — all of which Kendo UI’s data grid does.

    Users can quickly parse the data bound to the Angular data grid. Filtering can be done through a dedicated filter row, or through a filter menu that pops up when clicking on a filter icon in the header of a column. 

    One way to filter the data is to click on a column header, select the Filter option, and set the criteria.

    Kendo UI’s documentation is great. Here’s how fast we can get this up and running.

    First, import the component

    No tricks here — import the data grid as you would any other component:

    import { Component, OnInit, ViewChild } from '@angular/core';
    import { DataBindingDirective } from '@progress/kendo-angular-grid';
    import { process } from '@progress/kendo-data-query';
    import { employees } from './employees';
    import { images } from './images';

    Next, call the component

    @Component({
      selector: 'my-app',
      template: `
        <kendo-grid>
          // ...
        </kendo-grid>
      `
    })

    This is incomplete, of course, because next we have to…

    Configure the component

    The key feature we want to enable is filtering, but Kendo’s Angular Grid takes all kinds of feature parameters that can be enabled in one fell swoop, from sorting and grouping, to pagination and virtualization, to name a few.

    Filtering? It’s a one-liner to bind it to the column headers.

    @Component({
      selector: 'my-app',
      template: `
        <kendo-grid
          [kendoGridBinding]="gridView"
          kendoGridSelectBy="id"
          [selectedKeys]="mySelection"
          [pageSize]="20"
          [pageable]="true"
          [sortable]="true"
          [groupable]="true"
          [reorderable]="true"
          [resizable]="true"
          [height]="500"
          [columnMenu]="{ filter: true }"
        >
          // etc.
        </kendo-grid>
      `
    })

    Then, mark up the rest of the UI

    We won’t go deep here. Kendo UI’s documentation has an excellent example of how that looks. This is a good time to work on the styling as well, which is done in a styles parameter. Again, theming a Kendo UI component is a cinch.

    So far, we have a nice-looking data grid before we even plug any actual data into it!

    And, finally, bind the data

    You may have noticed right away when we imported the component that we imported “employee” data in the process. We need to bind that data to the component. Now, this is where someone like me would go run off in a corner and cry, but Kendo UI makes it a little too easy for that to happen.

    // Active the component on init
    export class AppComponent implements OnInit {
      // Bind the employee data to the component
      @ViewChild(DataBindingDirective) dataBinding: DataBindingDirective;
      // Set the grid's data source to the employee data file
      public gridData: any[] = employees;
      // Apply the data source to the Grid component view
      public gridView: any[];
    
      public mySelection: string[] = [];
    
      public ngOnInit(): void {
        this.gridView = this.gridData;
      }
      // Start processing the data
      public onFilter(inputValue: string): void {
        this.gridView = process(this.gridData, {
          filter: {
            // Set the type of logic (and/or)
            logic: "or",
            // Defining filters and their operators
            filters: [
              {
                field: 'full_name',
                operator: 'contains',
                value: inputValue
              },
              {
                field: 'job_title',
                operator: 'contains',
                value: inputValue
              },
              {
                field: 'budget',
                operator: 'contains',
                value: inputValue
              },
              {
                field: 'phone',
                operator: 'contains',
                value: inputValue
              },
              {
                field: 'address',
                operator: 'contains',
                value: inputValue
              }
            ],
          }
        }).data;
    
        this.dataBinding.skip = 0;
      }
    
      // ...
    }

    Let’s see that demo again


    That’s a heckuva lot of power with a minimal amount of effort. The Kendo UI APIs are extensive and turn even the most complex feature dead simple.

    And we didn’t even get to all of the other wonderful goodies that we get with Kendo UI components. Take accessibility. Could you imagine all of the consideration that needs to go into making a component like this accessible? Like all of the other powerful features we get, Kendo UI tackles accessibility for us as well, taking on the heavy lifting that goes into making a keyboard-friendly UI that meets WCAG 2.0 Alice standards and is compliant with Section 508 and WAI-ARIA standards.


    Building an Angular Data Grid With Filtering originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/building-an-angular-data-grid-with-filtering/feed/ 1 354782
    A Themeable React Data Grid With Great UX-Focused Features https://css-tricks.com/a-themeable-react-data-grid-with-great-ux-focused-features/ https://css-tricks.com/a-themeable-react-data-grid-with-great-ux-focused-features/#comments Thu, 07 Oct 2021 14:35:28 +0000 https://css-tricks.com/?p=353242 (This is a sponsored post.)

    KendoReact can save you boatloads of time because it offers pre-built componentry you can use in your app right away. They look nice, but more importantly, they are easily themeable, so they look however …


    A Themeable React Data Grid With Great UX-Focused Features originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    (This is a sponsored post.)

    KendoReact can save you boatloads of time because it offers pre-built componentry you can use in your app right away. They look nice, but more importantly, they are easily themeable, so they look however you need them to look. And I’d say the looks aren’t even the important part. There are lots of component libraries out there that focus on the visuals. These components tackle the hardest interactivity problems in UI/UX, and do it with grace, speed, and accessibility in mind.

    Let’s take a look at their React Data Grid component.

    The ol’ <table> element is the right tool for the job for data grids, but a table doesn’t offer most of the features that make for a good data browsing experience. If we use the KendoReact <Grid /> component (and friends), we get an absolute ton of extra features, any one of which is non-trivial to pull off nicely, and all together make for an extremely compelling solution. Let’s go through a list of what you get.

    Sortable Columns

    You’ll surely pick a default ordering for your data, but if any given row of data has things like ID’s, dates, or names, it’s perfectly likely that a user would want to sort the column by that data. Perhaps they want to view the oldest orders, or the orders of the highest total value. HTML does not help with ordering in tables, so this is table stakes (get it?!) for a JavaScript library for data grids, and it’s perfectly handled here.

    Pagination and Limits

    When you have any more than, say, a few dozen rows of data, it’s common that you want to paginate it. That way users don’t have to scroll as much, and equally importantly, it keeps the page fast by not making the DOM too enormous. One of the problems with pagination though is it makes things like sorting harder! You can’t just sort the 20 rows you can see, it is expected that the entire data set gets sorted. Of course that’s handled in KendoReact’s Data Grid component.

    Or, if pagination isn’t your thing, the data grid offers virtualized scrolling — in both the column and row directions. That’s a nice touch as the data loads quickly for smooth, natural scrolling.

    Expandable Rows

    A data grid might have a bunch of data visible across the row itself, but there might be even more data that a user might want to dig out of an entry once they find it. Perhaps it is data that doesn’t need to be cross-referenced in the same way column data is. This can be tricky to pull off, because of the way table cells are laid out. The data is still associated with a single row, but you often need more room than the width of one cell offers. With the KendoReact Data Grid component, you can pass in a detail prop with an arbitrary React component to show when a row is expanded. Super flexible!

    Notice how the expanded details can have their own <Grid /> inside!

    Responsive Design

    Perhaps the most notoriously difficult thing to pull off with <table> designs is how to display them on small screens. Zooming out isn’t very good UX, nor is collapsing the table into something non-table-like. The thing about data grids is that they are all different, and you’ll know data is most important to your users best. The KendoReact Data Grid component helps with this by making your data grid scrollable/swipeable, and also being able to lock columns to make sure they continue to be easy to find and cross-reference.

    Filtering Data

    This is perhaps my favorite feature just because of how UX-focused it is. Imagine you’re looking at a big data grid of orders, and you’re like “Let me see all orders from White Clover Markets.” With a filtering feature, perhaps you quickly type “clover” into the filter input, and viola, all those orders are right there. That’s extra tricky stuff when you’re also supporting ordering and pagination — so it’s great all these features work together.

    Grouping Data

    Now this feature actually blows my mind 🤯 a little bit. Filtering and sorting are both very useful, but in some cases, they leave a little bit to be desired. For example, it’s easy to filter too far too quickly, leaving the data you are looking at very limited. And with sorting, you might be trying to look at a subset of data as well, but it’s up to your brain to figure out where that data begins and ends. With grouping, you can tell the data grid to strongly group together things that are the most important to you, but then still leverage filtering and sorting on top of that. It instantly makes your data exploration easier and more useful.

    Localization

    This is where you can really tell KendoReact went full monty. It would be highly unfortunate to pick some kind of component library and then realize that you need localization and realize it wasn’t made to be a first-class citizen. You avoid all that with KendoReact, which you can see in this Data Grid component. In the demo, you can flip out English for Spanish with a simple dropdown and see all the dates localized. You pull off any sort of translation and localization with the <LocalizationProvider> and <IntlProvider>, both comfortable React concepts.

    Exporting to PDF or Excel

    Here’s a live demo of this:

    C’mon now! That’s very cool.

    That’s not all…

    Go check out the docs for the React Data Grid. There are a bunch more features we didn’t even get to here (row pinning! cell editing!). And here’s something to ease your mind: this component, and all the KendoReact components, are keyboard friendly and meet Section 508 accessibility standards. That is no small feat. When components are this complex and involve this much interactivity, getting the accessibility right is tough. So not only are you getting good-looking components that work everywhere, you’re getting richly interactive components that deliver UX beyond what you might even think of, and it’s all done fast and accessiblty. That’s pretty unreal, really.


    A Themeable React Data Grid With Great UX-Focused Features originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/a-themeable-react-data-grid-with-great-ux-focused-features/feed/ 3 https://css-tricks.com/wp-content/uploads/2021/10/CleanShot-2021-10-04-at-15.53.58.mp4 performance Archives - CSS-Tricks nonadult 353242
    ct.css — Performance Hints via Injected Stylesheet Alone https://css-tricks.com/ct-css-performance-hints-via-injected-stylesheet-alone/ https://css-tricks.com/ct-css-performance-hints-via-injected-stylesheet-alone/#comments Tue, 05 Oct 2021 14:28:28 +0000 https://css-tricks.com/?p=353225 This is some bonafide CSS trickery from Harry that gives you some generic performance advice based on what it sees in your <head> element.

    First, it’s possible to make a <style block visible like any other element by changing the …


    ct.css — Performance Hints via Injected Stylesheet Alone originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    This is some bonafide CSS trickery from Harry that gives you some generic performance advice based on what it sees in your <head> element.

    First, it’s possible to make a <style> block visible like any other element by changing the display away from the default of none. It’s a nice little trick. You can even do that for things in the <head>, for example…

    head,
    head style,
    head script {
      display: block;
    }

    From there, Harry gets very clever with selectors, determining problematic situations from the usage and placement of certain tags. For example, say there is a <script> that comes after some styles…

    <head>
      <link rel="stylesheet" href="...">
      <script src="..."></script>
      <title>Page Title</title>
      <!-- ...  -->

    Well, that’s bad, because the script is blocked by CSS likely unnecessarily. Perhaps some sophisticated performance tooling software could tell you that. But you know what else can? A CSS selector!

    head [rel="stylesheet"]:not([media="print"]):not(.ct) ~ script,
    head style:not(:empty) ~ script {
    
    }

    That’s kinda like saying head link ~ script, but a little fancier in that it only selects actual stylesheets or style blocks that are truly blocking (and not itself). Harry then applies styling and pseudo-content to the blocks so you can use the stylesheet as a visual performance debugging tool.

    That’s just darn clever, that. The stylesheet has loads of little things you can test for, like attributes you don’t need, blocking resources, and elements that are out of order.

    To Shared LinkPermalink on CSS-Tricks


    ct.css — Performance Hints via Injected Stylesheet Alone originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/ct-css-performance-hints-via-injected-stylesheet-alone/feed/ 1 353225
    Frameworks Helping Image Usage https://css-tricks.com/frameworks-helping-image-usage/ https://css-tricks.com/frameworks-helping-image-usage/#comments Thu, 09 Sep 2021 19:44:54 +0000 https://css-tricks.com/?p=350644 I recently blogged about how images are hard and it ended up being a big ol’ checklist of things that you could/should think about and implement when placing images on websites.

    I think it’s encouraging to see frameworks — these …


    Frameworks Helping Image Usage originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    I recently blogged about how images are hard and it ended up being a big ol’ checklist of things that you could/should think about and implement when placing images on websites.

    I think it’s encouraging to see frameworks — these beloved tools that we leverage to help us build websites — offering additional tools within them to help tackle this checklist and take on the hard (but perfectly suited for computers) tasks of displaying images.

    Some examples:

    I’m not sure I’d give any of them flying colors as far as ease of use. There is stuff to install, configure, and it’s likely you’ll only reach for it if you already know you should be doing it, and your pre-existing knowledge of image performance can help you through the process. It’s not the failing of these frameworks; this stuff is complicated and the audience is developers who are, fair is fair, a little into the idea of control.

    I do gotta hand it to my BFF WordPress on this one. You literally do nothing and just get responsive images out of the box. If you need to tap into the filters to control things, you can do that like you can anything else in WordPress: through hooks. If you go for Jetpack (and I highly encourage you to), you flip on the (incredibly, free) Site Accelerator feature, which takes all those images, optimizes them, CDN-hosts them, lazy loads them, and serves them in formats, like WebP, when possible (I would assume more next-gen formats will happen eventually). Jetpack is a sponsor, so full disclosure there, but I use it very much on purpose because the experience makes image handling something I literally don’t have to think about.

    Another interesting aspect of frameworks-helping-with-images is that some of it was born out of Google getting involved. Google calls it “Aurora”:

    For almost two years, we have worked with some of the most popular frameworks such as Next.js, Nuxt and Angular, working to improve web performance.

    The project does all sorts of stuff, including hand out money to help fund open-source tools, and direct help specific initiatives. Like images:

    An Image component in Next.js that encapsulates best practices for image loading, followed by a collaboration with Nuxt on the same. Use of this component has resulted in significant improvements to paint times and layout shift (example: 57% reduction in Largest Contentful Paint and 100% reduction in Cumulative Layout Shift on nextjs.org/give).

    Cool, right? I think so? What weirds me out about this just a smidge is that it feels meaningful when Google’s squad rolls up to contribute to a framework. They didn’t pick underdog frameworks here, surely on purpose, because they want their work to impact the most people. So, frameworks that are already successful benefit from A-squad contributions. A rich-get-richer situation. I’m not sure it’s a huge problem, but it’s just something I think about.


    Frameworks Helping Image Usage originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/frameworks-helping-image-usage/feed/ 2 350644
    Don’t attach tooltips to document.body https://css-tricks.com/dont-attach-tooltips-to-document-body/ https://css-tricks.com/dont-attach-tooltips-to-document-body/#comments Wed, 08 Sep 2021 19:08:55 +0000 https://css-tricks.com/?p=351389 Here’s Atif Afzal on using a <div> that is permanently on the page where tooltips are added/removed and how they perform vastly better than plopping those same tooltips right into the <body>. It’s not really discussed, but the reason …


    Don’t attach tooltips to document.body originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    Here’s Atif Afzal on using a <div> that is permanently on the page where tooltips are added/removed and how they perform vastly better than plopping those same tooltips right into the <body>. It’s not really discussed, but the reason you put them that high-up in the DOM is so you can absolutely position them exactly where you need to on the page without having to deal with hidden overflow or relative parents and the like.

    To my amazement, just having a separate container without even adding the [CSS] contain property fixed the performance. The main problem now, was to explain it. First I thought this might be some internal browser heuristic optimizing the Recalculate Style, but there is no black magic and I discovered the reason.

    The trick is to avoid forced recalculations of style:

    […] The tooltip container is not visible in the page, so modifying it doesn’t invalidate the complete page render tree. If the tooltip container would have been visible in the page, then the complete render tree would be invalidated but in this case only an independent subtree was invalidated. Recalculating Style for a small subtree of 3 doesn’t take a lot of time and hence is faster.

    Looks like popper.js was used here, so you have to be smart about it. We use toast messages on CodePen, and it’s the only third-party component we use at the moment: react-hot-toast. I checked it, and not only do we tuck the messages in a <div> of our own, but the library itself does that, so I think we’re in the clear.


    Don’t attach tooltips to document.body originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/dont-attach-tooltips-to-document-body/feed/ 2 351389
    Links on Performance IV https://css-tricks.com/links-on-performance-iv/ https://css-tricks.com/links-on-performance-iv/#comments Thu, 02 Sep 2021 22:28:51 +0000 https://css-tricks.com/?p=350934
  • HTTP Caching is a Superpower — Hugh Haworth covers how the Cache-Control header is an awfully potent ingredient in web performance. I mis-read the title at first and was waiting to read about HTML caching. Hugh covers it a bit

  • Links on Performance IV originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
  • HTTP Caching is a Superpower — Hugh Haworth covers how the Cache-Control header is an awfully potent ingredient in web performance. I mis-read the title at first and was waiting to read about HTML caching. Hugh covers it a bit (like how you’d need to be careful doing so on something like a forum, where the content on pages changes rapidly), but I find it something that’s generally under-talked-about. As in, generally, people just don’t cache HTML at all, because it changes the most and it’s risky being the parent of most other cache. I only do it now because Cloudflare handles it.
  • Why it’s okay for web components to use frameworks — I admit it rubs me the wrong way to think of web components as needing a framework at all, let alone being OK with a hogepodge of random frameworks. But Nolan Lawson digs into the nuance here. A couple of kilobytes that might be lazy loaded/code-split out is not that big of a deal, especially since the frameworks might be optimized for runtime performance.
  • web-vitals-element — An npm packages from Stefan Judis that boots up a <web-vitals> web component showing your CLS, FID, LCP, and BVD (demo). Sorta weird it won’t build on Skypack.
  • Top 10 performance pitfalls — I probably wouldn’t have guessed any of the things Jake and Surma cover in this top 10 list. My guess is that the low hanging fruit of performance either becomes a bunch of non-issues through technological improvements, or it is generally handled by site owners and hosts and, thus, a new set of problems become the top offenders.
  • How to Eliminate Render-Blocking Resources: a Deep Dive — Sia Karamalegos: Render-blocking resources are files that ‘press pause’ on the critical rendering path. They interrupt one or more of the steps. You should be super aware of anything that is render-blocking and only render-block on purpose (like critical CSS) rather than letting it happen by accident.
  • How we reduced Next.js page size by 3.5x and achieved a 98 Lighthouse score — Colin Armstrong covers how dynamically loading assets, reducing the size of assets, and using responsive images goes along way. Fortunately, the tooling to diagnose performance problems and the tools for solving them are largely build into Next.js. The bit about PurgeCSS and Tailwind seems extra pertinant here. I think if you aren’t using PurgeCSS to remove unused Tailwind selectors (or the JIT compiler to only create the selectors you need), you’re basically doing Tailwind wrong. Shipping 350KB of CSS instead of the 10KB you need is not OK.
  • Improving responsiveness in text inputs — Nolan Lawson covers how to prevent blocking the input event with “expensive” main thread work, using requestIdleCallback to batch UI updates.
  • Vector? Raster? Why Not Both! — Zach gets the best possible file size by splitting a graphic into two parts: An SVG (vector) for the things SVG does well, and a super-optimized raster graphic (ideally AVIF) for the things it does well, then plopping them on top of one another.

  • Links on Performance IV originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/links-on-performance-iv/feed/ 1 350934
    Learnings From a WebPageTest Session on CSS-Tricks https://css-tricks.com/learnings-from-a-webpagetest-session-on-css-tricks/ https://css-tricks.com/learnings-from-a-webpagetest-session-on-css-tricks/#comments Tue, 27 Jul 2021 23:18:33 +0000 https://css-tricks.com/?p=345169 (This is a sponsored post.)

    I got together with Tim Kadlec from over at WebPageTest the other day to use do a bit of performance testing on CSS-Tricks. Essentially use the tool, poke around, and identify performance pain points …


    Learnings From a WebPageTest Session on CSS-Tricks originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    (This is a sponsored post.)

    I got together with Tim Kadlec from over at WebPageTest the other day to use do a bit of performance testing on CSS-Tricks. Essentially use the tool, poke around, and identify performance pain points to work on. You can watch the video right here on the site, or over on their Twitch channel, which is worth a subscribe for more performance investigations like these.

    Web performance work is twofold:

    Step 1) Measure Things & Explore Problems
    Step 2) Fix it

    Tim and I, through the amazing tool that is WebPageTest, did a lot of Step 1. I took notes as we poked around. We found a number of problem areas, some fairly big! Of course, after all that, I couldn’t get them out of my head, so I had to spring into action and do the Step 2 stuff as soon as I could, and I’m happy to report I’ve done most of it and seen improvement. Let’s dig in!

    Identified Problem #1) Poor LCP

    Largest Contentful Paint (LCP) is one of the Core Web Vitals (CWV), which everyone is carefully watching right now with Google telling us it’s an SEO factor. My LCP was clocking in at 3.993s which isn’t great.

    WebPageTest clearly tells you if there are problems with your CWV.

    I learned from Tim that it’s ideal if the First Contentful Paint (FCP) contains the LCP. We could see that wasn’t happening through WebPageTest.

    Things to fix:

    • Make sure the LCP area, which was ultimately a big image, is properly optimized, has a responsive srcset, and is CDN hosted. All those things were failing on that particular image despire working elsewhere.
    • The LCP image had loading="lazy" on it, which we just learned isn’t a good place for that.

    Fixing technique and learnings:

    • All the proper image handling stuff was in place, but for whatever reason, none of it works for .gif files, which is what that image was the day of the testing. We probably just shouldn’t use .gif files for that area anyway.
    • Turn off lazy loading of LCP image. This is a WordPress featured image, so I essentially had to do <?php the_post_thumbnail('', array('loading' => 'eager')); ?>. If it was an inline image, I’d do <img data-no-lazy="1" ... /> which tells WordPress what it needs to know.

    Identified Problem #2) First Byte to Start Render gap

    Tim saw this right away as a fairly obvious problem.

    In the waterfall above (here’s a super detailed article on reading waterfalls from Matt Hobbs), you can see the HTML arrives in about 0.5 seconds, but the start of rendering (what people see, big green line), doesn’t start until about 2.9 seconds. That’s too dang long.

    The chart also identifies the problem in a yellow line. I was linking out to a third-party CSS file, which then redirects to my own CSS files that contain custom fonts. That redirect costs time, and as we dug into, not just first-page-load time, but every single page load, even cached page loads.

    Things to fix:

    • Eliminate the CSS file redirect.
    • Self-host fonts.

    Fixing technique and learnings:

    • I’ve been eying up some new fonts anyway. I noted not long ago that I really love Mass-Driver’s licensing innovation (priced by # of employees), but I equally love MD Primer, so I bought that. For body type, I stuck with a comfortable serif with Blanco, which mercifully came with very nicely optimized RIBBI1 versions. Next time I swear I’m gonna find a variable font, but hey, you gotta follow your heart sometimes. I purchased these, and am now self-hosting the font-files.
    • Use @font-face right in my own CSS, with no redirects. Also using font-display: swap;, but gotta work a bit more on that loading technique. Can’t wait for size-adjust.

    After re-testing with the change in place, you can see on a big article page the start render is a full 2 seconds faster on a 4G connection:

    That’s a biiiiiig change. Especially as it affects cached page loads too.
    See how the waterfall pulls back to the left without the CSS redirect.

    Identified Problem #3) CLS on the Grid Guide is Bad

    Tim had a neat trick up his sleeve for measuring Cumulative Layout Shift (CLS) on pages. You can instruct WebPageTest to scroll down the page for you. This is important for something like CLS, because layout shifting might happen on account of scrolling.

    See this article about CLS and WebPageTest.

    The trick is using an advanced setting to inject custom JavaScript into the page during the test:

    At this point, we were testing not the homepage, but purposefully a very important page: our Complete Guide to Grid. With this in place, you can see the CWV are in much worse shape:

    I don’t know what to think exactly about the LCP. That’s being triggered by what happens to be the largest image pretty far down the page.

    I’m not terribly worried about the LCP with the scrolling in place. That’s just some image like any other on the page, lazily loaded.

    The CLS is more concerning, to me, because any shifting layout is always obnoxious to users. See all these dotted orange lines? That is CLS happening:

    The orange CLS lines correlate with images loading (as the page scrolls down and the lazy loaded images come in).

    Things to fix:

    • CLS is bad because of lazy loaded images coming in and shifting the layout.

    Fixing technique and learnings:

    • I don’t know! All those images are inline <img loading="lazy" ...> elements. I get that lazy loading could cause CLS, but these images have proper width and height attributes, which is supposed to reserve the exact space necessary for the image (even when fluid, thanks to aspect ratio) even before it loads. So… what gives? Is it because they are SVG?

    If anyone does know, feel free to hit me up. Such is the nature of performance work, I find. It’s a mixture of easy wins from silly mistakes, little battles you can fight and win, bigger battles that sometimes involves outside influences that are harder to win, and mysterious unknowns that it takes time to heal. Fortunately we have tools like WebPageTest to tell us the real stories happening on our site and give us the insight we need to fight these performance battles.


    1. RIBBI, I just learned, means Regular, Italic, Bold, and Bold Italic. The classic combo that most body copy on the web needs.

    Learnings From a WebPageTest Session on CSS-Tricks originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/learnings-from-a-webpagetest-session-on-css-tricks/feed/ 7 345169
    CSS for Web Vitals https://css-tricks.com/css-for-web-vitals/ https://css-tricks.com/css-for-web-vitals/#comments Thu, 01 Jul 2021 18:54:45 +0000 https://css-tricks.com/?p=343637 The marketing for Core Web Vitals (CWV) has been a massive success. I guess that’s what happens when the world’s dominant search engine tells people that something’s going to be an SEO factor. Ya know what language can play a …


    CSS for Web Vitals originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    The marketing for Core Web Vitals (CWV) has been a massive success. I guess that’s what happens when the world’s dominant search engine tells people that something’s going to be an SEO factor. Ya know what language can play a huge role in those CWV scores? I’ll wait five minutes for you to think of it. Just kidding, it’s CSS.

    Katie Hempenius and Una Kravets:

    The way you write your styles and build layouts can have a major impact on Core Web Vitals. This is particularly true for Cumulative Layout Shift (CLS) and Largest Contentful Paint (LCP).

    For example…

    • Absolutely positioning things takes them out of flow and prevents layout shifting (but please don’t read that as we should absolute position everything).
    • Don’t load images you don’t have to (e.g. use a CSS gradient instead), which lends a bit of credibility to this.
    • Perfect font fallbacks definitely help layout shifting.

    There are a bunch more practical ideas in the article and they’re all good ideas (because good performance is good for lots of reasons), even if the SEO angle isn’t compelling to you.

    To Shared LinkPermalink on CSS-Tricks


    CSS for Web Vitals originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/css-for-web-vitals/feed/ 3 343637