Hugo Shortcodes
Hugo shortcodes are useful when you need to output HTML from your markdown that is incompatible with the goldmark or black friday parser. When the markdown parser encounters an HTML element, it skips processing until that element is closed. The div
custom shortcode included below is an example of how to workaround this issue.
Custom shortcodes
Video
Here is the output for the Hugo youtube
shortcode when only the YouTube Video ID is supplied as a positional parameter.
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe src="https://www.youtube.com/embed/SsoOG6ZeyUI" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen="" title="YouTube Video"></iframe>
</div>
For custom style, use a class. Note that the inline style will be removed from the youtube shortcode output when a named parameter like class is used. Additionally, the id
parameter is required for the YouTube Video ID when you add a class parameter. e.g.,
{{< youtube id="w7Ft2ymGmfc" class="video-wrapper" >}}
Respective .video-wrapper
Sass:
.video-wrapper {
position: relative;
padding-bottom: 56.25%;
height: 0;
overflow: hidden;
> iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border:0;
}
}
Custom Shortcodes
Here are some custom shortcodes and tips for creating your own Hugo shortcodes.
If your writing documentation for Hugo shortcodes, you will need to escape the shortcode processing. e.g.,
{{</* code */>}}
will be rendered as{{< code >}}
.
div.html
This shortcode is for outputting a div
element that wraps your markdown. Useful when you want to specify specific styling for the div
and its nested or child elements.
{{/*-----------------------------------------------------------
{{< div someclass >}} markdown {{< / div >}}
{{< div class="classList" >}} markdown {{< / div >}}
{{< div style="property:value" >}} markdown {{< / div >}}
style parameter: since the semicolon declaration separator `;`
is not permitted, only one declaration allowed
------------------------------------------------------------*/}}
<div
{{- if .IsNamedParams -}}
{{ if .Get `class` }} class="{{ .Get `class` }}"{{ end }}
{{ if .Get `style` }} style="{{ .Get `style` }}"{{ end }}
{{- else -}}
{{ if .Get 0 }} class="{{ .Get 0 }}"{{ end }}
{{- end -}}>
{{ .Inner | markdownify }}
</div>
picture.html
Picture element
{{/*-----------------------------------------------------------
{{< picture src="/images/filename" ext="png" alt="" caption="" class="" >}}
------------------------------------------------------------*/}}
{{ if .Get `class`}}
<div class="{{ .Get `class` }}">
{{ end }}
{{ if .Get `caption`}}
<figure>
{{ end }}
<picture>
<source media="(min-width: 768px)"
srcset="{{ .Get `src` }}.{{ .Get `ext` }}">
<source media="(max-width: 767px)"
srcset="{{ .Get `src` }}-m.{{ .Get `ext` }}">
<img src="{{ .Get `src` }}.{{ .Get `ext` }}"
alt="{{ .Get `alt` }}">
</picture>
{{ if .Get `caption`}}
<figcaption>{{ .Get `caption` }}</figcaption>
</figure>
{{ end }}
{{ if .Get `class`}}
</div>
{{ end }}
Tabs
This is a complex shortcode for creating tabbed content. Here is a working example:
tab.html
{{/*-----------------------------------------------------
Depends on {{< tabcontent >}}
{{< tab set1 tab1 active >}}Tab 1{{< /tab >}}
{{< tab set1 tab2 >}}Tab 2{{< /tab >}}
-----------------------------------------------------*/}}
<button data-tabset="{{ .Get 0 }}" data-tabcontent="{{ .Get 1 }}" {{ with .Get 2 }}class="active"{{ end }}>{{ .Inner | markdownify }}</button>
tabcontent.html
{{/*----------------------------
Depends on {{< tab >}} for tab buttons
{{< tabcontent set1 tab1 >}}
markdown content
{{< /tabcontent >}}
{{< tabcontent set1 tab2 >}}
markdown content
{{< /tabcontent >}}
-----------------------------*/}}
<div class="tabcontent" data-tabset="{{ .Get 0 }}" data-tabcontent="{{ .Get 1 }}">{{ .Inner | markdownify }}</div>
The parameter for the
data-tabset
at position 0, e.g.,set1
, must be unique for the tabs used on a given markdown page.
The tab
and tabcontent
shortcodes depend upon the following Sass and JavaScript. Convert the Sass to CSS using SassMeister if you prefer.
$color__background-light: #f8f8f8;
$color__light-gray: #eaeaea;
$color__text-main: #404040;
.tabs {
display: flex;
border-bottom: 1px solid $color__light-gray;
max-width: 100%;
}
.tabs button {
min-width: 100px;
color: lighten($color__text-main, 30%);
background-color: $color__background-light;
border: 1px solid $color__light-gray;
border-top-right-radius: 1rem;
border-top-left-radius: 1rem;
outline: none;
cursor: pointer;
margin-bottom: -1px;
padding: 14px 16px;
transition: 0.3s;
}
.tabs button:hover {
color: inherit;
background-color: darken($color__background-light, 5%);
}
.tabs button.active {
color: inherit;
background-color: white;
border-bottom: white;
}
.tabcontent {
display: none;
}
.tabcontent.active {
display: block;
}
JavaScript for the tab
and tabcontent
shortcodes:
(function() {
var tab = document.querySelectorAll('button[data-tabset]');
if (tab != null) {
var i, el, tabcontent, tabset, tabSetList, tabContentList;
var clear = function(nodeList) {
for (i = 0; i < nodeList.length; i++) {
nodeList[i].classList.remove('active');
}
}
var onTabClick = function() {
tabset = event.target.dataset.tabset;
tabcontent = event.target.dataset.tabcontent;
tabSetList = document.querySelectorAll('button[data-tabset="'+ tabset +'"]');
tabContentList = document.querySelectorAll('.tabcontent[data-tabset="'+ tabset +'"]');
clear(tabSetList);
event.target.classList.add('active');
clear(tabContentList);
el = document.querySelector('.tabcontent[data-tabset="' + tabset + '"].tabcontent[data-tabcontent="' + tabcontent + '"]');
if (el != null) {
el.classList.add('active');
}
}
for (i = 0; i < tab.length; i++) {
tabset = tab[i].dataset.tabset;
tabcontent = tab[i].dataset.tabcontent;
// add `tabs` class to parent element
if (!tab[i].parentElement.classList.contains('tabs')) {
tab[i].parentElement.classList.add('tabs');
}
// show active content
if (tab[i].classList.contains('active')) {
el = document.querySelector('.tabcontent[data-tabset="' + tabset + '"].tabcontent[data-tabcontent="' + tabcontent + '"]');
if (el != null) {
el.classList.add('active');
}
}
}
for (i = 0; i < tab.length; i++) {
tab[i].addEventListener('click', onTabClick);
}
}
})();