More Flexible Twitter Cards In Hugo

Note: I’ve created a GitHub repository for the template described in this post.

Twitter is my social media platform of choice, and Twitter Cards are Twitter’s way of enhancing the display of web pages (and other media) shared on the platform. Twitter Cards allow you to specifically define how the shared web page is to be viewed in the Twitter feed, with some constraints. The values for the Twitter Cards are defined using <meta> tags in the <head> section of a web page.

<!DOCTYPE html>
<html>
    <head>
        ... HTML Head stuff ...

        <meta name="twitter:card" content="summary" />
        <meta name="twitter:image" content="https://dereckcurry.com/images/twitter-card-summary-image.jpg" />
        <meta name="twitter:image:alt" content="Dereck Curry's avatar" />
        <meta name="twitter:title" content="More Flexible Twitter Cards In Hugo" />
        <meta name="twitter:description" content="Enhancing Hugo's default Twitter Card template to give site creators more control over card generation and content." />
        <meta name="twitter:creator" content="dereckcurry" />
        <meta name="twitter:site" content="dereckcurry" />

        ... More HTML Head stuff ...
    </head>
    <body>
        ... HTML Body stuff ...
    </body>
</html>

Unfortunately, the default Hugo template for generating Twitter Cards wasn’t really working for me. So I made a new template for use on my site, and wanted to share it here in case someone else found it useful.

The Default Hugo Twitter Card Template

Before digging into my template, let’s take a look at the default Hugo template. The template is written in the Go HTML Template language, and I’ve cleaned it up a bit below to make it easier to read and follow. If you want to see the original template, check it out at GitHub.

 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
{{- with $.Params.images -}}
    <meta name="twitter:card" content="summary_large_image"/>
    <meta name="twitter:image" content="{{ index . 0 | absURL }}"/>
{{  else -}}
{{-     $images := $.Resources.ByType "image" -}}
{{-     $featured := $images.GetMatch "*feature*" -}}
{{-     $featured := cond (ne $featured nil) $featured ($images.GetMatch "{*cover*,*thumbnail*}") -}}
{{-     with $featured -}}
    <meta name="twitter:card" content="summary_large_image"/>
    <meta name="twitter:image" content="{{ $featured.Permalink }}"/>
{{-     else -}}
{{-         with $.Site.Params.images -}}
    <meta name="twitter:card" content="summary_large_image"/>
    <meta name="twitter:image" content="{{ index . 0 | absURL }}"/>
{{          else -}}
    <meta name="twitter:card" content="summary"/>
{{-         end -}}
{{-     end -}}
{{- end }}
    <meta name="twitter:title" content="{{ .Title }}"/>
    <meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}"/>
{{ with .Site.Social.twitter -}}
    <meta name="twitter:site" content="@{{ . }}"/>
{{ end -}}
{{ range .Site.Authors }}
{{     with .twitter -}}
    <meta name="twitter:creator" content="@{{ . }}"/>
{{     end -}}
{{ end -}}

As you can see, the template uses a set of logic to output <meta> tags. The values for each of the <meta> tags depends on each of the Hugo page resources ($.Resources) and values (.Description, .Summary, .Title), and sometimes the front matter defined for the page ($.Params.images) and the site configuration itself ($.Site.Params.images, .Site.Params.description, .Site.Social.twitter, .Site.Authors).

The default Hugo Twitter Card template itself is expected to be called from the code that generates the <head> section for an HTML page. Check your theme’s layout directory and look for the call to the internal Twitter Cards template, {{ template “_internal/twitter_cards.html” . }}.

<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
    <head>
        ... Template Head stuff ...

        {{ template "_internal/twitter_cards.html" . }}

        ... More Template Head stuff ...
    </head>
    <body>
        ... Template Body Stuff
    </body>
</html>

As I understand it, this default template will either use the Twitter Card type of summary or summary_large_image. The Twitter Card types tell Twitter how to display the web page. Summary is used if no image is found for the page, or if an image is not defined in the page’s front matter or site configuration. If any image is found by the template, then the summary_large_image card type is used.

An example of the summary Twitter Card type follows.

And here is an example of the summary_large_image Twitter Card type.

Both examples look great in my opinion, but the real deciding factor for what looks best is the dimensions of the image to be displayed by the Twitter Card. For the summary card, the images that look best are square. Summary_large_image images that look best are landscapes.

