I often want to show multiple languages in one post. Instead of posten them sequentially, I enjoy to have them in tabs that allow me to easily switch between them. I didn’t find an out-of-the box feature that Hugo (or my theme) provides, so here’s a step-by-step guide to implement this feature in your own blog:
Here’s an example for some R code that you can show to your readers:
a <- 7
b <- 5
And here’s the same in Python:
a = 7
b = 5
And here are the steps that you need to follow to implement it:
In your shortcodes
folder, create a new file called tabs.html
. You can do this manually or by using the following command:
touch tabs.html
Open the file and add the following content:
{{ $id := .Get "id" | default "tabs" }}
{{ $titles := .Get "titles" }}
{{ $contents := .Inner }}
<div class="tabs-container" id="{{ $id }}">
<ul class="tabs-nav">
{{ $titleList := split $titles "," }}
{{ range $i, $title := $titleList }}
<li class="tab-nav" data-tab="{{ $i }}">{{ $title }}</li>
{{ end }}
</ul>
<div class="tabs-content">
{{ $delimiters := split $contents "<!-- tab -->" }}
{{ range $i, $content := $delimiters }}
<div class="tab-pane" data-tab="{{ $i }}">
{{ $content | markdownify }}
</div>
{{ end }}
</div>
</div>
<style>
.tabs-container {
font-family: Arial, sans-serif;
}
.tabs-nav {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
.tab-nav {
padding: 10px;
cursor: pointer;
border: 1px solid #ccc;
border-radius: 3px 3px 0 0;
margin-right: 5px;
}
.tab-nav.active {
background-color: #f1f1f1;
border-bottom: 1px solid transparent;
}
.tabs-content {
border: 1px solid #ccc;
border-radius: 0 0 3px 3px;
padding: 10px;
}
.tab-pane {
display: none;
}
.tab-pane.active {
display: block;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
const container = document.getElementById('{{ $id }}');
if (!container) return;
const tabs = container.querySelectorAll('.tab-nav');
const panes = container.querySelectorAll('.tab-pane');
tabs.forEach(tab => {
tab.addEventListener('click', function () {
tabs.forEach(t => t.classList.remove('active'));
panes.forEach(p => p.classList.remove('active'));
this.classList.add('active');
const index = this.getAttribute('data-tab');
container.querySelector(`.tab-pane[data-tab="${index}"]`).classList.add('active');
});
});
// Activate the first tab by default
if (tabs.length > 0) {
tabs[0].classList.add('active');
panes[0].classList.add('active');
}
});
</script>
Save it. Now you’re good to go 🚀
To call it, simply use the following pattern in your markdown-based blog post:
{{< tabs id="example2" titles="Tab title 1, Tab title 2, Tab title 3" >}}
Some text
<!-- tab -->
Some more text for tab 2.
<!-- tab -->
And here's tab 3.
{{< /tabs >}}
This will generate the following version:
What’s important to remember when you’re using the implementation:
tabs id="your id"
. Think of ids as meaningful names for your tab collections.titles
section, separate tabs are separated by comma (,
). See for instance titles="Tab title 1, Tab title 2, Tab title 3"
in the example.<!-- tab -->
.Happy tabbing 🙌