Back to home page
How I built this site

Overview and motivation

The main goal was to have something slick and cute that would exist independently of social networks. Something lightweight that works in all browsers, is accessible to people with disabilities, and is easy to maintain.

I built it all by myself, working evenings over a week. Most of it is custom, even if I used a lot of open source tools and libraries. I didn't mind if it took a while, or if I reinvented the wheel, or if it ended up a bit overkill. I have plenty of experience doing frontend work, in and out of a web browser, and I've used both Angular and Vue quite a bit. I prefer TypeScript but I know JavaScript. I also couldn't contribute more than a scant few hours each day to it, and had no expectations going in other than a desire to make something cool. At the end of the day, if something is fun doing, it's always worth doing.

Requirements

I immediately established my goals and constraints:
  1. Pure HTML and CSS, no JavaScript (and should even work with scripts disabled)
  2. All local, no backend besides whatever serves the static files
  3. Responsive design that looks good on both desktop and mobile, on somewhat up to date browsers
  4. Dark and light theme selected from user preference
  5. Easy to update and tweak as things in my life change

Styling

Now, point #1 didn't mean I couldn't use JS at all. I can still use JS tools and scripts to automate some parts of the development, as long as there's no JS in the end result. To that end, I initially setup an npm project so I could use it to install dependencies. From the get go, I knew I wanted to use Tailwind for points #3 and #4.

First snag: Tailwind is often intended to work with a JS framework like Angular, React or Vue, but that wouldn't work with point #1. No worries, they have a command line tool with file watching support, so I can just run that in the background while I edit the HTML by hand. Brilliant!

That went well until I tried to include Font Awesome for icons. I specifically did not want to have to include FA's CSS and webfonts directly in my project and version them, since I prefer to grab them from npm. But that means the assets are somewhere in node_modules, and I didn't want to import them in my Tailwind generated CSS because that would dump it all in the same file and I didn't know how that would play with Font Awesome's license. So clearly, I needed a different setup, something that could both generate the CSS for Tailwind while also copying other assets like images and Font Awesome stuff.

Layout

Enter Gulp . Gulp is an automation framework written JavaScript. Its main use case is making build pipelines for JS applications. It offers a lot out of the box, and it's easy to extend it with plugins. One of those plugins allows running PostCSS, which is what Tailwind is built on. With that, I could generate my site CSS with the following Gulp task:

import postcss from 'gulp-postcss';

function css() {
    return src('*.css')
            .pipe(postcss())
            .pipe(dest(destination));
}
Gulp also has a file watching feature, which I can use to automatically run tasks whenever certain files change.

With that, I can use Gulp to generate the Tailwind CSS and copy other assets when I run a "build" task, and also have a "dev" task that builds on file change. I can then define npm scripts that run gulp and launch npm run build to build my project, placing assets in a dist directory. As I work, I just need to save the file and then hit F5 to see the changes. It doesn't reload the page automatically, but maybe I can find a way to make it work later.

Then I started working on my resume. I could have just linked to a PDF, but I wanted to also write it in HTML and CSS as a kind of unnecessary flex, but I didn't want to have to duplicate the whole site layout, even if there's not a lot of it. But then it hit me: I have a build pipeline, I can have it also generate HTML from a template! Some quick research later, I settled on Nunjucks , which I then hooked to Gulp with a plugin:

import nunjucks from 'gulp-nunjucks-render';

function html() {
    return src('templates/pages/*.nunjucks')
            .pipe(nunjucks({
                path: 'templates/'
            }))
            .pipe(dest(destination));
}
With that, I can split my HTML into multiple templates and generate HTML from them. A watcher regenerates HTML whenever a template file changes. I can even define macros for commonly encountered elements like links to ensure they're styled properly.

This also had a neat side effect: on my resume, the layout is designed to be responsive so you get 2 columns on desktop and one column on mobile. But the resume also has to be printable, and CSS grid and flex don't play nice with page breaks. I could use an HTML table, but that would sacrifice responsiveness. The solution? Have a "browser" version with grid/flex and a "print" version with a table with Tailwind classes selecting which is visible. Using Nunjucks includes, I can avoid actually duplicating the source markup. It technically means I'm rendering the page twice, but bandwidth is cheap these days and compression is easy.

Deployment

This step was fairly straightforward. I push this site's code to a Github repository, which has a Github Actions workflow that installs the dependencies via npm, generates the site's assets by running my Gulp pipeline, an finally uploads the generated assets to Cloudflare Pages with Wrangler. I point my domain name to that and it just works. It also means I'm respecting point #2 since, no backend. I'm okay with the free plan, so the only expense if the domain name, really.

Looking Back

This was a fun few days. I learned new things, some of which I might considering applying to my day job later, and I'm fairly satisfied with the result. It's easily extensible (I added syntax higlighting via highlight.js while I was writing this article), and most importantly, it's mine.