
Updates in June 2021
Cover image: Star vector created by upklyak - www.freepik.comI have continued learning about Hugo
capabilities in June and have implemented several small improvements:
- added image attribution and
- parallax effect in blog posts;
- customized title for a feedback form;
- added robots.txt;
- fixed sitemap.xml;
- added meta for social pages sharing.
Image attribution
Hugo
has a nice set of default front matter variables, including images
. I was thinking how this can be used and came up with a two-image approach: first image in a blog post works as cover picture and second image is used for a parallax effect:
images:
- june_updates_small.jpg| Star vector created by upklyak - www.freepik.com
- june_updates.jpg| Star vector created by upklyak - www.freepik.com
Important thing is that I don’t own these pictures and most of them require some kind of attribution. My decision was to add a pipe separator after an image file name and mention the image author on the same line. Later on I could check the whole line, retrieve the image name and attribution text in a Hugo layout. For example, take a look at a Summary.html layout that is used to list blog posts:
<div class="card">
<div class="postsummary">
{{ if isset .Params "images" }}
{{$img_options := (split (index .Params.Images 0) "|")}}
<a href="{{ .Permalink }}">
<img class="card-img-top" src="{{.Permalink}}{{trim (index $img_options 0) " "}}"
alt="{{ .Title }}" title="{{trim (index $img_options 1) " "}}" >
</a>
{{ end }}
<div class="card-body">
<h5 class="card-title"><a href="{{ .Permalink }}">{{ .Title }}</a></h5>
<p class="card-text">{{ .Summary }}</p>
<p class="card-text">
<small class="text-muted">
<i class="fas fa-calendar-alt"></i> {{ .Date.Format "2006-01-02" }}
{{ if gt .ReadingTime 1 }} {{ .Scratch.Set "readingTime" "mins" }} {{ else }} {{ .Scratch.Set "readingTime" "min" }} {{ end }} · {{ .ReadingTime }} {{ .Scratch.Get "readingTime" }}
</small>
</p>
</div>
</div>
</div>
If a blog post has any images at all, the first line is taken and split into two parts {{$img_options := (split (index .Params.Images 0) “|”)}}
. Later on the first element {{ trim (index $img_options 0) " " }}
is used as a file URL and the second one {{ trim (index $img_options 1) " " }}
- as an attribution text.
Parallax effect
Parallax effect works in a similar way, but with some additions. At first, it is not taken for granted that a blog post front matter images
variable has two items. That’s why an additional check is needed in the article layout:
{{ if isset .Params "images" }}
{{$img_options := (split (index .Params.Images 1) "|")}}
{{if gt (len (index $img_options 0)) 0}}
<script src="/js/parallax.js"></script>
<div class="parallax" data-type="background" style="margin-bottom:20px;">
<img src="{{.Permalink}}{{trim (index $img_options 0) " "}}" class="parallax__img">
<div class="parallax__text-box">
<h1 class="parallax__title">{{ .Title }}</h1>
<span class="parallax__subtitle"><time>Published on {{ .Date.Format "2006-01-02" }}</time></span>
{{if gt (len (index $img_options 1)) 0}}
<span class="parallax__subtitle" style="font-style: italic;">Cover image: {{trim (index $img_options 1) " "}}</span>{{ end }}
</div>
</div>
{{else}}
<div class="container" style="margin-bottom:20px;">
<h1>{{ .Title }}</h1>
<time>Published on {{ .Date.Format "2006-01-02" }}</time>
</div>
{{ end }}
{{ end }}
The line {{if gt (len (index $img_options 0)) 0}}
checks whether a second item of the images settings has something at all. If yes, then we can produce a blog post header with a parallax effect. If not - then a standard header is enough.
Second, small parallax.js script should be added to the page: <script src=“/js/parallax.js”></script>
. Original HTML/CSS + JS example can be found in this Codepen snippet. The example is very simple and doesn’t allow multiple images with parallax effect on the same page. But for me it does the job very well.
Page-specific feedback form title
Another thing I wanted to improve was a feedback form: it would be great to see a website page title where the user has filled the form. By default, formspree.io
uses a static email subject, which can be easily extended via Page title option:
<input type="hidden" name="_subject" id="email-subject"
value="stats-consult form submitted from page: {{ if .Page.Title }}{{ .Page.Title }}{{ else }}{{ .Site.Title }}{{ end }}">
SEO tweaks
So far, the website contains virtually no SEO elements. Search engines make some default assumptions, but it was a good exercise to remember SEO basics and introduce some important items.
robots.txt
Hugo
is capable of generating robots.txt file itself. I used an instruction from Google and added a few steps: excluded Rmd/md files and a few subfolders (as I didn’t implement a proper tags search yet). Also, a link to sitemap.xml was explicitly provided. The result looks like this:
# check the robots.txt instructions here https://developers.google.com/search/docs/advanced/robots/create-robots-txt#syntax
User-agent: *
Allow: /
Disallow: /*.md$
Disallow: /*.Rmd$
Disallow: /categories/
Disallow: /tags/
Sitemap: https://stats-consult.com/sitemap.xml
sitemap.xml
Sitemap.xml file should follow certain strict rules. For example, URLs for all pages should be explicit. Hugo
can create a default sitemap, but it was a bit too simplistic. I have added a few additional things: possibility to exclude certain pages (this can be done via page front matter settings) and slightly tweaked page URLs to make sure they provide full paths to each page. The resulting layout is here:
{{ printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
{{ range .Data.Pages }}{{ if ne .Params.sitemap_ignore true }}
<url>
<loc>{{ .Permalink }}</loc>{{ if not .Lastmod.IsZero }}
<lastmod>{{ safeHTML ( .Lastmod.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ end }}{{ with .Sitemap.ChangeFreq }}
<changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}
<priority>{{ .Sitemap.Priority }}</priority>{{ end }}{{ if .IsTranslated }}{{ range .Translations }}
<xhtml:link
rel="alternate"
hreflang="{{ .Language.Lang }}"
href="{{ .Permalink }}"
/>{{ end }}
<xhtml:link
rel="alternate"
hreflang="{{ .Language.Lang }}"
href="{{ .Permalink }}"
/>{{ end }}
</url>
{{ end }}{{ end }}
</urlset>
Conclusion
There are many other ways how a website can be improved with Hugo
(check a TODO list post), but for now I am satisfied with the current results, it was interesting and challenging to dig into a new CMS framework. I wouldn’t probably encourage everyone to go the same route, but it was definitely an exciting task.