Which gets me to my problem with the default Hugo Twitter Card template. By default, if any image is discovered, Hugo’s default is to use the summary_large_image, even if the image is square. On my site, I wanted to use my avatar as the default Twitter Card image, but the avatar is square, and it just didn’t look right in a landscape format.

Furthermore, the default Hugo template makes some assumptions about what image to use for the Twitter Card. And while those assumptions will work just fine most of the time, I wanted a little more control of what image to use. And while I was it, might as well make the entire card configurable.

Twitter Card Rules

So after thinking about it for a bit, I decided on the following rules to determine what Twitter Card type to use and what values should be used for the rest of the Twitter Card <meta> tags.

  1. First, determine if a Twitter Card should be generated at all.
    1. By default Twitter Cards should be generated.
    2. However, a site wide configuration setting can turn off generation for the entire site.
    3. But the settings in an individual page’s front matter can override the site wide and default settings.
  2. If the Twutter Card is not to be generated, then we’re done. Output no Twitter Card <meta> tags.
  3. But if the Twitter Card is to be generated, then the following Twitter Card values need configurable in both the site configuration and the individual page front matter.
    1. Twitter Card type
    2. Twitter Card image
    3. Twitter Card image alternate text
    4. Twitter Card title
    5. Twitter Card description
    6. Twitter Card creator
    7. Twitter Card site
  4. For Twitter Card values the following precedence should be followed.
    1. If defined, a page’s front matter values for the Twitter Card take the highest precedence and should be used above all others.
    2. If not defined, then the page’s resources and values should be used.
    3. If a value is still not set, then use the site’s Twitter Card configuration values, if they are defined.
    4. Finally, the template default values should be used if a Twitter Card value is still not defined. These template defaults might include values from the page’s front matter and the site’s non-Twitter Card configuration settings, as appropriate.
  5. At the end of determining values, if any Twitter Card value is set to false, then the <meta> tag for that value should not be generated.
  6. Also, if the Twitter Card type is set to false, then the entire Twitter Card should not be generated.
  7. And if the Twitter Card image is set to false, then the Twitter Card image alternate text <meta> should not be generated, even if the alternate image text contains a non false value.

The New Template

It seems like a lot of rules, but it really isn’t too bad.

