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.
Requirements
I immediately established my goals and constraints:- Pure HTML and CSS, no JavaScript (and should even work with scripts disabled)
- All local, no backend besides whatever serves the static files
- Responsive design that looks good on both desktop and mobile, on somewhat up to date browsers
- Dark and light theme selected from user preference
- 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.