How I Built This Website (With 11ty & CraftCMS)
Since my old site fell into a general state of disrepair, and since I previously thought it was a good idea to go all in with Gatsby at the time (despite it mostly being a static HTML/CSS build, I ended up shipping megabytes of JavaScript, please forgive me for my sins), it was time for a rebuild.
I’ve been playing around with both 11ty and Astro over the last year, and have built a couple of non-trivial side-projects with the latter.
Having spent much of the last ten years building component-driven architectures, I was happy to see first-class support for the paradigm from both offerings, through 11ty’s WebC and Astro’s Lit SSR support respectively. But while I find component-driven architecture fits building and scaling complex user interfaces very well, it felt egregiously overkill for my needs.
So after a few iterations on the new design using both frameworks, I ended up settling on 11ty and Nunjucks.
Requirements
My primary requirements were fairly straightforward. I need it to be simple, static and fast, and to provide a simple mechanism for being able to pull in data from external sources (like a CMS, or an RSS feed) so that I can post articles and other content without needing to touch the source code.
I also wanted to publish an RSS feed of my articles, including RSS Club (RSS-only) posts, TILs, notes and links.
The Design Process
It’s been a while, but I’m a ‘design in the browser’ kind of person, and whilst I have experience with and have enjoyed working with the likes of Figma, Sketch, et al in the past, they’ve mostly served as communication tools, bridging the gap between ‘designers’ who don’t code, and ‘developers’ who do.
I’m pretty comfortable with HTML and CSS, and whilst admittedly a little messy at times while iterating, I find working directly with code, in the browser, to be the most rewarding.
So that’s what I did.
I quite liked the design of my old site, and I had spent quite some time developing the content, so I wasn’t starting from scratch in that respect. I had some ideas, some inspiration to work from, and some real content to play around with.
HTML First
First things first, I started marking up my layout and content, ensuring the correct semantics, accessibility considerations and information architecture: some general base layout, navigation, footer, etc, and then some more specific pages, like the homepage, the words and article pages.
A Touch Of Style
I then started to apply some CSS.
I headed over to Josh Comeau’s website, grabbed his Modern CSS Reset and defined it under a @reset
layer in my source code.
I then headed over to Utopia to generate fluid spacing and typography scales and defined them in a @variables
layer, and then went on to configure some further base styles defined under a @main
layer.
Laying it out
Once I had my base styles and variables set up, it was time to start laying out my markup.
During this part of the process, I like to focus on the layout alone. That is, I don’t apply colour, or sugar or anything else not strictly related to layout.
This allows me to stay focussed, and to change things with low risk.
Make it delightful
At this stage, I had a nice layout, but an otherwise pretty boring-looking website: black on white with the default system font.
I started with the homepage. I wanted to take a prompt from my previous website design and lead with a typographical hero. I played around with various “web-safe” fonts, but nothing felt quite right, so opted to reach for a (self-hosted) web font, to inject a bit of character into the copy. Once settled on the homepage, I carried this typographical style across the rest of the pages, using my web font for headings and accents, and a system font everywhere else.
A note on web fonts and CLS:
Cumulative Layout Shift is a core web vital metric which measures the cumulative shift in the layout of your webpage as it loads. As web fonts are external resources, there is often a period of time during loading where the page is originally rendered with whatever default (or fallback) font that was defined before the externally loaded web font arrives, and this can lead to poor CLS outcomes. There are several approaches to reducing this, including font preloading, font-display strategies, and size-adjust properties.
Building upon the typographical foundation, I started to add other visual elements, such as illustrations, call-outs, and a sprinkling of movement via subtle animations.
Content Management
My previous site was hooked up to a headless CMS (Contentful) via a webhook that would trigger a rebuild when I saved content in the CMS, which the build would then in turn make a request to the Contentful API to get the latest data and produce the desired output.
I wanted a similar set-up for my new site, but this time I really wanted to own and control both my data and the tooling around it.
In a previous life, I did a lot of CMS work. Mostly originating from the PHP world: Joomla, Drupal, ExpressionEngine, Magento, and have also dabbled with WordPress. Toward the end of that chapter of my career, emerging from the ExpressionEngine community, a new CMS called Craft was launched.
I was doing a lot of ExpressionEngine work at the time (a proprietary, paid CMS), and when Craft was released, it was a natural progression. I really liked working with Craft back then, and I wondered whether this might just be what I’m after.
Craft is a proprietary, paid-for CMS, but they offer a Solo plan, “For when you’re building a website for you or a friend”. Just the ticket. That sounds like me!
I followed the Quick Start guide, installed DDEV, scaffolded my test project, launched the application and quickly got to work exploring the UI and refamiliarising myself with content modelling.
And just like that, I was back in the PHP world.
After a few minutes of experimenting, designing a content model, and exploring some of the community offerings (i.e. the webhook plugin), it became apparent that Craft was going to be perfect for my needs.
But where to host?
There was only one last question to solve. Where do I host this?
One of my requirements was ‘simple’ and another that I hadn’t really articulated until then was ‘cheap’ or preferably, ‘free’.
I started scouting around the web for various different offerings, weighing up the benefits of hand-rolling something with a PaaS, or spinning it up on a dedicated VPS or even a managed server.
If I wanted cheap, I got complex, and if I wanted simple I got expensive. Nothing fit the bill.
Then it dawned on me. Why don’t I host it myself, at home, on a Raspberry Pi?
Thirty minutes or so later, I had done just that.
I created a new image of Raspberry Pi OS and wrote it to an SD card, inserted it into the Pi and powered it up.
I then proceeded to install Apache, PostgreSQL, PHP (plus a bunch of required dependencies), and Let’s Encrypt’s Certbot to issue and manage SSL certificates.
The last part of the puzzle was to configure a Dynamic DNS service so that I could reliably access the CMS from the web, choosing a provider that was supported by my router, so that I could set it up once and then automate the whole thing.
Gluing it all together
I decided to use Netlify’s Starter plan (the free tier was released a day or two after I launched the site, to which I’ve since migrated), which supports triggering a build by hitting a ‘secret’ endpoint. I grabbed the URL, then headed over to my local Craft instance, installed the webhook plugin, and configured it to hit the build URL every time I save content. One side of the puzzle solved.
The next thing to address was how to retrieve and make the data available to 11ty at build time.
For this purpose I wrote a simple Global Data File to hit my CMS’s GraphQL API, to retrieve and process the data so that I could access it in my templates.
I also use a bookmark manager to manage my bookmarks, and I wanted to be able to post them to my RSS feed too. This was another simple case of throwing together a global data file to retrieve the links from my bookmark manager’s API, process them and make them available to my templates.
I save quite a lot of links to my bookmark manager, many of which are neither relevant nor appropriate to be published on my feed. Setting this up was simple enough – saving to and only pulling from a specific collection that I wanted to publish to the web – but I still needed a way of triggering a rebuild of the site when I had new link content to publish.
Due to the frequency of saving links, the cost of build minutes, and the complexity overhead of configuring some pipes together with the likes of IFTTT, it became apparent that it didn’t need to be real-time. I could queue my links and post them in bulk. The path of least resistance to achieve this was via a GitHub Action, scheduled to run once per day at a specific time. More than good enough for my needs.
Job Done
And that’s How I Built This Website. What started as an escape from a massively over-engineered Gatsby site has landed me with something much more managable: 11ty spitting out static files, Craft CMS running on a Pi in my lounge, and a few bits of automation holding it all together.
The whole thing just works. I write stuff in a proper CMS, hit save, and the site rebuilds itself. My bookmarks get published once a day without me lifting a finger. No monthly bills for hosting the CMS, no JavaScript frameworks where I don’t need them, and all my data stays exactly where I want it.
Running a CMS on a Raspberry Pi probably sounds a bit mad to most people, but in this context (n.b. I don’t host the site on the Pi, only the content), it’s absolutely fine. There’s something quite satisfying about knowing your setup inside and out, and not being beholden to anyone else’s pricing changes or terms of service updates.
Sometimes you really don’t need the fancy solution.
This post doesn't support comments
(if you'd like to reach out, I'd love to hear from you!)