Here’s the template I went with. This was my first foray into Go’s HTML Templating Language, so forgive me if I’ve not followed conventions.

  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
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
{{  $generate := true -}}
{{- if isset $.Params.twitterCard "generate" }}
{{-     $generate = $.Params.twitterCard.generate -}}
{{- else -}}
{{-     if isset $.Site.Params.twitterCard "generate" -}}
{{-          $generate = $.Site.Params.twitterCard.generate }}
{{-     end -}}
{{- end -}}
{{- if eq $generate true -}}
{{-     $card := "summary" -}}
{{-     $image := false -}}
{{-     with $.Site.Params.images -}}
{{-         $card = "summary_large_image" -}}
{{-         $image = (index . 0 | absURL) -}}
{{-     end -}}
{{-     $imageAlt := false -}}
{{-     $title := false -}}
{{-     $description := false -}}
{{-     with $.Site.Params.description -}}
{{-         $description = . }}
{{-     end -}}
{{-     $creator := false -}}
{{-     if isset $.Site.Author "twitter" -}}
{{-         $creator = $.Site.Author.twitter -}}
{{-     end -}}
{{-     $site := false -}}
{{-     with $.Site.Social.twitter -}}
{{-         $site = . -}}
{{-     end -}}
{{-     if isset $.Site.Params "twittercard" -}}
{{-         if isset $.Site.Params.twitterCard "card" -}}
{{-             $card = $.Site.Params.twitterCard.card -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "image" -}}
{{-             $image = ($.Site.Params.twitterCard.image | absURL) -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "imagealt" -}}
{{-             $imageAlt = $.Site.Params.twitterCard.imageAlt -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "title" -}}
{{-             $title = $.Site.Params.twitterCard.title -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "description" -}}
{{-             $description = $.Site.Params.twitterCard.description -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "creator" -}}
{{-             $creator = $.Site.Params.twitterCard.creator -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "site" -}}
{{-             $site = $.Site.Params.twitterCard.site -}}
{{-         end -}}
{{-     end -}}
{{-     $images := $.Resources.ByType "image" -}}
{{-     $featured := $images.GetMatch "*feature*" -}}
{{-     $featured = cond (ne $featured nil) $featured ($images.GetMatch "*cover*") -}}
{{-     with $featured -}}
{{-         $card = "summary_large_image" -}}
{{-         $image = $featured.Permalink -}}
{{-         $imageAlt = false -}}
{{-     else -}}
{{-         $thumbnail := $images.GetMatch "*thumbnail*" -}}
{{-         with $thumbnail -}}
{{-             $card = "summary" -}}
{{-             $image = $thumbnail.Permalink -}}
{{-             $imageAlt = false -}}
{{-         end -}}
{{-     end -}}
{{-     with .Title -}}
{{-         $title = . -}}
{{-     end -}}
{{-     with .Description -}}
{{-         $description = . -}}
{{-     else -}}
{{-         if .IsPage -}}
{{-              $description = .Summary -}}
{{-         end -}}
{{-     end -}}
{{-     if isset $.Params "twittercard" -}}
{{-         if isset $.Params.twitterCard "card" -}}
{{-             $card = $.Params.twitterCard.card -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "image" -}}
{{-             $image = ($.Params.twitterCard.image | absURL) -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "imagealt" -}}
{{-             $imageAlt = $.Params.twitterCard.imageAlt -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "title" -}}
{{-             $title = $.Params.twitterCard.title -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "description" -}}
{{-             $description = $.Params.twitterCard.description -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "creator" -}}
{{-             $creator = $.Params.twitterCard.creator -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "site" -}}
{{-             $site = $.Params.twitterCard.site -}}
{{-         end -}}
{{-     end -}}
{{-     if ne $card false }}
    <meta name="twitter:card" content="{{- $card -}}" />
{{-         if ne $image false }}
    <meta name="twitter:image" content="{{- $image -}}" />
{{-             if ne $imageAlt false }}
    <meta name="twitter:image:alt" content="{{- $imageAlt -}}" />
{{-             end }}
{{-         end }}
{{-         if ne $title false }}
    <meta name="twitter:title" content="{{- $title -}}" />
{{-         end }}
{{-         if ne $description false }}
    <meta name="twitter:description" content="{{- $description -}}" />
{{-         end }}
{{-         if ne $creator false }}
    <meta name="twitter:creator" content="{{- $creator -}}" />
{{-         end }}
{{-         if ne $site false }}
    <meta name="twitter:site" content="{{- $site -}}" />
{{-          end }}
{{-      end }}
{{  end }}

I attempted to retain as much logic from the default Hugo Twitter Card template as reasonable, while also expanding on the overall functionality.

Let’s dig into the code a bit.

To Generate Or Not To Generate

First things first, do we generate or not generate the Twitter Card? By default, the template generates the Twitter Card. Then it does whatever the front matter on the page says to do, if defined. If not defined, then it does whatever site configuration file says to do, if it is defined.

1
2
3
4
5
6
7
8
{{  $generate := true -}}
{{- if isset $.Params.twitterCard "generate" }}
{{-     $generate = $.Params.twitterCard.generate -}}
{{- else -}}
{{-     if isset $.Site.Params.twitterCard "generate" -}}
{{-          $generate = $.Site.Params.twitterCard.generate }}
{{-     end -}}
{{- end -}}

Note: I’m going to use TOML for the front matter and site configuration values in this post. So you might need to adapt the layout of these values to whatever you use for your pages and site.

For the page’s front matter, the template is looking for the existence of the twitterCard.generate value to determine whether to generate or not. In the example below, Twitter Card generation is turned off for the page as the generate value is set to false. It is only necessary to include the generate setting in the front matter in order to overrule the site configuration setting or the template default behavior of generating the Twitter Card.

+++
[twitterCard]
    generate = false
+++

If no twitterCard.generate value is set in the front matter, then the site configuration will be checked next. Here is an example of the generate setting in the site configuration.

[params.twitterCard]
    generate = false

The twitterCard.generate value only needs to be included in the site configuration if you don’t want Twitter Cards generated by default.

If the Twitter Card is not to be generated, then we’re done processing the template.

The Template Default Twitter Card Values

