A Nanogallery Hugo Shortcode

I wanted a nice photo gallery, but none of the existing Hugo options appealed to me. I found this nice survey article by James Taylor, where he tried several options and found them all wanting. In a comment someone posted a simple shortcode for Nanogallery, a beautiful and comprehensively configurable gallery package. The code wasn’t quite what I wanted, but it was enough to show me the way.

After a couple hours of alt-tabbing between my editor and the Hugo & Nanogallery docs I had a nice shortcode for a configurable gallery.

To use it you list your images in a JSON file. Paths are relative to the directory the gallery is in.

content.json:

[
 {
  "src": "western-swallowtail-side.jpg",
  "mtime": "2020-01-26T13:24:11",
  "downloadURL": "originals/orig-western-swallowtail-side.jpg",
  "imgWidth": 1200,
  "imgHeight": 900,
  "title": "Western Swallowtail Side",
  "srct": "thumbnails/thumb-western-swallowtail-side.jpg",
  "imgtWidth": 250,
  "imgtHeight": 187,
 },
 {
  "src": "tower-of-power.jpg",
  "downloadURL": "originals/orig-tower-of-power.jpg",
  "imgWidth": 1051,
  "imgHeight": 1400,
  "title": "Tower Of Power",
  "srct": "thumbnails/thumb-tower-of-power.jpg",
  "imgtWidth": 169,
  "imgtHeight": 225,
 }
]

Then you define an (optional) gallery config as a JavaScript snippet. Note that this isn’t legal JSON, it’s just the attrs of a JS object.

config.js:

"thumbnailWidth": "auto",
"thumbnailHeight": 225,
"thumbnailBorderHorizontal": 0,
"thumbnailBorderVertical": 0,
"thumbnailDisplayTransition": "scaleUp",

Finally, you call the shortcode in your markdown.

gallery-page.md:

+++
title = "Photogallery"
nanogallery = "yes"
+++
# Photogallery

Look, a gallery.

{{ < nanogallery id="my-gal" configfile="/photography/config.js" contentfile="content.js" >}}

The Plumbing

The shortcode itself is pretty simple.

layouts/shortcodes/nanogallery.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!-- configfile can be an absolute path or page relative -->
{{ $configFilePath := ( .Get "configfile" ) }}
{{ if not (strings.HasPrefix $configFilePath "/") }}
  {{ $configFilePath = print $.Page.File.Dir $configFilePath }}
{{ end }}

{{ $contentFilePath := print $.Page.File.Dir ( .Get "contentfile" ) }}
{{ $galId := .Get "id" | default "gallery" }}

{{ $excludeFilePath := print $.Page.File.Dir ( "/exclude-originals.txt" ) }}
{{ if not ( os.FileExists $excludeFilePath ) }}
  <div class="the-fine-print">
  </div>
{{- end }}

<div id="{{ $galId }}"></div>
<script>
$(document).ready(function () {
  $("#{{ $galId }}").nanogallery2({
{{ $configFilePath | readFile | safeJS }}
   items:
{{ $contentFilePath | readFile | safeJS }}
  });
 });
</script>

<div class="the-fine-print">
{{ if os.FileExists $excludeFilePath }}
  (Full resolution images available on politely worded request)
{{ else }}
  You can download a full-res version by clicking on the <i
  class="nGY2Icon-ngy2_download2"></i> icon at the top right when viewing a
  photo (way easier on a "real" computer versus a phone).  These photos (like
  all my photos) are provided under the <a href="https://creativecommons.org/licenses/by-sa/4.0">Creative
  Commons BY-SA license</a>, which means (roughly) that you can use them as
  you like so long as you give me credit.
{{ end }}
</div>

There’s a partial to load jQuery and Nanogallery as needed that you should call from your template somewhere.

{{ if isset .Params "nanogallery" }}
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<script type="text/javascript" src="/js/jquery-2.1.1.min.js"></script>
<link href="/css/nanogallery2.min.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="/js/jquery.nanogallery2.core.min.js"></script>
{{ end }}

Preparing the images and building content.json is handled by my Nanogallery Wrangler script.