Image featuring the text 'tobiasm.dev' on top of a color gradient.

Building tobiasm.dev with Eleventy

How I built my personal website using Eleventy and its plugins, including a section on performance optimization.

published (updated )

Tags:

Contents

With the launch of tobiasm.dev I figured it'd be a good idea to write a little piece about it. I'll touch on my choice of Eleventy for static-site generator, the external plugins and assets used in the build-process and even some deployment notes.

Choosing a static-site generator: Hugo vs. Eleventy

Since a blog and project showcase is largely static a static-site generator seemed like a perfect fit.

Initially I decided to try out Hugo. While I was thoroughly impressed with its build-time performance, it wasn't as flexible and extensible as I'd liked. Given that it is written in Go and therefore pre-compiled, either your desired functionality is already part of the generator, or you're out of luck. The documentation is also tough to step through, best exemplified by its template lookup order: A huge list of lists with dozens of slightly different names for the different kinds of templates and layouts.

After doing a little more research I found a lot recommendations for Eleventy. Instead of delivering all features in one package, Eleventy's core has fewer batteries included by default, but can be easily extended with plugins, both first- and third-party. Want HTML / CSS / JS minification for your output but that feature isn't included out-of-the-box? No problem, just install a minification package from NPM, hook it up with a filter or transform and you're good to go. The fact that Eleventy's configuration is itself a JS-file, allowing you to add all manner of pre- and post-processing, makes configuration a breeze.

I settled on Eleventy and have had a fantastic time working with it.

Custom design, layout and templates

In order to sharpen my web development skills I decided to build all the layouts, templates, markup and styles myself. On top of being a great learning experience it also gave me full control over all the features I wanted to include, such as a CSS-only dark-mode / light-mode toggle that also respects the browser's builtin preference or an always-visible top-bar that collapses into a burger-menu on mobile.

To ensure a consistent viewing experience across browsers and operating systems I use two web fonts: Inter is the core font used basically everywhere, including standard text and headlines. Source Code Pro is used in monospace contexts.

Enhancing the site with Eleventy's awesome plugins

One of Eleventy's greatest strengths is its plugin ecosystem. I use a number of them on this site:

Optimizing performance

As I was building the site, one of the central throughlines was a focus on performance. It's a problem that can be tackled from two sides: How the site is built, and how the site is served to the user. The Lighthouse tool built into Chromium has proven incredibly useful in finding ways to optimize the site's performance (and accessibility, but more on that later).

Measures taken to improve the site's build include the following:

  • All render-blocking HTML, CSS and JS is inlined to minimize the time to First Contentful Paint.
  • Eleventy's Image plugin generates all the images and markup necessary to serve reponsive images using the <picture>-tag.
    • This allows me use state-of-the-art image formats such as AVIF while providing WEBP and JPEG-fallbacks for older browsers that don't support AVIF.
    • It also allows me to provide an image in multiple resolutions, with the browser automatically choosing the one that's optimal for the device's resolution.
  • All web-fonts have been subsetted using glyphhanger, removing all the glyphs from the font files that aren't actually used on the site.
    • I cannot stress how significant this is for reducing total bytes transferred.
      For example, Inter's variable-width WOFF2-file went from 338 KiB to 74 KiB, a 78% reduction.

In order to optimize how the site is served I am renting a VPS at Netcup, giving me full control over the stack of technologies used to serve requests. I deploy nginx behind the reverse-proxy Traefik, both managed as Docker containers with Docker Compose.

To optimize requests all static assets are served with an an efficient cache policy, significantly speeding up subsequent requests. Of course, any assets not already compressed are served with gzip-compression. Additionally, Traefik's builtin support allows me to serve requests via HTTP/3.

HTTPS is enforced by default thanks to Chrome's HSTS-preload list. Since the .dev-TLD is included on that list by default I don't even need to send any HSTS-headers. Any modern browser will only connect to the site via HTTPS. Of course, an HTTP-to-HTTPS redirect is still in place.

Finally, this site should always be reachable via IPv4 and IPv6. It still boggles my mind that two decades after IPv6 first going live there are still major websites (and even hosters!) out there that don't support IPv6 at all.

Accessibility as a first-class concern

The other major throughline while building the site was accessibility. I've made a conscious effort to use semantic markup everwhere and only use <div> and <span> elements where appropriate. The keyboard-tab-order on the site should be consistent and I've also ensured all colors have sufficient contrast, in both the light and dark theme.

That said, I am by no means an expert and have undoubtedly made mistakes. If you have found an issue on the site that needs fixing, accessibility-related or otherwise, I'd love to hear from you.