Because of the precedence requirements, this new Twitter Card template will start with the lowest precedence first and then move up the precedence ladder to modify the Twitter Card values as appropriate. The lowest precedence is defined to be the template default values. Wherever possible, the Twitter Card values defined in the default Hugo Twitter Card template are used.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{{-     $card := "summary" -}}
{{-     $image := false -}}
{{-     with $.Site.Params.images -}}
{{-         $card = "summary_large_image" -}}
{{-         $image = (index . 0 | absURL) -}}
{{-     end -}}
{{-     $imageAlt := false -}}
{{-     $title := false -}}
{{-     $description := false -}}
{{-     with $.Site.Params.description -}}
{{-         $description = . }}
{{-     end -}}
{{-     $creator := false -}}
{{-     if isset $.Site.Author "twitter" -}}
{{-         $creator = $.Site.Author.twitter -}}
{{-     end -}}
{{-     $site := false -}}
{{-     with $.Site.Social.twitter -}}
{{-         $site = . -}}
{{-     end -}}

Like the default Hugo Twitter Card template, this new template uses the summary Twitter Card type as the default value, as no image used is to be the default (line 11, $image := false) for the Twitter Card.

However, lines 12 through 15 set the card type to summary_image_large if an image array is defined in the site configuration ($.Site.Params.images). The first image defined in the array is selected and used for the Twitter Card. This is the same logic used on the default Hugo Twitter Card template. But this value can be overridden by the higher precedence value checks that occur later in this new template.

Like the default image value, several other of the Twitter Card default values are also false. From the rules above, values that are false are not to rendered into <meta> elements used in the <head> of the HTML page. So unless they are overridden by later code in the new template, these values will not be renderd by default.

Lines 19 through 21 will set the description Twitter Card value based upon the site’s configuration, if it is defined. Likewise lines 23 through 25 will set the creator value based upon the site’s configuration, if defined. Also, lines 27 through 29 will set the Twitter Card’s site value based upon the site configuration file, if defined. Again, these settings come from the default Hugo Twitter Card template and I tried to preserve those as much as possible in this new template.

Site Configuration Twitter Card Settings

The next lowest precedence is the site configuration Twitter Card specific settings. So we’ll check those next starting with line 30 of the new template.

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{{-     if isset $.Site.Params "twittercard" -}}
{{-         if isset $.Site.Params.twitterCard "card" -}}
{{-             $card = $.Site.Params.twitterCard.card -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "image" -}}
{{-             $image = ($.Site.Params.twitterCard.image | absURL) -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "imagealt" -}}
{{-             $imageAlt = $.Site.Params.twitterCard.imageAlt -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "title" -}}
{{-             $title = $.Site.Params.twitterCard.title -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "description" -}}
{{-             $description = $.Site.Params.twitterCard.description -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "creator" -}}
{{-             $creator = $.Site.Params.twitterCard.creator -}}
{{-         end -}}
{{-         if isset $.Site.Params.twitterCard "site" -}}
{{-             $site = $.Site.Params.twitterCard.site -}}
{{-         end -}}
{{-     end -}}

For each site configuration Twitter Card setting, the template checks for the existence of the configuration value before overriding the existing value determined by the template up to now. The template follows this pattern throughout the rest of the new template code.

An example TOML site configuration for Twitter card settings would be similar to the following.

[params.twitterCard]
    generate = true
    card = "summary"
    image = "images/twitter-card-summary-image.jpg"
    imageAlt = "Dereck Curry's avatar"
    site = "dereckcurry"
    creator = "dereckcurry"
    title = "DereckCurry.com Page"
    description = "A page from DereckCurry.com"

In the above example, generate is not necessary since it is set to true. It is included here to show a complete list of site configuration settings for the Twitter Card. Also, it is unlikely that you would normally set the title or the description values, as they will likely come come from the template defaults or be specific to a page.

So a more typical site configuration would more like the one below.

[params.twitterCard]
    card = "summary"
    image = "images/twitter-card-summary-image.jpg"
    imageAlt = "Dereck Curry's avatar"
    site = "dereckcurry"
    creator = "dereckcurry"

In the example, the site configuration for the Twitter Cards sets the card type to summary since the default image is square, not landscape.

Better control of the card type and image Twitter Card values is the reason this new template exists. Everything else that this template allows to be configurable were written now so I wouldn’t have to do so in the future once I decided I needed more control over those particular values. Technically, this isn’t MVP, but it really isn’t much bloat or technical debt either. And in reality, I ended up defining the description value in this page’s front matter (see below) to provide a better description of this post for Twitter.

If you don’t want to include any site configuration defaults for the Twitter Card template, you don’t have to. The code checks for their existence, and if they are not there, it continues without error.

Page Resources And Values Used For Twitter Card Values

The next highest precedence for Twitter Card values are those defined by each page of the site. These are not the specifically defined Twitter Card values, but other values associated with the page.

