Front-End System Design

CSS, Images & Rendering

Evgenii Ray

Evgenii Ray

Staff UI Engineer
Front-End System Design

Check out a free preview of the full Front-End System Design course

The "CSS, Images & Rendering" Lesson is part of the full, Front-End System Design course featured in this preview video. Here's what you'd learn in this lesson:

Evgenii covers optimization techniques for assets such as CSS stylesheets and images. Similar JS optimizations like splitting, minifying, and compressing can be applied to CSS. Image optimization strategies depend on the type of image asset. The WEBP format is designed to replace PNG, JPG, and GIF usage on web pages. This lesson also includes performance optimizations for fonts and rendering performance.

Preview
Close

Transcript from the "CSS, Images & Rendering" Lesson

[00:00:00]
>> Evgenii Ray: Okay, so what about CSS? For CSS, we can apply exactly the same technique. So if we expect that we have three types of devices to work with, mobile, standard, or super wide, we can split our CSS into the three bundles, and then serve it based on the user agent.

[00:00:17]
And instead of loading the whole CSS chunk, we can load a slightly reduced one. Same applies to the minification and compression. So if you take the minified Tailwind library, so uncompressed is 2.1 megabyte, minified, it's 1,900 kilobytes, gzipped it's 200 kilobytes. And the brotli provides the best compression, it's 46 kilobytes.

[00:00:44]
So the next important thing is critical style extraction. So when we fetch the index.html page, the way we load the assets, we see the asset link, and then we send the additional HTTP requests to fetch the data. So it results to doing two network trips. So we can optimize that by inlining the critical styles that are important for our application to function into the initial index.html request.

[00:01:20]
So when we load our first page, we already have all the styles inlined, and we can immediately show the content. And non-critical styles, such as some advanced graphic features or inactive pages, can be loaded under the low priority, and the way we can load additional styles. So first, let's see actually how this would look like.

[00:01:46]
So the non-optimized way, we just link our CSS to some CDN, and this will be loaded in normal order as a separate HTTP request. And optimize, we just create a style tag on our initial index.html and inline the styles within this section.
>> Evgenii Ray: And how do we fetch non-critical styles ideally?

[00:02:11]
So there are two ways how can we do that. The first one, we can tell the browser that these styles are used for printer, this is like a life hack. So when this the media type is print, then the browser will not load this immediately. But here we see there is a little hack.

[00:02:35]
So we have onload event. So when the content is loaded, we change the media type to all. This will instruct the browser to load the following CSS when the whole page is loaded. So, alternatively, you can also use the preload parameter to load the non-critical CSS under the low priority.

[00:03:00]
So both solutions would work. Summary, so how do we optimize the network performance? First of all, split the bundle. So now when we have the HTTP/2, split as much as possible. Because usually, generally, it's better to load things in parallel. And minify and compress, this is basically included in every bundle now.

[00:03:31]
And also inline critical resources to optimize the first screen rendering. And load the non-critical resources on the background under low priority. So in the bright future, I hope that we will not have any bundler, because everything will be supported natively, and we will not need to actually prepare the assets anyhow.

[00:03:53]
But for now, yeah, we will we have to introduce the boundary in our application. Okay, so what about other assets?
>> Speaker 2: Quick question on the CSS thing in the previous example. It seems like preload would be sort of the like, quote unquote, right way to do it, whereas the print one is a little hackier.

[00:04:13]
Is there a reason you would ever use the print one over?
>> Evgenii Ray: So the thing about preload that, so, you mentioned that you are loading the page, and when the browser reads the preload, it still fires the request. But in case, in the media print, it will fire the request only when the whole page is rendered, so the ordering will be slightly different.

[00:04:39]
And utilizing the preload too much may lead to having too many assets being queued, so you really need to consider case-by-case how you want to load the resources. So what about the images? So the images have multiple types, so it can be animated content, it can be icon or logos, it can be also a raster graphic or the photos.

[00:05:06]
For animated content, usually we use the GIF, which is GIF. And the GIF is not the best way to represent animated content because the size of the GIF, even for three seconds, it can be 10 megabytes. So usually, we replace the GIF with a MP4, or we can also use the WebP.

[00:05:29]
And for icons and logos, we use SVG, and we can optimize SVG by using compressed SVG. And for the raster graphic, there's a PNG. But there is a newer format called webp, which is basically built to replace the PNG and JPEG. And we can utilize it because it provides the better compression ratio without the quality loss.

[00:05:56]
And the next format, which is relatively new one, is the AVIF, which provides the best compression rate for the photo content. So if you're building the photo website and you need to provide high-quality images, then AVIF will provide you the best compression ratio to quality.
>> Evgenii Ray: And so let's go through these assets.

