Customising This Website
Table of Contents
Out of the box, Hugo and Congo provide a solid foundation for building a clean, responsive website. However, the default style might not align with your vision, or you may want to incorporate custom functionality. In this post, I’ll walk you through how I customized my Hugo website. By the end, you’ll have the tools to enhance both the design and interactivity of your site.
If you’re curious about why Hugo is a great choice, check out this post. To learn how to deploy your own Hugo website, refer to this guide.
Tailwind CSS #
Tailwind CSS is a utility-first CSS framework that takes a unique approach to styling web pages. Unlike traditional CSS frameworks with predefined components and rigid designs, Tailwind provides low-level utility classes, allowing you to build custom designs directly in your HTML. This approach can save time and deliver a polished, professional look without the need for repetitive CSS coding.
Building the theme CSS from source #
If you’re using the Congo theme, your website already leverages a default Tailwind CSS configuration. To modify this setup—or if your theme doesn’t use Tailwind—you’ll need to install Tailwind dependencies and configure the tailwind.config.js
file. This will enable Tailwind’s Just-In-Time (JIT) compiler and allow you to rebuild the theme CSS from source. For this step, ensure that npm is installed on your local machine.
cd themes/congo
npm install
# and if no tailwind.config.js file exists:
npx tailwindcss init
If using a theme that does not include Tailwind out of the box, you will just need to add the necessary paths in your tailwind.config.js
file:
themes/congo/tailwind.config.js
|
|
With the dependencies installed and the tailwind.config.js
file configured, you can use Tailwind CLI to invoke the JIT compiler:
cd ../..
./themes/congo/node_modules/tailwindcss/lib/cli.js -c ./themes/congo/tailwind.config.js -i ./themes/congo/assets/css/main.css -o ./assets/css/compiled/main.css --jit
The compiler reads the themes/congo/tailwind.config.js
file and generates a compiled CSS file in the assets/css/compiled/
folder. This file contains all the necessary CSS for the files specified in the content
array.
One important consideration: you can only use Tailwind classes in these files if they are already defined in the tailwind.config.js
file. Because of how Hugo handles file hierarchy, the tailwind.config.js
file in your project will automatically override the default configuration provided by the theme, including the compiled CSS file (themes/congo/assets/css/compiled/main.css
).
Customizing the Default Theme #
Here are the changes I made to the default Tailwind CSS theme bundled with Congo:
Inline code blocks #
Example: this is some inline code
themes/congo/tailwind.config.js
|
|
code:not(pre code)
- Targets all<code>
elements that are not inside<pre>
tags. i.e inline code blockscode::before
andcode::after
- Removes the backticks that Hugo would automatically add around inline code blocks
Links #
Example: this is a link
themes/congo/tailwind.config.js
|
|
a
- Configures the base styling for links (<a>
tags).&:hover
- Modifies the styling for links when hovered overa code
- Ensure that code snippets within links use the proper code color variable
Keyboard inputs #
Example: CMD + P
themes/congo/tailwind.config.js
|
|
This configures the styling for keyboard inputs (<kbd>
tags).
Highlighted text #
Example: important text
themes/congo/tailwind.config.js
|
|
This configures the styling for highlighted text (<mark>
tags).
List spacing #
themes/congo/tailwind.config.js
|
|
This targets ordered list items (ol > li
), nested unordered lists (ol > li > ul
), nested list items (ol > li > ul > li
) and list items containing lists (ol > li:has(> ul)
). The main purpose of this is to ensure that the spacing between list items is consistent and that paragraphs inside list items have no margins.
Code Blocks #
While writing my Obsidian periodic notes post, I noticed that some of the code blocks were quite long, making the content harder to navigate. To improve the user experience, I added expand/collapse functionality to these code blocks. This involved creating custom Tailwind CSS classes, writing some JavaScript, and working with Hugo’s templating system. Along the way, I also added titles to the code blocks and adjusted the syntax highlighting logic.
Thankfully, Tailwind CSS allows you to define reusable utility classes beyond its default set. This is especially useful for grouping related styles and incorporating raw CSS properties that aren’t available as Tailwind utilities. You can use the @apply
directive within these custom classes to include existing Tailwind utilities, making it easier to maintain consistent styling.
themes/congo/tailwind.config.js
|
|
code-title
- Creates a tab-like element above code blocks e.g. thethemes/congo/tailwind.config.js
title for the code block above.code-block
- Removes the rounded top-left corner of code blocks to match with the title and styles the transition for the expand/collapse feature.code-block-container
- Adds a bottom margin.expand-toggle-container
andexpand-toggle
- Aligns the expand/collapse button with the code block and styles it.no-line-numbers
- Allows for the removal of line numbers from code blocks (see below for more details).
JavaScript #
The JavaScript below handles the interactive expand/collapse functionality by:
- Finding all expand toggles on the page
- Managing the max-height of code blocks
- Toggling between “Expand” and “Collapse” text
- Handling the transition animations
assets/js/custom.js
|
|
Hugo Shortcodes #
Shortcodes in Hugo are reusable content snippets that you can embed in your Markdown files. They’re ideal for dynamic features like embedding videos, generating tables, or adding custom design elements. In addition to the default Hugo shortcodes and the Congo shortcodes, you can also create custom shortcodes of your own. Hugo shortcodes are built using Go templates, which means you can leverage the full power of Go to create any functionality you need.
For example, I created a custom shortcode (<showcode>
) to display code examples on this blog, which works alongside the Tailwind CSS classes and JavaScript mentioned above.
layouts/shortcodes/showcode.html
|
|
- The shortcode takes three optional parameters in lines 2-4:
lang
- The language of the code block e.g.go
,javascript
,bash
etc.options
- The options to pass to thehighlight
function e.g.linenos=table
,linenos=false
title
- The title of the code block
- If a title is provided, lines 5-7 shows it above the code block with styling from the custom
.code-title
class - Lines 8-9 retrieves the actual code to be displayed from the shortcode’s inner content (
.Inner
), trims any whitespace and then splits it into lines to count them. - Line 11 creates a code block with a maximum height of
27rem
(i.e. the height of the collapsed code block). - Lines 12-19 conditionally applies the
no-line-numbers
class to the code block depending on the options passed and uses Hugo’shighlight
function to display the code. This allows for line highlighting without line numbers. - Lines 22-29 adds an expand/collapse button if the code block has more than 20 lines.
Partials #
Hugo projects use a template lookup system to select the appropriate template for rendering a page. This lookup order allows you to override theme templates by creating your own templates in the layouts/
folder. For example, to override the main article template in Congo (themes/congo/layouts/_default/single.html
), you can create your own layouts/_default/single.html
file.
Custom JavaScript Files #
To use a custom JavaScript file like the one mentioned above, you’ll need to include it in the asset bundle. This can be done through Hugo’s partial system: simply copy the themes/congo/layouts/partials/head.html
file, save it as layouts/partials/head.html
, and then add the following code somewhere within the {{/* Title */}}
section.
layouts/partials/head.html
|
|
This ensures that your custom JS file is bundled with other JS files before Hugo minifies and fingerprints it. The final bundle will then be included in the HTML output of your website.
Comment Functionality #
Since Hugo is a static site generator, it doesn’t natively support dynamic features like comments. However, you can integrate third-party services such as Giscus to add commenting functionality to your website. Giscus is an open-source, lightweight solution that uses GitHub Discussions to store comments. The only downside is that users need a GitHub account to comment.
To integrate this functionality, visit the Giscus website, configure it, and copy the provided script. Then, add the script to your Hugo project, typically in layouts/partials/comments.html
. Make sure the showComments
parameter (or a similar one if using a theme other than Congo) is set to true
.
Once you’ve done this, any new blog posts you create will display the comments section below the post.
Conclusion #
In this guide, we’ve covered several ways to customize a Hugo website:
- Using Tailwind CSS for flexible and maintainable styling
- Adding interactive features with JavaScript
- Creating custom shortcodes for reusable components
- Leveraging Hugo’s partial system to override templates
These customizations enhance both the visual appeal and user experience of your site. Hugo’s flexibility also opens up many additional possibilities for tailoring your site beyond what we’ve discussed here.
If you have any questions or would like to share your own customizations, feel free to leave a comment below.