davidwiles.net (This Website)

January 2nd, 2023

This website was created without the use of a static site generator or other tools that work "out-of-the-box". This is partly due to my desire to create something new, but mostly because I didn't want to learn a new framework just to create a website. I wanted to create a website that was fast, cheap, and easy to maintain. I use yarn for project management and webpack to process JavaScript to make maintenance and reproducibility a bit simpler.

This website is just HTML, which makes it as fast as possible. My index.html loads a total of 80.48kB over 4 requests (the html, the fonts, and the favicon). If you already have the fonts and favicon cached by your browser, the page load will only be 4.39kB! I am able to limit the number of requests per page load by inlining all JS and CSS.

I host everything on my site from my site, no CDNs used at all. This makes it a bit slower since it is all hosted from a free-tier static site on Render, but it ensures complete privacy for you. Loading index.html for me with a private window in Firefox takes about 1.33s: 810ms for DOMContentLoaded to fire and ~0.5s to load the fonts and favicon. A cached page load will be 419ms: 107ms for DOMContentLoaded and ~300ms for the rest; not bad for a website hosted for free.

This project is composed of three parts (as is any website): HTML, CSS, and JavaScript.

HTML: Custom Template Engine

My custom template engine is written with Scala Native and uses a syntax based on the Go template syntax. The only features it has are:

These features are just enough to minimize duplicated code while still making it as easy as writing pure HTML. You can find the source code for the project at github.com/david-wiles/templater. I currently use this template engine with a short bash script that walks through the html directory and builds the HTML files.

SCSS to CSS

node-sass is easily the most heavyweight dependency of this project, but I do think that it is necessary if only for the benefit of maintainability. This adds variables, macros, modules, and selector nesting to CSS. Making a change to a nested selector in CSS is a nightmare, but in SCSS it can be a single-line change. Although there is talk of updating the CSS standard, SCSS probably won't be going away due to how CSS parsing works.

Vanilla JavaScript

There isn't really any need for JavaScript on my site, but it is nice to have some interactive features. Other than prismJS for code highlighting, the only JavaScript on the page is used for theme toggling (check out the moon icon in the upper-righthand corner!).

Script and StyleSheet Inlining

CSS and JS inlining is somewhat of a "secret sauce" to get sites to load faster. Replacing <link> and <script> tags with the actual source for those links prevents the browser from needing to make extra HTTP requests and lets the browser's parser continue through the page without needing to stop. It is relatively easy to add this to a project. I have a script, inline.js, in my project which does this in a separate step during my yarn run build:prod compilation.

First, the script reads the given directory's files:


let files = fs.readdirSync(dir);
files.forEach((file) => { })
  

If the "file" is a directory, we recursively call the script's main function again. Otherwise, we search for links and scripts in the text:


let html = fs.readFileSync(file).toString();
let links = findLinks(html);
  

findLinks will use regex to search the text


let scripts = html.matchAll(/<script\s+(defer){0}\s*src="([.a-zA-Z0-9-\/]+\.js)"\s*(defer){0}\s*><\/script>/gm);
let result = scripts.next();

// snip

let stylesheets = html.matchAll(/<link\s+rel="stylesheet"\s+href="([.a-zA-Z0-9-\/]+\.css)"\s*\/>/gm);
result = stylesheets.next();
  

Finally, the matching file's contents will replace those link and script tags


links.forEach((link) => {
  if (cache[link.file] === undefined) {
    cache[link.file] = link.type === "js" ?
      "<script>" + fs.readFileSync(base + link.file).toString() + "</script>" :
      "<style>" + fs.readFileSync(base + link.file).toString() + "</style>";
  }
  html = html.replace(link.tag, function () { return cache[link.file]; });
});
  

This file can be found at inline.js, and the entire source code of this website is at github.com/david-wiles/davidwiles.net.