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.