This segment of code also mostly comes from the default Twitter Card template that comes with Hugo. Again, I wanted to leave as much of that logic in tact.

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
{{-     $images := $.Resources.ByType "image" -}}
{{-     $featured := $images.GetMatch "*feature*" -}}
{{-     $featured = cond (ne $featured nil) $featured ($images.GetMatch "*cover*") -}}
{{-     with $featured -}}
{{-         $card = "summary_large_image" -}}
{{-         $image = $featured.Permalink -}}
{{-         $imageAlt = false -}}
{{-     else -}}
{{-         $thumbnail := $images.GetMatch "*thumbnail*" -}}
{{-         with $thumbnail -}}
{{-             $card = "summary" -}}
{{-             $image = $thumbnail.Permalink -}}
{{-             $imageAlt = false -}}
{{-         end -}}
{{-     end -}}
{{-     with .Title -}}
{{-         $title = . -}}
{{-     end -}}
{{-     with .Description -}}
{{-         $description = . -}}
{{-     else -}}
{{-         if .IsPage -}}
{{-             $description = .Summary -}}
{{-         end -}}
{{-     end -}}

But I did make one change from the Hugo default Twitter Card template logic. Specifically, when the template is searching for images in the $.Resource values, if an image name contains *thumbnail* then the card type of summary is used. In the original template, the type of summary_large_image is used regardless of the image name. It just made more sense to me to use the summary type for thumbnails as they are likely much smaller in size than the feature or cover images sought out by this segment of code. Feel free to change this if it turns out not to be the experience you have on your site.

Page Front Matter Twitter Card Settings

Finally, we get to the final value checks, those with the highest precedence. These are the Twitter Card specific values defined in the front matter of a page, and they will override all other values and settings determined to this point in the template.

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
{{-     if isset $.Params "twittercard" -}}
{{-         if isset $.Params.twitterCard "card" -}}
{{-             $card = $.Params.twitterCard.card -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "image" -}}
{{-             $image = ($.Params.twitterCard.image | absURL) -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "imagealt" -}}
{{-             $imageAlt = $.Params.twitterCard.imageAlt -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "title" -}}
{{-             $title = $.Params.twitterCard.title -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "description" -}}
{{-             $description = $.Params.twitterCard.description -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "creator" -}}
{{-             $creator = $.Params.twitterCard.creator -}}
{{-         end -}}
{{-         if isset $.Params.twitterCard "site" -}}
{{-             $site = $.Params.twitterCard.site -}}
{{-         end -}}
{{-     end -}}

An example front matter TOML configuration is below.

+++
[twitterCard]
    generate = true
    card = "summary"
    image = "images/twitter-card-summary-image.jpg"
    imageAlt = "Dereck Curry's avatar"
    site = "dereckcurry"
    creator = "dereckcurry"
    title = "DereckCurry.com Page"
    description = "A page from DereckCurry.com"
+++

The generate param is not used in this section of code, but is included for completeness. You should only need to include this param and set the value to true if the twitterCard.generate param value is set to false in the site configuration.

Notice that these params are named identically to the site configuration params.

It should not be necessary to manually set all these params and values, as the site configuration and page values previously determined will suffice in most cases. If you don’t need them, no need to include them in the front matter. The code checks to see if they are there, and if they aren’t, it moves on without error.

However, I used the description param value to provide a more concise description of this post for sharing via Twitter.

+++
draft = false
title = "More Flexible Twitter Cards In Hugo"
date = 2019-04-05T11:49:00-04:00
tags = [ "Hugo", "Twitter" ]
categories = [ "Hugo" ]
[twitterCard]
    description = "Enhancing Hugo's default Twitter Card template to give site creators more control over card generation and content."
+++

Outputting The Twitter Card Meta Tags

The final section of code is for the generation of the Twitter Card specific <meta> tags used in the <head> section of the HTML of each web page.

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
{{-     if ne $card false }}
    <meta name="twitter:card" content="{{- $card -}}" />
{{-         if ne $image false }}
    <meta name="twitter:image" content="{{- $image -}}" />
{{-             if ne $imageAlt false }}
    <meta name="twitter:image:alt" content="{{- $imageAlt -}}" />
{{-             end }}
{{-         end }}
{{-         if ne $title false }}
    <meta name="twitter:title" content="{{- $title -}}" />
{{-         end }}
{{-         if ne $description false }}
    <meta name="twitter:description" content="{{- $description -}}" />
{{-         end }}
{{-         if ne $creator false }}
    <meta name="twitter:creator" content="{{- $creator -}}" />
{{-         end }}
{{-         if ne $site false }}
    <meta name="twitter:site" content="{{- $site -}}" />
{{-          end }}
{{-      end }}

