diff --git a/content/blog/galleries.md b/content/blog/galleries.md
new file mode 100644
index 0000000..f0ea87b
--- /dev/null
+++ b/content/blog/galleries.md
@@ -0,0 +1,188 @@
+---
+title: Adding Image Galleries to My Website
+description: At last, I've gotten around to implementing image galleries.
+date: 2024-12-02
+tags:
+ - Site Updates
+ - Eleventy
+synopsis: At last, I've gotten around to implementing image galleries.
+imageURL: /img/isabella-fischer-X2l9M6jsS7E-unsplash.webp
+imageAlt: Some very tasty looking pop tarts with pink icing and sprinkles.
+mastodon_id: ""
+---
+I've been meaning to add an image gallery feature to this website for ages, and I'm happy to finally announce that I've done it! If you'd like to see my very first image gallery in action, [here's a gallery of PopTart memes I've collected](../../gallery/pop-tart-flavor-memes/). If you're a massive nerd and would like to read about how I implemented this feature on my [Eleventy](https://www.11ty.dev/) website, read on.
+
+Note: this is a fast and loose description of the process. It should be helpful if you're trying to do this yourself, but don't expect to be able to copy and paste my implementation. See [the repo](https://upchur.ch/gitea/n_u/nathanupchurch.com) to copy and paste my spaghetti.
+
+## Specifying new galleries
+First of all, I had to decide how new galleries would be specified so that Eleventy could work its magic. There are a few approaches here, such as utilizing template frontmatter, directories of images, or using a data file in my site's `_data` directory. After weighing up the pros and cons, I decided to use the latter option, even though it's not the most ergonomic. So I created `_data/galleries.js`, which looks like this[^1]:
+
+``` javascript
+export default [
+ {
+ title: "",
+ description: "",
+ synopsis: "",
+ url: "",
+ date: new Date(""),
+ galleryImage: "",
+ galleryImageAlt: "",
+ pictures: [
+ {
+ title: "",
+ filename: "",
+ altText: "",
+ thumbAltText: "",
+ caption: "",
+ },
+ ],
+ },
+];
+```
+
+## Gallery index pagination
+Now that I know how I'll go about specifying my galleries, I need to paginate the gallery index pages; the pages that feature thumbnails of each image in the gallery and allow the user to navigate to the images. To do this, I created `content/galleries.njk`:
+``` html
+---{% raw %}
+pagination:
+ data: galleries
+ size: 1
+ alias: gallery
+layout: layouts/base.njk
+tags: gallery
+eleventyComputed:
+ title: "{{ gallery.title }}"
+ permalink: "/gallery/{{ gallery.title | slugify }}/"
+ description: "{{ gallery.description }}"
+---
+
+
+ {% endfor %}{% endraw %}
+
+```
+By ensuring that each gallery index page is tagged "gallery," they will automatically be grouped into a new collection by that name, which is important so that I can filter galleries out from post lists and other things throughout my site.
+
+## Generating image pages
+I considered using a lightbox / modal sort of arrangement for viewing images, but I decided against it as it can be tricky to get accessibility right for these patterns, and I wanted to avoid using JavaScript if possible. What I chose to do instead was to create a page for each image, which would feature buttons to navigate to the next / previous image, and to return to the gallery. The first step was to create a collection containing all gallery images in my Eleventy config file:
+
+``` javascript
+// Collections
+ eleventyConfig.addCollection("galleryImages", (collection) => {
+ const galleries = collection.getAll()[0].data.galleries;
+ let galleryImages = [];
+
+ galleries.forEach((gallery) => {
+ gallery.pictures.forEach((picture, i, arr) => {
+ picture.containingGallery = `${gallery.title}`;
+ picture.baseUrl = `${gallery.url}`;
+ i ? (picture.previousImage = arr[i - 1].filename) : null;
+ i + 1 != arr.length ? (picture.nextImage = arr[i + 1].filename) : null;
+ galleryImages.push(picture);
+ });
+ });
+```
+You may notice that I did a little jiggery-pokery in that callback function to provide information about each image that isn't included in `_data/galleries.js` because it would have been a pain to include or would have unecessarily inflated the tile size, specifically: the filenames of images before and after the current image (should they exist), and the URL of the gallery that the image belongs to. We can use this information to have our image pages generated in the same directory as their parent gallery index, and to generate next / previous buttons to help the user navigate through the gallery. This information is added to the `galleryImages` collection object in memory so that we can use it when we paginate the image pages using `content/galleryImage.njk`:
+
+``` html{% raw %}
+---
+pagination:
+ data: collections.galleryImages
+ size: 1
+ alias: picture
+layout: layouts/base.njk
+eleventyComputed:
+ title: "Image: {{ picture.title }}"
+ permalink: "/gallery/{{ picture.containingGallery | slugify }}/{{ picture.filename | slugify }}/"
+ description: "{{ picture.title }} from gallery: {{ picture.containingGallery}}"
+---
+
+
+
+
+
+
+ {% if picture.caption %}
+
+ {{ picture.caption }}
+
+ {% endif %}
+
+{% endraw %}
+```
+
+Et voilà ; we have galleries! But before we can call this project done, it would be nice to have a page that lists all galleries on the site for the benefit of visitors. For this, I created `/content/galleries/index.njk`:
+
+``` html{% raw %}
+---
+layout: layouts/base.njk
+eleventyNavigation:
+ key: Pics
+ order: 4
+---
+
Image Galleries
+
+Some pictures I thought would be worth posting.
+
+
+```{% endraw %}
+
+Note that `readableDate()` and `htmlDateString` are custom filters that came with the [Eleventy Base Blog template](https://github.com/11ty/eleventy-base-blog) that I based my website on. They require Luxon:
+``` javascript
+eleventyConfig.addFilter("readableDate", (dateObj, format, zone) => {
+ // Formatting tokens for Luxon: https://moment.github.io/luxon/#/formatting?id=table-of-tokens
+ return DateTime.fromJSDate(dateObj, { zone: zone || "utc" }).toFormat(
+ format || "dd LLLL yyyy",
+ );
+ });
+
+ eleventyConfig.addFilter("htmlDateString", (dateObj) => {
+ // dateObj input: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
+ return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat("yyyy-LL-dd");
+ });
+```
+You may be thinking that this all seems a convoluted, and I agree! If you know of a simpler way to accomplish this functionality, do feel free to let me know, but for now, I'm just happy that I finally have the ability to add image galleries to this website.
+
+[^1]: Thanks to those who answered [my cry for help](https://lounge.town/@nathanu/113574428382435982)!
diff --git a/content/galleries/index.njk b/content/galleries/index.njk
index 0ec9a40..28984cc 100644
--- a/content/galleries/index.njk
+++ b/content/galleries/index.njk
@@ -23,7 +23,7 @@ Some pictures I thought would be worth posting.
{{ gallery.title }}
-
+
{{ gallery.synopsis | truncate(105) | safe }}
diff --git a/public/img/isabella-fischer-X2l9M6jsS7E-unsplash.webp b/public/img/isabella-fischer-X2l9M6jsS7E-unsplash.webp
new file mode 100644
index 0000000..52b5a1f
Binary files /dev/null and b/public/img/isabella-fischer-X2l9M6jsS7E-unsplash.webp differ