Hugo Theme (Part 4) : GitHub style alerts
Intro
When I started creating this theme for Hugo, I did not have a good way to add alerts in the body of the posts. I was using markdown’s bold and italic style to get the reader’s attention. The following markdown
***Note:** This is a sample note.*
is render as
Note: This is a sample note.
This is very lightweight and easy to use but still lacks the necessary formatting to draw the reader’s attention. It is also hard to customize to different types of alerts. Let’s add some GitHub style alerts that are more user friendly. Check out the specifics about GitHub style alerts at https://github.com/orgs/community/discussions/16925
Markdown
As for writing this post, Hugo did not support custom render blocks for markdown. So, we will have to use a workaround to implement it. Hugo supports adding custom attributes to the elements. Read more about this at https://gohugo.io/getting-started/configuration-markup. We can enable this feature for adding CSS classes to block quotes by adding a rule to the hugo.toml file in our site
[markup.goldmark.parser.attribute]
block = true
This lets us add a CSS class to either the parent block quote element or its children. I added the class on the first child because it was easier to create the alerts with some markdown editors like Typora.
We will use the note block as an example for the rest of the post. The tip, important, warning and caution blocks will have similar implementations.
We will use the following markdown to create the note block
> [!NOTE]
>
> {.note}
>
> This is a sample note
The empty lines above and below {.note}
are optional. Typora adds them on pressing the return key, but some editors might not. The following markdown works as well
> [!NOTE]
> {.note}
> This is a sample note
The HTML generated by Hugo will now look like
<blockquote>
<p class="note">[!Note]</p>
<p>This is a sample note</p>
</blockquote>
CSS
Now, we can add some styles to it using CSS. We will some Tailwind CSS classes since this is a Tailwind based theme.
Change the border color, font and spacing
blockquote:has(.note) {
@apply border-blue-500;
@apply text-lg not-italic font-light;
}
.dark blockquote:has(.note) {
@apply border-blue-300;
}
blockquote:has(.note) p {
@apply p-0 m-0;
}
Hide the [!Note]
and replace it with an icon and Note
title. We can set the font size to zero to hide ![NOTE]
.
blockquote:has(.note) p:first-child {
@apply text-[0];
}
We can use the CSS ::before
to show the icon. We don’t want to hardcode the color to the SVG icon. We want to change it for easier access later and also to switch between light and dark modes. We can either use two separate icons or we can use a little trick with CSS mask
. We will set the background color of the ::before
pseudo-element and CSS mask
to show it in the shape of our icon
blockquote:has(.note) p:first-child::before {
content: "";
@apply inline-block bg-blue-500 w-6 h-6;
mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-info-circle alert-note" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" /><path d="M12 9h.01" /><path d="M11 12h1v4h1" /></svg>') no-repeat;
}
.dark blockquote:has(.note) p:first-child::before {
@apply bg-blue-300;
}
We can show the note title using the ::after
pseudo-element
blockquote:has(.note) p:first-child::after {
content: "Note";
@apply text-blue-500 text-base align-text-bottom px-1;
}
.dark blockquote:has(.note) p:first-child::after {
@apply text-blue-300;
}
We will have very similar CSS for the other types of alerts as well. Let’s extract the shared styles
blockquote:has(.note, .tip, .important, .warning, .caution){
@apply text-lg not-italic font-normal;
}
blockquote:has(.note, .tip, .important, .warning, .caution) p {
@apply p-0 m-0;
}
blockquote:has(.note, .tip, .important, .warning, .caution) p:first-child {
@apply text-[0];
}
blockquote:has(.note, .tip, .important, .warning, .caution) p:first-child::before {
content: "";
@apply inline-block w-6 h-6;
}
blockquote:has(.note, .tip, .important, .warning, .caution) p:first-child::after {
@apply text-base font-semibold align-text-bottom px-1;
}
Now for note, we will only have the styles to apply the color, icon and title
blockquote:has(.note) {
@apply border-blue-500;
}
.dark blockquote:has(.note) {
@apply border-blue-400;
}
blockquote:has(.note) p:first-child::before,
blockquote:has(.note) p:first-child::after {
@apply text-blue-500;
}
.dark blockquote:has(.note) p:first-child::before,
.dark blockquote:has(.note) p:first-child::after {
@apply text-blue-400;
}
blockquote:has(.note) p:first-child::before {
@apply bg-blue-500;
mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-info-circle alert-note" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" /><path d="M12 9h.01" /><path d="M11 12h1v4h1" /></svg>') no-repeat;
}
.dark blockquote:has(.note) p:first-child::before {
@apply bg-blue-400;
}
blockquote:has(.note) p:first-child::after {
content: "Note";
}
Just like the style for tip above, we can add the color, icon and title for other alerts. For full CSS check out the main.css file in the theme GitHub repository.
Results
Let’s check what they all look like now with the same text from the GitHub discussion
> [!NOTE]
> {.note}
> Highlights information that users should take into account, even when skimming.
> [!TIP]
> {.tip}
> Optional information to help a user be more successful.
> [!IMPORTANT]
> {.important}
> Crucial information necessary for users to succeed.
> [!WARNING]
> {.warning}
> Critical content demanding immediate user attention due to potential risks.
> [!CAUTION]
> {.caution}
> Negative potential consequences of an action.
[!NOTE]
Highlights information that users should take into account, even when skimming.
[!TIP]
Optional information to help a user be more successful.
[!IMPORTANT]
Crucial information necessary for users to succeed.
[!WARNING]
Critical content demanding immediate user attention due to potential risks.
[!CAUTION]
Negative potential consequences of an action.
Thank you for reading. I will post more content shortly. You can also check out other posts in this series below.