First the code does a sanity check to see if the card setting was set to false for some reason. There is no good reason it should be set to false. But if it is, then the Twitter Card is not generated.

The rest of the Twitter Card values go through a similar check for false. A false value will keep the <meta> tag from being generated.

And that wraps up the new Twitter Card template codeg.

How To Use The New Template

For my site, I named the new template configurable_twitter_cards.html and placed it in the /layouts/partials/ directory.

dereckcurry.com
    layouts
        partials
            configurable_twitter_cards.html

Then I located where the existing default Hugo template was being called in my theme, copied that theme file from the theme layout directory to the corresponding site layout directory. The /layouts/ directory will take precedence over the /themes/hugo-coder/layouts/ directory when generating the site.

In my case, the site theme layout file that contained the call to the default Hugo Twitter Card template was the /themes/hugo-coder/layouts/_default/baseof.html file.

dereckcurry.com
    themes
        layouts
            _default
                baseof.html

The directory structure of my site for this new template looks like this:

dereckcurry.com
    layouts
        _default
            baseof.html
        partials
            configurable_twitter_cards.html

Next, I simply replaced one line in the /layouts/_default/baseof.html file. The line containing {{ template “_internal/twitter_cards.html” . }} was replaced with {{ partial “configurable_twitter_cards.html” . }}.

So, this:

<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
    <head>
        ... Template Head stuff removed for clarity ...

        {{ template "_internal/twitter_cards.html" . }}

        ... Lots of template Head stuff removed for clarity ...
    </head>
    <body class="{{ if .Site.Params.rtl }}rtl{{ end }} {{ if .Site.Params.inverted }}inverted{{ end }}">
        ... Template Body stuff removed for clarity ...
    </body>
</html>

Became this:

<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
    <head>
        ... Template Head stuff removed for clarity ...

        {{ partial "configurable_twitter_cards.html" . }}

        ... Lots of template Head stuff removed for clarity ...
    </head>
    <body class="{{ if .Site.Params.rtl }}rtl{{ end }} {{ if .Site.Params.inverted }}inverted{{ end }}">
        ... Template Body stuff removed for clarity ...
    </body>
</html>

The site now uses the new configurable_twitter_cards.html template when the static files are generated.

Although it not necessary, it would be a good idea to set the default Twitter Card values in the site config to make certain that the template defaults are overridden for certain Twitter Card values.

[params.twitterCard]
    card = "summary"
    image = "images/twitter-card-summary-image.jpg"
    imageAlt = "Dereck Curry's avatar"
    site = "dereckcurry"
    creator = "dereckcurry"

But as stated earlier, it is also possible to override the default template values, the site config values, and the values determined from the page itself by setting the Twitter Card values in the page’s front matter. For this post, I decided to override the description value that was pulled from the page content to something more concise and descriptive.

+++
draft = false
title = "More Flexible Twitter Cards In Hugo"
date = 2019-04-05T11:49:00-04:00
tags = [ "Hugo", "Twitter" ]
categories = [ "Hugo" ]
[twitterCard]
    description = "Enhancing Hugo's default Twitter Card template to give site creators more control over card generation and content."
+++

Caveats

I don’t like that I had to modify the baseof.html template file. This is a pretty essential file for the theme, and I’m concerned that future theme updates might change this file. If they do, then I’ll need to copy and modify the new theme file. This is the sort of technical debt I normally would like to stay away from. However, I don’t have much choice in this case.

Although I’m pretty happy with the Hugo Coder theme that I’m using so far, it would be nice if the included baseof.html file was made more modular using partials templates. In that way, a user may only need to override a small partial instead of the larger baseof.html file for changes such as those made here for the new template.

That being said, the code works for me and the testing I’ve done with my site. So, if you want to use the Twitter Cards template I’ve provided, feel free. But do test it too, as there might be some bugs still lurking in there. And if you do find any bugs, please let me know so that I can update my file and this post. Thanks.

Conclusion

I hope you find this new template useful.

Please let me know if you there areas that can be improved in the post and the template.

Thanks.

Dereck

comments powered by Disqus