[00:06:21]
So for instance, the following 60-kilobyte GIF can be the same 5-kilobyte webp. So it's very important to choose the right format. Also, if you look at the webp and/or AVIF, and we can see uncompressed image quality. So the original JPEG is 10 megabytes, the WebP is 7.5 megabytes, while the AVIF is 6.2 megabytes.

[00:06:48]
So uncompressed image is 38% less than the JPEG, which is pretty good, and also AVIF allows us to compress even image even farther without a big data loss comparing to JPEG. So anyway, when you're building your website, it's a good practice to compress images. Because when we browse the website, we don't really need to see the full size of the image.

[00:07:18]
And so the question is, should we actually use these formats? And I think we should, because right now, the webp is supported by 97% of the clients, while the AVIF is supported by 93% of the clients. So we'll see, I think all these formats will become pretty standard.

[00:07:37]
They already are the standard, to be honest. And if you're still supporting some legacy clients, you can always fall back, you provide a fallback to JPEG in case if the WebP image is not supported.
>> Evgenii Ray: Same applies to SVG. So when you have your SVG, you can actually compress that, so it's called SVG path compression.

[00:08:03]
And we already have extension to our bundlers that do this automated job when you do the build of the bundle. So basically, we can express the same SVG using slightly different semantics. And by simply removing a few symbols, we compress the SVG by more than 50%. And if you have lots of sprites, SVG-based sprites, then you probably could have, if you have the large SVG library icon set, then you can optimize the SVG significantly.

[00:08:42]
And this is the number without the zip compression or brotli, so it can be even optimized even further. So the summary for the image section. So compress images for web and use optimized formats. So now the optimized formats become the standard for the web. And make sure that if you use SVG, use SVG path compression.

[00:09:08]
>> Evgenii Ray: Okay, what about the fonts? A few interesting characteristics of the font is if you use any custom font on your application, then the browser behavior is it waits three seconds to load the font, and then it renders the content. If the font is not loaded within three seconds, it will render the content using unstyled text.

[00:09:32]
So I think this, this part is not pretty good because, for instance, our CDN that stores the fonts fails, and we don't want to not show in the screen until the font is loaded. So we could potentially sacrifice some beautiness of the app and provide the font display fallback.

[00:09:53]
This will instruct the browser to render the unstyled text immediately without waiting for three seconds. If the font is loaded, then we do the runtime switch of the font. And there is also the value called optional. So basically, we render unstyled text immediately. But when we load the font, we just store and cache it.

[00:10:18]
And when we refresh the page, we just get the value from cache and render the font as it should be. You can choose whatever suits your application and the design requirements. So I think the fallback is pretty good alternative. Just make sure that you provide that because default value is auto.

[00:10:43]
And here is the summary of what we discussed. You can just store this diagram and it will provide you the shortcut what we need to do with the app. So, and let's summarize the rendering performance. So we're not creating a new section in slides because we actually discussed this already, how to optimize the rendering performance.

[00:11:06]
So first of all, it's the DOM. Minimize the number of DOM you use within your DOM tree, and use virtualization when it's necessary. So minimize the number of graphic layers, but use them wisely when you need to optimize certain things. And also, avoid any potential reflows and just use the separate stacking context to isolate the elements mutation, and also avoid using the complex CSS selectors.

[00:11:38]
Try to implement some naming strategy so you can use any methodology that suit your application or use any CSS frameworks. So because the time it takes to compile the long CSS selector may impact your rendering performance if you have hundreds of complex selectors in the app. And also, try to utilize the CSS animations in your app, because they are GPU-accelerated, so you will not block your rendering thread.

[00:12:11]
And as a good practice, use the placeholders and the loaders to give some, use visual feedback while your application is loading. The JavaScript performance summary is very simple. The rule of thumb, don't block the UI thread at any cost. And you can achieve that by reducing your CPU usage and optimizing your search and access cost to the data.

[00:12:37]
And you need to reduce your RAM usage. And when you have lots of data that you store it in your global state, make sure you put this into some kind of hard drive storage. So IndexedDB is a very good example. And if you need to write and read your data, avoid putting this data into the any synchronous storage, such as local storage or session storage, because it will block your UI thread.

[00:13:06]
And also one thing if you can utilize a synchronous job. So now that the Web Workers support it in many clients, you can put some heavy tasks to the Web Worker and allow Web Worker to sort it out in a separate thread, or you can utilize the server.

[00:13:25]
And that's it. So we've done all the sections of the presentation now, and I hope that all the sections were useful to you. And now, I think it's time for us to apply this knowledge in practice by actually doing the mock system design interview, we're gonna set up the board.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now