A Static Art Gallery with Hugo

This is not the first time, and won’t be the last time I post about JS-free, bloat-free web. In this post I’d like to share how I build a art collection site for my gf.

the site: https://gi.nseng.art

I’m not a web designer and I’m not a frontend engineer. Yet I’ve wrote a lot about static blogging. I do this to make a point for the bloat-free web.

A simple grid layout

I adopted the example from here. Later I’ll show how to plug this into the hugo system.

Content Organization

Now, this is the tricky part. Simply put, hugo is opt for text, not blob objects like images or videos.

  • For a formal presentation, artworks needs to documented with their meta data e.g. time of making, author, material, techniques, etc..
  • The need for meta data management is similar to the need for bibliography management in acdemia.
  • It’s far more complicated than the usual approach : putting a piece of artwork in a post with markdown image via [alt text]("url" "desc"). For example, each media could appear in different places, with a consistent meta data display. Copying around the hyperlinks is a dead end.

are you talking about wordpress? - Yes I am. Wordpress makes it pretty easy to manage media files and I find it the optimal CMS for artists. But I won’t use it, again, to make a point.

Now, what about content organization?

A typical hugo content structure would look like this:

|- category1
    |- post1.md
    |- post2.md
    |- post3
        |- _index.md
        |- sub1.md
        |- sub2.md
        |- img1.jpg
        |- ...

The problem here is obvious: the assets like media are bounded to posts and there is not a central place for meta data management.

So instead, I do this:

|- category1
|   |- post1.md
|   |- post2.md
|   ...
|- static
|   |- media files
|   |- ...
|- data
    |- meta.json

the meta.json would be our central place for metadata management. It will look like this:


        "id"            : "aq2",
        "name"          : "",
        "desc"          : "A question 2, 2024, Oil on canvas, 70x55cm",
        "image_url"     : "/A_Question_2/A_Question_2.jpg",
        "series"        : "A Question 2",
        "series_url"    : "/arts/a_question_2/",
        "tags"          : ["painting"]
        "id"            : "aq1"
        "name"          : "",
        "desc"          : "A question 1, 2024, Oil on canvas, 160x114cm",
        "image_url"     : "/A_Question_1/A_Question_1.jpg",
        "series"        : "A Question 1",
        "series_url"    : "/arts/a_question_1/",
        "tags"          : ["painting"]
  • each media may belong to a serie
  • each piece of a serie may have its own name
  • each media is identified by its file path (its relative url, same as its relative path to static/)
  • each media is also identified by the id: this is handy to include media via shortcodes
  • each media may be linked to a post (series_url)
  • each media may have one or multiple tags (as in “categories”).

hugo layouts and partials

Now let’s plug the metadata into hugo partials:

a gallery of ALL artworks:

layout/gallery/list.html (only showing relevant parts)

{{ $gl := site.Data.gl_all }}
<!-- filter the list here -->
{{ partial "gallery_grid.html" $gl}}


<div class="gallery-grid" style="max-width: 80%; margin:auto">
    {{ range . }}
      <figure class="gallery-frame">
          {{with .video_url}}
          <video class="gallery-img" playsinline="" controls="">
              <source src="{{.}}" type="video/mp4">
          <a href="{{.image_url}}" target="_blank">
          <img class="gallery-img"
          <figcaption> <a href ="{{.series_url}}">{{.series}}</a>
              // <i>{{.desc}}</i>

              <div class="tags">
                  {{range .tags}} <a href="/tags/{{.}}"> #{{.}} </a>
</div> <!--gallery-grid-->

Sub-gallery : filter the json data:

e.g. layouts/paintint/list.html

{{ $gl := where site.Data.gl_all "tags" "intersect" (slice "painting")}}
{{ partial "gallery_grid.html" $gl}}


It’s obvious that we shouldn’t use hi-res pictures for a gallery grid view. I have a simple script to generate thumbnails. The thumbnails are store to the same path as the original, with a suffix .tn.jpg. This makes it easier for templating.

target_dirs=( "$@" )
echo "making thumbnails : NO RECURSION!"
gen_thumb_for_dir() {
    local dir=$1
    if [ ! -d "$dir" ]; then
        echo "[ERROR] $DIR does not exist."
    rm $dir/*.tn.jpg
    targets=($(find $dir \( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" \)))
    for i in "${targets[@]}"
        echo "PROCESSING $i to $i.tn.jpg"
        convert $i -resize 1280x1280 -strip $i.tn.jpg
for i in "${target_dirs[@]}"
    gen_thumb_for_dir $i

Add media to post via shortcode and ID


{{ $gl := site.Data.gl_all }}
{{ $id := .Get "id" }}
{{ range $gl }}
    {{ if eq .id $id }}
        {{ $t := ""}}
        {{ if .name}}
            {{$t = printf "%s - %s" .name .desc}}
            {{$t = .desc}}
        {{ end }}
        <p class="md__image">
        <img src="{{.image_url}}" alt="{{$t}}"  title="{{$t}}" />
        <div class="img-cap">{{$t}}</div>
        {{ else }}
    {{ end }}
{{ end}}

to use in a post:

{{<media id="aq2">}}


edited 05.04.2024
created 29.03.2024
[+] click to leave a comment [+]
the comment system on this blog works via email. The button
below will generate a mailto: link based on this page's url 
and invoke your email client - please edit the comment there!

[optional] even better, encrypt the email with my public key

- don't modify the subject field
- specify a nickname, otherwise your comment will be shown as   
- your email address will not be disclosed
- you agree that the comment is to be made public.
- to take down a comment, send the request via email.


the greater struggle via serocell - media feed March 30, 2024

Screenshot: New Device via Detritus March 18, 2024

Idiot's guide to resource migration in Terraform via Archive Fever by Edwin Wenink March 15, 2024
Sometimes we need to migrate resources that are managed in Terraform. Terraform is a declarative language to manage cloud infrastructure from code, which allows you to reliably automate your deployments and put your infrastructure configuration under versi…

Generated by openring from webring