Now and Then: Keeping an archive of my life updates
When I did my recent redesign and rebuild of my website, I had an idea for how I wanted to manage my "Now" page. There were three features I wanted to have:
- Updates are just regular blog posts that appear in the blog post list. This way, subscribers to my RSS feed would also see these updates.
- My actual
/nowpage displays the most recent one of these updates. - A new page,
/then, displays all of my "Now" updates in reverse-chronological order.
I was able to achieve this using 11ty Collections, which I will now describe. You will want to read this to the end to see how I solved an issue with heading levels.
"Now" updates as blog posts
Writing "Now" updates as blog posts just means I write them like any other blog post I add to my site. I write these in Markdown, and every blog post has a title and date in the frontmatter.
For "Now" updates, I also add a now tag — this will enable me to make my dynamic /now and /then pages using the now collection.
---
title: Blah blah summary of big themes in this update
date: 2025-10-30
tags:
- now
---
The content of the update goes here...
Automatic /now page generation
To make my /now page, I need to find the most recent "Now" update blog post and render the content to this page. Since 11ty Collections are sorted by date, we can do this by grabbing collections.now.at(-1).
I use WebC for my /now page template, but you could do this with any template language. I snipped out some content here to focus on the technique.
---
layout: layouts/root.webc
title: Now
eleventyExcludeFromCollections: true
eleventyImport:
collections:
- now
---
<script webc:setup>
const latest = collections.now.at(-1);
</script>
<blog-post @title="What I've been up to">
<!-- [snip] -->
<div webc:nokeep @raw="latest.content"></div>
<!-- [snip] -->
</blog-post>
Here's a tip for you: do you see the eleventyImport key in the frontmatter? This is how you inform the 11ty compiler that this page has a dependency on the now collection so that templates are processed in the correct order. Without this, you might see an error message like this:
Tried to use templateContent too early on ./site/blog/now-2025-10-30.md
Alright! Now we're really cruising. It's time for the final piece of this puzzle.
Building a /then page
Making my /then page is just about as straightforward as iterating over the now collection, except I had one more problem to solve.
I wanted the overall page structure of my "Then" page to look like this:
<article>
<h2><!-- date of update --></h2>
<!-- content of update -->
</article>
<article>
<h2><!-- date of update --></h2>
<!-- content of update -->
</article>
<article>
<h2><!-- date of update --></h2>
<!-- content of update -->
</article>
The problem with this is that the actual updates are already using <h2> headings in them. I want to increase the level of all the headings in the content when I include them on this page so that the semantics are correct.
To do this, I integrated with rehype which is a JavaScript library and ecosystem of plugins for parsing and transforming HTML. Specifically, I am using the hast-util-shift-heading package to shift headings.
This is all wrapped in a custom 11ty transform:
import { shiftHeading } from "hast-util-shift-heading";
import { rehype } from "rehype";
import rehypeFormat from "rehype-format";
import rehypeRewrite from "rehype-rewrite";
export default async function (eleventyConfig) {
// Other stuff...
eleventyConfig.addTransform(
"html-postprocess",
async function (content) {
if (!(this.page.outputPath || "").endsWith(".html")) {
return content;
}
const file = await rehype()
.use(rehypeRewrite, {
selector: "[data-increase-headings]",
rewrite: (node) => {
shiftHeading(node, 1);
return node;
},
})
.use(rehypeFormat)
.process(content);
return String(file);
}
);
}
Putting this all together, the way we shift heading levels for a section of HTML is by adding the data-increase-headings attribute on an element wrapping the content whose headings we want to shift.
<article webc:for="post of collections.now.toReversed()">
<h2><!-- [snip] -->
<div data-increase-headings="true" @raw="post.content"></div>
</article>
And there you have it! The full source code for my website is available here if you want to see this in action.