Add post
This commit is contained in:
parent
686239ea03
commit
115c9497df
188
content/blog/galleries.md
Normal file
188
content/blog/galleries.md
Normal file
@ -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 }}"
|
||||
---
|
||||
<h1>{{ gallery.title }}</h1>
|
||||
<p>{{ gallery.description }}</p>
|
||||
<section>
|
||||
{% for picture in gallery.pictures %}
|
||||
<a href="/gallery/{{ gallery.title | slugify }}/{{ picture.filename | slugify }}/">
|
||||
<div>
|
||||
<img alt="{{ gallery.thumbAltText }}" class="gallery-image" src="{{ gallery.url }}{{ picture.filename }}">
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}{% endraw %}
|
||||
</section>
|
||||
```
|
||||
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}}"
|
||||
---
|
||||
<article>
|
||||
<h1>{{ picture.title }}</h1>
|
||||
<div>
|
||||
{% if picture.previousImage %}
|
||||
<a href="../{{ picture.previousImage | slugify }}">
|
||||
<button type="button">🡠 Previous</button>
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="/gallery/{{ picture.containingGallery | slugify }}/">
|
||||
<button type="button">Gallery</button>
|
||||
</a>
|
||||
{% if picture.nextImage %}
|
||||
<a href="../{{ picture.nextImage | slugify }}">
|
||||
<button type="button">Next 🡢</button>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<figure>
|
||||
<a href="{{ picture.baseUrl }}/{{ picture.filename }}">
|
||||
<img src="{{ picture.baseUrl }}/{{ picture.filename }}" alt="{{ picture.altText }}">
|
||||
</a>
|
||||
{% if picture.caption %}
|
||||
<figcaption>
|
||||
{{ picture.caption }}
|
||||
</figcaption>
|
||||
{% endif %}
|
||||
</figure>
|
||||
</article>{% 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
|
||||
---
|
||||
<h1>Image Galleries</h1>
|
||||
<p>
|
||||
Some pictures I thought would be worth posting.
|
||||
</p>
|
||||
<section>
|
||||
<div>
|
||||
{% for gallery in galleries %}
|
||||
<article>
|
||||
<a href="../gallery/{{ gallery.title | slugify }}" class="postlist-link">
|
||||
<div>
|
||||
<img {% if gallery.galleryImage %} src="{{ gallery.url }}{{ gallery.galleryImage }}" alt="{{ gallery.galleryImageAlt }}" {% else %} src="{{ metadata.defaultPostImageURL }}" alt="{{ metadata.defaultPostImageAlt }}"{% endif %}>
|
||||
</div>
|
||||
</a>
|
||||
<div>
|
||||
<a href="../gallery/{{ gallery.title | slugify }}" class="postlist-link">
|
||||
<h3>
|
||||
{{ gallery.title }}
|
||||
</h3>
|
||||
</a>
|
||||
<time datetime="{{ gallery.date | htmlDateString}}">{{ gallery.date | readableDate("LLLL yyyy") }}</time>
|
||||
<p>{{ gallery.synopsis | truncate(105) | safe }}</p>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
```{% 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)!
|
@ -23,7 +23,7 @@ Some pictures I thought would be worth posting.
|
||||
{{ gallery.title }}
|
||||
</h3>
|
||||
</a>
|
||||
<time class="postlist-date" datetime="{{ gallery.date }}">{{ gallery.date | readableDate("LLLL yyyy") }}</time>
|
||||
<time class="postlist-date" datetime="{{ gallery.date | htmlDateString}}">{{ gallery.date | readableDate("LLLL yyyy") }}</time>
|
||||
<p>{{ gallery.synopsis | truncate(105) | safe }}</p>
|
||||
</div>
|
||||
</article>
|
||||
|
BIN
public/img/isabella-fischer-X2l9M6jsS7E-unsplash.webp
Normal file
BIN
public/img/isabella-fischer-X2l9M6jsS7E-unsplash.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 79 KiB |
Loading…
x
Reference in New Issue
Block a user