performance – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Thu, 28 Apr 2022 14:42:50 +0000 en-US hourly 1 https://wordpress.org/?v=6.1.1 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 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
    Front-End Testing is For Everyone https://css-tricks.com/front-end-testing-is-for-everyone/ https://css-tricks.com/front-end-testing-is-for-everyone/#comments Tue, 01 Jun 2021 14:17:43 +0000 https://css-tricks.com/?p=341342 Testing is one of those things that you either get super excited about or kinda close your eyes and walk away. Whichever camp you fall into, I’m here to tell you that front-end testing is for everyone. In fact, …


    Front-End Testing is For Everyone originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    Testing is one of those things that you either get super excited about or kinda close your eyes and walk away. Whichever camp you fall into, I’m here to tell you that front-end testing is for everyone. In fact, there are many types of tests and perhaps that is where some of the initial fear or confusion comes from.

    I’m going to cover the most popular and widely used types of tests in this article. This might be nothing new to some of you, but it can at least serve as a refresher. Either way, my goal is that you’re able to walk away with a good idea of the different types of tests out there. Unit. Integration. Accessibility. Visual regression. These are the sorts of things we’ll look at together.

    And not just that! We’ll also point out the libraries and frameworks that are used for each type of test, like Mocha. Jest, Puppeteer, and Cypress, among others. And don’t worry — I’ll avoid a bunch of technical jargon. That said, you should have some front-end development experience to understand the examples we’re going to cover.

    OK, let’s get started!

    What is testing?

    Software testing is an investigation conducted to provide stakeholders with information about the quality of the software product or service under test.

    Cem Kaner, “Exploratory Testing” (November 17, 2006)

    At its most basic, testing is an automated tool that finds errors in your development as early as possible. That way, you’re able to fix those issues before they make it into production. Tests also serve as a reminder that you may have forgotten to check your own work in a certain area, say accessibility.

    In short, front-end testing validates that what people see on the site and the features they use on it work as intended.

    Front-end testing is for the client side of your application. For example, front-end tests can validate that pressing a “Delete” button properly removes an item from the screen. However, it won’t necessarily check if the item was actually removed from the database — that sort of thing would be covered during back-end testing.

    That’s testing in a nutshell: we want to catch errors on the client side and fix them before code is deployed.

    Different tests look at different parts of the project

    Different types of tests cover different aspects of a project. Nevertheless, it is important to differentiate them and understand the role of each type. Confusing which tests do what makes for a messy, unreliable testing suit.

    Ideally, you’d use several different types of tests to surface different types of possible issues. Some test types have a test coverage analytic that shows just how much of your code (as a percentage) is looked at by that particular test. That’s a great feature, and while I’ve seen developers aim for 100% coverage, I wouldn’t rely on that metric alone. The most important thing is to make sure all possible edge cases are covered and taken into account.

    So, with that, let’s turn our attention to the different types of testing. Remember, it’s not so much that you’re expected to use each and every one of these. It’s about being able to differentiate the tests so that you know which ones to use in certain circumstances.

    Unit testing

    Unit testing is the most basic building block for testing. It looks at individual components and ensures they work as expected. This sort of testing is crucial for any front-end application because, with it, your components are tested against how they’re expected to behave, which leads to a much more reliable codebase and app. This is also where things like edge cases can be considered and covered.

    Unit tests are particularly great for testing APIs. But rather than making calls to a live API, hardcoded (or “mocked”) data makes sure that your test runs are always consistent at all time.

    Let’s take a super simple (and primitive) function as an example:

    const sayHello = (name) => {
      if (!name) {
        return "Hello human!";
      }
    
      return `Hello ${name}!`;
    };

    Again, this is a basic case, but you can see that it covers a small edge case where someone may have neglected to provide a first name to the application. If there’s a name, we’ll get “Hello ${name}!” where ${name} is what we expect the person to have provided.

    “Um, why do we need to test for something small like that?” you might wonder. There are some very important reasons for this:

    • It forces you to think deeply about the possible outcomes of your function. More often than not, you really do discover edge cases which helps you cover them in your code.
    • Some part of your code can rely on this edge case, and if someone comes and deletes something important, the test will warn them that this code is important and cannot be removed.

    Unit tests are often small and simple. Here’s an example:

    describe("sayHello function", () => {
      it("should return the proper greeting when a user doesn't pass a name", () => {
        expect(sayHello()).toEqual("Hello human!")
      })
    
      it("should return the proper greeting with the name passed", () => {
        expect(sayHello("Evgeny")).toEqual("Hello Evgeny!")
      })
    })

    describe and it are just syntactic sugar. The most important lines with expect and toEqual. describe and it breaks the test into logical blocks that are printed to the terminal. The expect function accepts the input we want to validate, while toEqual accepts the desired output. There are a lot of different functions and methods you can use to test your application.

    Let’s say we’re working with Jest, a library for writing units. In the example above, Jest will display the sayHello function as a title in the terminal. Everything inside an it function is considered as a single test and is reported in the terminal below the function title, making everything very easy to read.

    The green checkmarks mean both of our tests have passed. Yay!

    Integration testing

    If unit tests check the behavior of a block, integration tests make sure that blocks work flawlessly together. That makes Integration testing super important because it opens up testing interactions between components. It’s very rare (if ever) that an application is composed of isolated pieces that function by themselves. That’s why we rely on integration tests.

    We go back to the function we unit tested, but this time use it in a simple React application. Let’s say that clicking a button triggers a greeting to appear on the screen. That means a test involves not only the function but also the HTML DOM and a button’s functionality. We want to test how all these parts play together.

    Here’s the code for a <Greeting /> component we’re testing:

    export const Greeting = () => {  
      const [showGreeting, setShowGreeting] = useState(false);  
    
     return (  
       <div>  
         <p data-testid="greeting">{showGreeting && sayHello()}</p>  
         <button data-testid="show-greeting-button" onClick={() => setShowGreeting(true)}>Show Greeting</button>  
       </div>
     );  
    };

    Here’s the integration test:

    describe('<Greeting />', () => {  
      it('shows correct greeting', () => {  
        const screen = render(<Greeting />);  
         const greeting = screen.getByTestId('greeting');  
         const button = screen.getByTestId('show-greeting-button');  
    
         expect(greeting.textContent).toBe('');  
         fireEvent.click(button);  
         expect(greeting.textContent).toBe('Hello human!');  
     });  
    });

    We already know describe and it from our unit test. They break tests up into logical parts. We have the render function that displays a <Greeting /> component in the special emulated DOM so we can test interactions with the component without touching the real DOM — otherwise, it can be costly.

    Next up, the test queries <p> and <button> elements via test IDs ( #greeting and #show-greeting-button, respectively). We use test IDs because it’s easier to get the components we want from the emulated DOM. There are other ways to query components, but this is how I do it most often.

    It’s not until line 7 that the actual integration test begins! We first check that <p> tag is empty. Then we click the button by simulating a click event. And lastly, we check that the <p> tag contains “Hello human!” inside it. That’s it! All we’re testing is that an empty paragraph contains text after a button is clicked. Our component is covered.

    We can, of course, add input where someone types their name and we use that input in the greeting function. However, I decided to make it a bit simpler. We’ll get to using inputs when we cover other types of tests.

    Check out what we get in the terminal when running the integration test:

    Termain message showing a passed test like before, but now with a specific test item for showing the correct greeting. It includes the number of tests that ran, how many passed, how many snapshots were taken, and how much time the tests took, which was 1.085 seconds.
    Perfect! The <Greeting /> component shows the correct greeting when clicking the button.

    End-to-end (E2E) testing

    • Level: High
    • Scope: Tests user interactions in a real-life browser by providing it instructions for what to do and expected outcomes.
    • Possible tools: Cypress, Puppeteer

    E2E tests are the highest level of testing in this list. E2E tests care only about how people see your application and how they interact with it. They don’t know anything about the code and the implementation.

    E2E tests tell the browser what to do, what to click, and what to type. We can create all kinds of interactions that test different features and flows as the end user experiences them. It’s literally a robot that’s interacted to click through an application to make sure everything works.

    E2E tests are similar to integration tests in a sort of way. However, E2E tests are executed in a real browser with a real DOM rather than something we mock up — we generally work with real data and a real API in these tests.

    It is good to have full coverage with unit and integration tests. However, users can face unexpected behaviors when they run an application in the browser — E2E tests are the perfect solution for that.

    Let’s look at an example using Cypress, an extremely popular testing library. We are going to use it specifically for an E2E test of our previous component, this time inside a browser with some extra features.

    Again, we don’t need to see the code of the application. All we’re assuming is that we have some application and we want to test it as a user. We know what buttons to click and the IDs those buttons have. That’s all we really have to go off of.

    describe('Greetings functionality', () => {  
      it('should navigate to greetings page and confirm it works', () => {
        cy.visit('http://localhost:3000')  
        cy.get('#greeting-nav-button').click()  
        cy.get('#greetings-input').type('Evgeny', { delay: 400 })  
        cy.get('#greetings-show-button').click()  
        cy.get('#greeting-text').should('include.text', 'Hello Evgeny!')  
      })  
    })

    This E2E test looks very similar to our previous integration test. The commands are extremely similar, the main difference being that these are executed in a real browser.

    First, we use cy.visit to navigate to a specific URL where our application lies:

    cy.visit('http://localhost:3000')

    Second, we use cy.get to get the navigation button by its ID, then instruct the test to click it. That action will navigate to the page with the <Greetings /> component. In fact, I’ve added the component to my personal website and provided it with its own URL route.

    cy.get('#greeting-nav-button').click()

    Then, sequentially, we get text input, type “Evgeny,” click the #greetings-show-button button and, lastly, check that we got the desired greeting output.

    cy.get('#greetings-input').type('Evgeny', { delay: 400 })
    cy.get('#greetings-show-button').click()
    cy.get('#greeting-text').should('include.text', 'Hello Evgeny!')  

    It is pretty cool to watch how the test clicks buttons for you in a real live browser. I slowed down the test a bit so you can see what is going on. All of this usually happens very quickly.

    Here is the terminal output:

    Terminal showing a run test for greetings.spec.js that passed in 12 seconds.

    Accessibility testing

    Web accessibility means that websites, tools, and technologies are designed and developed so that people with disabilities can use them.

    W3C

    Accessibility tests make sure people with disabilities can effectively access and use a website. These tests validate that you follow the standards for building a website with accessibility in mind.

    For example, many unsighted people use screen readers. Screen readers scan your website and attempt to present it to users with disability in a format (usually spoken) those users can understand. As a developer, you want to make a screen reader’s job easy and accessibility testing will help you understand where to start.

    There are a lot of different tools, some of them automated and some that run manually to validate accessibilit. For example, Chrome already has one tool built right into its DevTools. You may know it as Lighthouse.

    Let’s use Lighthouse to validate the application we made in the E2E testing section. We open Lighthouse in Chrome DevTools, click the “Accessibility” test option, and “Generate” the report.

    That’s literally all we have to do! Lighthouse does its thing, then generates a lovely report, complete with a score, a summary of audits that ran, and an outline of opportunities for improving the score.

    But this is just one tool that measures accessibility from its particular lens. We have all kinds of accessibility tooling, and it’s worth having a plan for what to test and the tooling that’s available to hit those points.

    Visual regression testing

    • Level: High
    • Scope: Tests the visual structure of application, including the visual differences produced by a change in the code.
    • Possible tools: Cypress, Percy, Applitools

    Sometimes E2E tests are insufficient to verify that the last changes to your application didn’t break the visual appearance of anything in an interface. Have you pushed the code with some changes to production just to realize that it broke the layout of some other part of the application? Well, you are not alone. Most times than not, changes to a codebase break an app’s visual structure, or layout.

    The solution is visual regression testing. The way it works is pretty straightforward. Visual test merely take a screenshot of pages or components and compare them with screenshots that were captured in previous successful tests. If these tests find any discrepancies between the screenshots, they’ll give us some sort of notification.

    Let’s turn to a visual regression tool called Percy to see how visual regression test works. There are a lot of other ways to do visual regression tests, but I think Percy is simple to show in action. In fact, you can jump over to Paul Ryan’s deep dive on Percy right here on CSS-Tricks. But we’ll do something considerably simpler to illustrate the concept.

    I intentionally broke the layout of our Greeting application by moving the button to the bottom of the input. Let’s try to catch this error with Percy.

    Percy works well with Cypress, so we can follow their installation guide and run Percy regression tests along with our existing E2E tests.

    describe('Greetings functionality', () => {  
      it('should navigate to greetings page and confirm everything is there', () => {  
        cy.visit('http://localhost:3000')  
        cy.get('#greeting-nav-button').click()  
        cy.get('#greetings-input').type('Evgeny', { delay: 400 })  
        cy.get('#greetings-show-button').click()  
        cy.get('#greeting-text').should('include.text', 'Hello Evgeny!')  
    
    
        // Percy test
         cy.percySnapshot() // HIGHLIGHT
      })  
    })

    All we added at the end of our E2E test is a one-liner: cy.percySnapshot(). This will take a screenshot and send it to Percy to compare. That is it! After the tests have finished, we’ll receive a link to check our regressions. Here is what I got in the terminal:

    Terminal output that shows white text on a black background. It displays the same result as before, but with a step showing that Percy created a build and where to view it.
    Hey, look, we can see that the E2E tests have passed as well! That shows how E2E testing won’t always catch a visual error.

    And here’s what we get from Percy:

    Animated gif of a webpage showing a logo and navigation above a form field. The animation overlays the original snapshot with the latest to reveal differences between the two.
    Something clearly changed and it needs to be fixed.

    Performance testing

    Performance testing is great for checking the speed of your application. If performance is crucial for your business — and it likely is given the recent focus on Core Web Vitals and SEO — you’ll definitely want to know if the changes to your codebase have a negative impact on the speed of the application.

    We can bake this into the rest of our testing flow, or we can run them manually. It’s totally up to you how to run these tests and how frequently to run them. Some devs create what’s called a “performance budget” and run a test that calculates the size of the app — and a failed test will prevent a deployment from happening if the size exceeds a certain threshold. Or, test manually every so often with Lighthouse, as it also measures performance metrics. Or combine the two and build Lighthouse into the testing suite.

    Performance tests can measure anything related to performance. They can measure how fast an application loads, the size of its initial bundle, and even the speed of a particular function. Performance testing is a somewhat broad, vast landscape.

    Here’s a quick test using Lighthouse. I think it’s a good one to show because of its focus on Core Web Vitals as well as how easily accessible it is in Chrome’s DevTools without any installation or configuration.

    A Lighthouse report open in Chrome DevTools showing a Performance score of 55 indicated by a orange circle bearing the score. Various metrics are listed below the score, including a timeline of the page as it loads.
    Not a great score, but at least we can see what’s up and we have some recommendations for how to make improvements.

    Wrapping up

    Here’s a breakdown of what we covered:

    TypeLevelScopeTooling examples
    UnitLowTests the functions and methods of an application.
    IntegrationMediumTests Interactions between units.
    End-to-endHighTests user interactions in a real-life browser by providing it instructions for what to do and expected outcomes.
    AccessibilityHighTests the interface of your application against accessibility standards criteria.
    Visual regressionHighTests the visual structure of application, including the visual differences produced by a change in the code. 
    PerformanceHighTests the application forperformance and stability.

    So, is testing for everyone? Yes, it is! Given all the available libraries, services, and tools we have to test different aspects of an application at different points, there’s at least something out there that allows us to measure and test code against standards and expectations — and some of them don’t even require code or configuration!

    In my experience, many developers neglect testing and think that a simple click-through or post check will help any possible bugs from a change in the code. If you want to make sure your application works as expected, is inclusive to as many people as possible, runs efficiently, and is well-designed, then testing needs to be a core part of your workflow, whether it’s automated or manual.

    Now that you know what types tests there are and how they work, how are you going to implement testing into your work?


    Front-End Testing is For Everyone originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/front-end-testing-is-for-everyone/feed/ 11 341342