I've had quite a few blogs over the years. I don't quite remember which was my first. Maybe it was xanga? Or did livejournal come first? I've had more than a few wordpresses and tumblrs over the years. There's nothing wrong with these platforms, and if I could only stick with one blog and one platform over the years I'd probably write more, and have some kind of following. But writing is the hard part, and rebuilding a blog over and over again is the fun part. Just like the allure of a brand new notebook, fresh with possibilities, a new blog carries the same for me. It feels better to start fresh every few years. How long will this one last? Who can say.
So here we are, and as the first post here, I'm obligated to now describe to you how I made it with eleventy. The eleventy docs are much more helpful than I'll be, but sometimes docs are overwhelming and you just want to read a blog post. So here's another to add to the web.
My Goals for the blog
- I wanted it to feel like I had built this site all myself with as little magic as possible.
- Paradoxically, I also wanted all of deployment and dev-ops stuff to feel as easy as basically pressing a button.
- I wanted it to feel easy for me to write in. I personally enjoy writing in markdown and in a code editor.
- I wanted it to use tech I was already pretty familiar with (hopefully mostly javascript/html/css)
- I wanted it to be fully customizable. I don't want to feel like I'm forced into some kind of template.
- I wanted to design the UI myself, and I wanted something very minimalist and simple. I wanted it to feel a bit 90 retro but not so much so that it was hideous to look at. I'm not a designer, but I also wanted to own the design myself, rather than use a template. I guess I've got the ego of a designer without the artistic skills. Anyway, black and white and some fancy borders seemed doable.
About Eleventy
I heard about eleventy from my colleagues while working at Glitch. It was really neat then, it's even more neat now, as the project continues to grow and develop. Here's a few things that made it the right fit for this project:
It's a static site generator, meaning that it builds all of my blog posts into html files for me. In my day job, I tend to write a lot of single page web apps. This is fine for a big hunk of interactive software that stands behind a login screen. But react and it's like tend to be overkill for building a good old fashioned weblog. I don't have a bunch of state to manage. I have documents to organize and link together. Javascript is slow. It takes a bit to download, then takes another bit to turn itself into html elements on the dom. Already built HTML files on the other hand download quickly and are quick to render, and are great for SEO and accessibility. But I didn't want to write custom html everytime I had a thought. So there we go-- static site generator time.
Ok, ok, but there are hundreds of Static Site Generators. Why Eleventy? Eleventy's website seems to tell you that it's because they are fast to build. I think that's probably very true. But honestly I don't care a lot about how quickly my blog is to build. Like... maybe someday I will care if I ever have 1000s of blog posts to compile into html. But let's be real I'll be very lucky if this baby ever reaches 10 blog posts. This site is more likely going to be landing page that redirects recruiters to my linkedin.
I think for me, the appeal is it's simplicity (which probably is what enables that build performance). It's simple enough that I think I can walk through the basics of how it works right now.
first let's take a look at the file structure before we build it:
blog/
|-- source/
| |-- layouts/
| | |-- index.njk
| | |-- post.njk
| |-- public/
| | |-- styles.css
| |-- posts/
| | |-- some-post.md
| |-- index.md
|-- .eleventy.js
|-- package.json
Pretty small, nice!
The package.json file
Let's open the package.json:
{
"name": "sarah zinger's weblog",
"version": "1.0.0",
"scripts": {
"build": "npx @11ty/eleventy",
"start": "npx @11ty/eleventy --serve"
},
"devDependencies": {
"@11ty/eleventy": "2.0.1"
},
"dependencies": {}
}
Pretty small! No runtime dependencies! I have 2 build scripts, npm start
that I use when running locally, and npm build
which builds a production build for me.
Eleventy Config File
The magic happens in the .eleventy.js file. Technically you don't need one, but if you want to do any custom configuration, this is where the magic happens. Here's a simplified version of mine:
module.exports = function (eleventyConfig) {
eleventyConfig.addPassthroughCopy("source/public");
return {
dir: {
input: "source",
output: "build",
includes: "layouts",
}
}
};
All we're really doing here is:
- specifying that any file in the source folder should be considered "input" that should be processed by eleventy
- if the files reference layout files, eleventy will assume they are located in the "layouts" folder (for some reason eleventy calls this "includes" by default)
- eleventy will let all files in the "source/public" folder "passthrough" aka just leave them as is and copy them over to the build file. This is good for things like css, images, js, etc.
So let's take a look at what the file structure looks like after we run npm run build
:
blog/
|-- source/
| |-- layouts/
| | |-- index.njk
| | |-- post.njk
| |-- public/
| | |-- styles.css
| |-- posts/
| | |-- some-post.md
| |-- index.md
|-- build/
| |-- posts/
| | |-- some-post
| | | |-- index.html
| |-- public/
| | |-- styles.css
| |-- index.md
|-- .eleventy.js
|-- package.json
You can see my some-post.md has become it's own html file, and anything in public is just copied as is.
Layouts and templates
I'll be honest, I don't love templating languages, but eleventy supports many to choose from, and they do make life easier. Most of their docs seem to have a bias for nunjucks so that's what I used. It's used by mozilla, so I imagine it's not going away anytime soon, so far no real complaints. Here's an example of one of my nunjucks layout files:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<link rel="stylesheet" href="/public/styles.css">
</head>
<body>
<main>
{{ content | safe }}
</main>
</body>
</html>
You can do a lot more with it, but I have kept it pretty simple here. When used it's going to take whatever content is there and run it through a "safe" function (aka take this and render it as html, we don't think this is malicious code).
To then use this layout we can add some "front matter" to our post referencing the name of this file. So for example if the layout I just shared above was called "some-template.njk" I could make a markdown file like so:
---
layout: some-template.njk
title: some post
tags: post
---
# Pretend post
blah blah
## can use headers and other markdown
Also html <b>works</b>
then eleventy would create a matching html using that layout to look like this:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>some post</title>
<link rel="stylesheet" href="/public/styles.css">
</head>
<body>
<main>
<h1>Pretend post</h1>
<p>blah blah</p>
<h2>can use headers and other markdown</h2>
<p>Also html <b>works</b></p>
</main>
</body>
</html>
Collections
Eleventy has concept of "collections". It's basically a big javascript object that contains all of your processed files, sorted by tags.
So for example, in the markdown file example above you may have noticed some meta data including a tag called post. This gives me the ability to then reference all of those files that have been sorted into "post". For example I could write code like this:
<ul> Weblog Postings:
{%- for post in collections.post -%}
<li><a href="{{post.url}}">{{ post.data.title }}</a></li>
{%- endfor -%}
</ul>
and it will render all of my posts like so:
- Weblog Postings:
- Making a weblog with eleventy
- My Garden 2024
Getting fancy
Ok that's the basics for getting started with a blog for eleventy, but I found there were a few other things I wanted to set up right away:
Syntax Highlighting
I hope to occasionally write things about code here, and when I do I want it to look pretty. Eleventy has some tooling to make this easy using Prism. Here's how I did it:
- added another dev dependency:
@11ty/eleventy-plugin-syntaxhighlight
- I then added it to my eleventy file like so:
const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
module.exports = function (eleventyConfig) {
eleventyConfig.addPassthroughCopy("source/public");
eleventyConfig.addPlugin(syntaxHighlight);
return {
dir: {
input: "source",
output: "build",
includes: "layouts",
}
}
};
- this will then identify anytime I use a ```langname and convert it into
<pre><code>
tags - I then went to the prism.js download page and selected a theme, all the langs I cared to support, as well as the nifty treeview plugin (that's what lets me render those cool file tree structures above). I then clicked to download the js and css files and added them to my public folder.
- then I added link/script tags in my layouts and now I can write pretty code in my markdown files and it shows with the proper syntax highlighting.
Drafts
I wanted some way to be able to write and preview drafts, without building them in production. Fortunately 11ty has a handy tutorial for that. All you have to do is add a few lines to your config file, and then anytime you want a post to be unpublished you simply need to add draft: true
to the front matter meta data like so:
---
layout: post.njk
title: draft
tags: post
draft: true
---
a test draft
and the post will only build when you're running it locally
Table of Contents
I tend to think in terms of headers sometimes. I'm not sure that's useful when reading, but it's how I think when writing. When I write papers for work, I often use the Table of Contents feature from google docs, and I kind of wanted something similar. And wouldn't you know it, someone has already needed something like this too! Check it out another awesome eleventy plugin to the rescue!
Custom filters
One last thing, I suspect there is a "better" way to do this but when I wanted to show the day's date I noticed it was a bit wordy by default.
Writing:
{{page.date}}
shows as:
Fri Dec 29 2023 01:24:55 GMT+0000 (Coordinated Universal Time)
I found out one way to do this with nunjucks is to write a custom filter. Here's how I did it with eleventy:
eleventyConfig.addFilter("starDate", (function (datestring) {
return ` ⭐ Star Date: ${new Date(datestring).toDateString()} ⭐`;
}))
So now I can just write
{{ page.date | starDate }}
and I'll get
⭐ Star Date: Fri Dec 29 2023 ⭐
Anyway that's all for now!