I've lately seen some folks on [social.lol](https://social.lol) posting about their various strategies for automatically generating [Open Graph images](https://ogp.me/) for their [Eleventy](https://11ty.dev) sites. So this weekend I started exploring ways to do that for my [Hugo](https://gohugo.io) site.
During my search, I came across a few different approaches using external services or additional scripts to run at build time. I eventually came across a post from Aaro titled [Generating OpenGraph images with Hugo](https://aarol.dev/posts/hugo-og-image/) which seemed like exactly what I was after, as it uses Hugo's built-in [image functions](https://gohugo.io/functions/images/filter/) to dynamically create the image by layering text on top of a base.
- ... but if the post has a thumbnail[^thumbnail] listed in the front matter, that gets added to the corner of the `og:image`.
[^thumbnail]: My current theme doesn't make use of the thumbnails, but a previous theme did so I've got a bunch of posts with thumbnails still assigned. And now I've got a use for them again!
Based on Aaro's suggestions, I started by creating a 1200x600 image to use as the base. I used [GIMP](https://www.gimp.org/) for this.
I'm not a graphic designer[^web] so I kept it simple. I wanted to be sure that the text matched the font used on the site, so I downloaded the [Fira Mono `.ttf`](https://github.com/mozilla/Fira/blob/master/ttf/FiraMono-Regular.ttf) to `~/.fonts/` to make it available within GIMP. And then I emulated the colors and style of the "logo" displayed at the top of the page.
![File explorer window showing a directory structure with folders such as '.github/workflows', 'archetypes', 'assets' with subfolders 'css', 'js', and files 'FiraMono-Regular.ttf', 'og_base.png' under 'RUNTIMETERROR'.](new_resources.png)
### OpenGraph partial
Hugo uses an [internal template](https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/opengraph.html) for rendering OpenGraph properties by default. I'll need to import that as a partial so that I can override its behavior. So I drop the following in `layouts/partials/opengraph.html` as a starting point:
```jinja-html
// torchlight! {"lineNumbers": true}
<metaproperty="og:title"content="{{ .Title }}"/>
<metaproperty="og:description"content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}"/>
<metaproperty="og:type"content="{{ if .IsPage }}article{{ else }}website{{ end }}"/>
<metaname="twitter:description"content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}"/>
The [`resources.Get`](https://gohugo.io/functions/resources/get/) bits import the image and font resources to make them available to the [`images.Text`](https://gohugo.io/functions/images/text/) functions, which add the site and page title texts to the image using the designated color, size, placement, and font.
The `resources.Copy` line moves the generated OG image into the post bundle directory and gives it a clean `og.png` name rather than the very-long randomly-generated name it would have by default.
And then the `<meta ... />` lines insert the generated image into the page's `<head>` block so it can be rendered when the link is shared on sites which support OpenGraph.
This is a great starting point for what I want to accomplish, but I'm going to make some changes in my `opengraph.html` partial.
As I mentioned earlier, I want to have basically three recipes for baking my OG images: one for the homepage, one for standard posts, and one for posts with an associated thumbnail. They'll all use the same basic code, though, so I wanted to be sure that my setup didn't repeat itself too much.
<metaproperty="og:description"content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}"/>
<metaproperty="og:type"content="{{ if .IsPage }}article{{ else }}website{{ end }}"/>
<metaname="twitter:description"content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}"/>