HTML Rendering: An Important Lesson
Tags: webdev, frontend, bootstrap, css
Published: 2019-01-22 21:10:15

---
title: HTML Rendering: An Important Lesson
published: true
description: A simple trick I used to reduce the loading time of a new site that I was designing
tags: webdev, front-end, bootstrap, css
cover_image: https://images.unsplash.com/photo-1454166155302-ef4863c27e70?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80
---

_Header image: [Field notes and pencil](https://unsplash.com/photos/EPppwcVTZEo) by [helloquence](https://unsplash.com/@helloquence)_

I put out a tweet at the weekend:

{% twitter 1086632262702108672 %}

And I thought that I'd explain just what was going on and how I'd managed to speed up page render time from almost 3 seconds down to 700 milliseconds without (and this is the key part) needing to use any kind of caching.

One of the things you need to know going into this, is that I'm not great at front end development - I'm about average at it, I'd say. Which is why this (what I eventually found out) was a bit of a eureka moment to me. But it also makes perfect sense.

## The Site

Firstly you need to understand the original state of the site that I was attempting to optimise. First, the HTML which seems unassuming:

``` html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
    <link rel="stylesheet" href="dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="dist/css/custom.min.css">
</head>

<body>
  <main class="page landing-page">
        <section class="clean-block clean-hero" id="hero">
            <div class="text">
                <h2>Jamie "GaProgMan" Taylor<br></h2>
                <p>.NET Core Developer; Podcaster; Writer</p>
            </div>
        </section>
  </main>
</body>
</html>
```

Then some of the contents of `custom.css`:

``` css
/* other rules, which override
 * default bootstrap styles here
 */

#hero {	
  background-image: url('../img/header.jpg');	
  color: rgba(0,92,151,0.85);	
}
```

`custom.css` was designed as a file which overrides some of the default bootstrap styles, so it must be loaded in after bootstrap. Which is why the order of the css files is important.

All of this creates something which matches the following screenshot:

![My current site design](https://thepracticaldev.s3.amazonaws.com/i/j6l5xjxh97crwcqx5xqo.jpg)

## The Issue - And How Browsers* Render HTML

`*` = some browsers anyway

I'm about to explain how browsers do a fetch, render, fetch, and repaint. I'm about to get the minutiae wrong, as I'm going to be doing it from a very high level. But the idea is, kind of, right.


So when the page is accessed, the above HMTL is downloaded. The browser starts to parse the HTML and sends off requests for the css files that are linked. With me so far?

Because `bootstrap.min.css` is requested first, regardless of whether `custom.min.css` comes back faster or not, the browser will parse and apply the rules from `bootstrap.min.css` first. The browser will only apply the style rules from custom once bootstrap has been applied.

This means that the div with the "hero" Id will not have the `background-image` rule applied until _AFTER_ bootstrap has been downloaded and applied. Even then, the browser needs to then download the image, too. So we'll be waiting a long time for the image to download.

When I was testing this, on an intentionally throttled internet connection

_I aim to design sites from a mobile first point of view; when I'm doing that, I test them from a mobile first point of view too. This usually means that I throttle my internet connection down to a low-quality 3G connection during my testing loop_

I found that the header image would take, on average 2-3 seconds to load and appear.

Not good.

### What's a Dev To Do?

I spent a little time thinking about what I could do. I made the header image smaller; I put it through tools like [TinyPng](https://tinypng.com/); I moved the image to a very fast server; I even looked at using a caching reverse proxy service like [CloudFlare](https://www.cloudflare.com/).

I looked into using Brotli to compress the image at the server (but that wouldn't work on some browsers); I looked into using using the  [`srcset` attribute](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images); I even looked into using server-side caching.

Then I went for lunch.

When I got back from lunch, I stripped out bootstrap and realised that the bottleneck became how fast the server could serve the image. Which, over my throttled connection, took around 400 ms.

> Wait a minute. Why would bootstrap be causing this?

It wasn't.

### CSS File Order

Remember when I said earlier that we had to wait for bootstrap to be downloaded and applied before "hero"s background-image rule could be applied? Well, why not create a separate css file which just had that rule?

So I rewrote the HTML:

``` html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
    <link rel="stylesheet" href="dist/css/header.min.css">
    <link rel="stylesheet" href="dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="dist/css/custom.min.css">
</head>

<body>
  <main class="page landing-page">
        <section class="clean-block clean-hero" id="hero">
            <div class="text">
                <h2>Jamie "GaProgMan" Taylor<br></h2>
                <p>.NET Core Developer; Podcaster; Writer</p>
            </div>
        </section>
  </main>
</body>
</html>
```

and the only rule in `header.min.css` was:

``` css
#hero {	
  background-image: url('../img/header.jpg');	
  color: rgba(0,92,151,0.85);	
}
```

This meant that bootstrap didn't need to be loaded before the image had to be downloaded.

Great success! The hero banner and nav at the top of the page would load in around 600ms. Since the design placed these items (and nothing else) above the fold, it meant that I wasn't bothered about how long the rest of the content took to load.

### But We Can Do Better

The CSS file was great, and super fast. But the browser had to make one more round trip to the server in order to download the background image. So how to we reduce that?

We can't assume that the server will support http2, which would allow us to push more content to the client in parallel. So what could I do?

Enter: Base64 strings

For those who don't know, [Base64](https://en.wikipedia.org/wiki/Base64) is a way of encoding strings - it's in no way secure, but can be useful if you need to transmit binary data via an [ASCII](https://en.wikipedia.org/wiki/ASCII) only medium.

You can take an image, encode it as a base64 string, and use that string as the value of the `src` attribute for an image element. This technique does have it's drawbacks, tough - the chief two being:

- [browser support isn't perfect](https://caniuse.com/#feat=datauri)
  - unless you don't care about targeting Microsoft browsers
- because of the sheer length of base64 representation of an image, it can take a little longer to download
  - the base64 string for the screen shot of the rendered website design at the top of this post is 80,161 characters long for instance

_BUT_ if you combine a base64 string with gzip, you can get pretty good transfer speeds. So I generated a base64 string for my hero image and altered the `header.min.css` file:

``` css
#hero {	
  background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAUEBAQEAwUEBAQGBQUGCA0ICAcHC /* and the rest of the base64 string here*/ 5c1O5zUrxIMGEJWl6f/2Q=');
  color: rgba(0,92,151,0.85);	
}
```

This no longer required the extra round trip to the server, which shaved another 50ms from the initial download. With the added bonus that the css would be cached in the browser as soon as it was downloaded.

### Reflections

I didn't really need to go the extra step (using a base64 string) but, if I'm honest with you, I was simply using it as an experiment to see whether it could be faster.

It's this kind of micro optimisation (moving simpler css rules above blocking calls) which can really make a huge effect on the perceived speed of your sites. Like I said at the top of this article, I'm not the best at front end dev, but thought I'd share this.

Also, I've probably not done this in a way that is even vaguely "correct" or an optimised way. If you were me, what would you have done?