Building Static Websites for Dummies~
html webdev
Okay, this is a quick post to get back into things! I like to build my own websites (see this page hehe), but I'm not a web developer and sadly I don't keep up to date on the best practices and tooling for doing so. Recently, I decided to redesign this site (you're browsing it on the new design, do you like it?~), and in this process discovered a bunch of cool things on how to do webdev quickly.
This is a little quick guide to as I understand it, a good starting point of hints and tips and tricks for myself and other people for writing static webpages quickly.
Tip 1: Live-server
My usual go-to for previewing websites locally in the past has been to use Python's built-in web server:
$ cd project/
$ python3 -m http.server --cgi 8080
This kinda works mostly, but the problem is that when I update the HTML for my site, then I need to manually refresh the browser to see my changes.
So, now to introduce the trick, the node-js community have this super
nifty webserver called live-server
(link).
It does the same as the python server… but, it installs a script into the website and configures it such that whenever the server notices that the source files have changed, then your webpage will automatically refresh itself.
To install it:
$ npm install -g live-server
Then, you can just use it as a simple replacement for anywhere you would have used the python server:
$ cd project/
$ live-server
If there's nothing else you get from this blog post, please, please, pleaseeeeee, try out this package, it is honestly such an amazing life-saver, it allowed me to iterate sooo much faster, I can not believe how I ever lived without it!!!
Tip 2: Eleventy (or 11ty) for static site generation
Next, writing HTML by hand can be a bit of a pain, so the next thing I usually like to do is to set up some kind of means of generating it.
Now, my usual go-to to do this is to use Jekyll, but when I'm trying to quickly set up a custom site, Jekyll comes out of the box with too many defaults on its site structure, and the way in which posts are generated and such. Jekyll's behaviour is generally good for quickly making a blog and such, but when I'm trying to build a site from scratch, the extra effort to reset all of Jekyll's helpful defaults is a pain.
So, for a quick barebones site generation with all the goodies (like
file-watching and such) that you'd expect, I'd recommend eleventy
(link).
Eleventy is very easy to setup, quoting from their home page:
… Now we'll create an
index.md
Markdown file. …echo '# Heading > index.md'
Once you have some source files, then to run the static site generation:
Run Eleventy using
npx
, an npm-provided command that is bundled with Node.js.npx @11ty/eleventy --serve
With just these two commands you'll be up and running, and Eleventy will start building your site from your source language:
[11ty] Writing _site/index.html from ./index.md (liquid)
[11ty] Wrote 1 file in 0.03 seconds (v3.0.0)
[11ty] Watching…
[11ty] Server at http://localhost:8080/
In this case, by default, Eleventy writes the build output into a
_site
directory, and you can pair this with live-server
to
automatically refresh your webpage whenever the source files change
and the project is rebuilt.
Tip 4: Tailwind (+ Pug)
The final part of JS-witchery that I had to learn about and work out, was Tailwind (link), which is also pretty rad, and integrates nicely with pug. I'd heard a lot about Tailwind from like my web-dev friends, but I'd never dug into it, so it just seemed like magic.
In essence, Tailwind is framework for allowing developers to quickly build styles by modifying HTML directly rather than having to do a dance of modifying CSS styles and HTML at the same time.
So, rather than writing something like:
my-div {
display: flex;
margin-left: auto;
margin-right: auto;
}
Then going and also modifying your HTML to add my-div
to a class in my
HTML, using tailwind, we'd elide writing a custom class in the first
place, and instead write the following HTML directly:
<div class="flex mx-auto">
...
</div>
In other words, Tailwind defines a collection of essentially single-property classes out of the box that users can then mix and match on their HTML to get the desired styles they want.
When you want a particular property for a HTML object, you can search up the property on the Tailwind site (link). It takes a bit of time to get used to the names that Tailwind gives to each of these classes, but they're fairly systematically designed and once you figure out the patterns you can usually work out the name without even having to look it up!
Another cool thing is that this methodology integrates amazingly with Pug!
div.flex.mx-auto
...
Pug seems to not be that popular with webdevs for some reason, and I honestly don't know the reason why, it's honestly so amazing aaaaah omgomgomg!
Now Tailwind also involves some preprocessing — to avoid generating overly-large css files with all of the Tailwind classes present in it, Tailwind itself provides a preprocessor to which you can feed in your html/pug files and it will only generate the classes that you use.
I think there might be a way to integrate Tailwind into Eleventy directly so this preprocessing happens through Eleventy itself, but I wasn't able to work out how exactly.
In the end, a good pipeline that I found worked for me was to run Tailwind's preprocessor concurrently with Eleventy and then configure Eleventy to treat Tailwind's output css file as one of its inputs.
So to do this, I used the following scripts in my package.json
file:
{
...
"scripts": {
// script for running eleventy in watch mode
"11ty:watch": "cross-env NODE_ENV=development eleventy --watch --incremental",
// script for running tailwind in watch mode
"css:watch": "tailwindcss -i ./style/base.css -o ./_includes/style.css -w --postcss",
// when serving, run tailwind and eleventy concurrently
"serve": "concurrently -c auto npm:css:watch npm:11ty:serve",
},
...
}
You'll need the concurrently
Node.js package installed for this (and
eleventy
and tailwind
if you don't have it installed already).
The above rule tells Tailwind's preprocessor to treat ./style/base.css
as the raw source CSS files to be preprocessed and extended with
Tailwind's built-in classes, and then outputs it to
`./includes/style.css` for Eleventy to process.
Finally, I added the following rule to my Eleventy config to tell it
to treat the output of Tailwind, ./_includes/style.css
as just another
one of it's input source files, so each time Tailwind updates its
output, this triggers Eleventy to also rebuild automatically!
// file: eleventy.config.js
import pugPlugin from "@11ty/eleventy-plugin-pug";
export default function (eleventyConfig) {
...
// copy over static files
eleventyConfig.addPassthroughCopy({
'_includes/style.css': './style.css'
});
}
Putting it all together, tailwind
, pug
, eleventy
and live-server
now
you'll have a some simple Pug source files for which you can quickly
apply styles and then have your web browser page update almost
immediately.
Final Comments
Okay, so that's about all the tips and tricks I had for static site generation!
I found this whole pipeline to be a complete game-changer for how I can quickly prototype website designs, and authoring pug html with tailwind styles was so quick and ergonomic I was able to build up the stylesheet for this website very quickly (admittedly a fair bit of this stylesheet was stolen from the Gov.uk designs, but this pipeline still helped a great deal).
One final comment I want to make is that the Tailwind community actually proposes a workflow where the tailwind classes are actually used directly in the final project, and users never write custom classes at all, I think it's called utility-driven design or something.
I think this makes sense if you're a big website where your designs are going to be iterated on frequently, but for my purposes I felt it looked a bit ugly having all the tailwind classes present in my HTML, so once I'd figured out the styles I wanted, I then went back and created custom classes for each one.
Tailwind actually also makes this quite easy as it provides an @apply
construct for including the tailwind properties into a custom class.
.figure-caption {
@apply text-center mt-1;
}