This guide walks through installing Mono and setting up a site.

Initial Setup

Install Hugo

Hugo is a fast static site generator. Follow the official Hugo installation guide. After installation, verify it works:

bash
hugo version

For full functionality, we recommend Hugo v0.139 or later as we tested Mono with this version.

Create a New Site

Create a new Hugo site:

bash
hugo new site my-site
cd my-site

This creates a directory with Hugo’s default structure:

my-site/
├── archetypes/    # Templates for new content
├── content/       # Pages and posts
├── data/          # Data files (TOML, YAML, JSON)
├── layouts/       # Custom templates (optional)
├── static/        # Static files (images, etc.)
├── themes/        # Installed themes
└── hugo.toml      # Site configuration

Install Mono

Themes go in the themes/ directory. Choose one of these methods:

Keeps theme files separate from the repository and makes updates easy.

bash
git init
git submodule add https://github.com/yassouali/mono.git themes/mono

To update the theme later:

bash
git submodule update --remote --merge

When cloning the site on a new machine, run git submodule update --init --recursive to fetch the theme.

Git Clone

Downloads the theme directly into the project.

bash
git clone https://github.com/yassouali/mono.git themes/mono --depth=1

To update the theme later:

bash
cd themes/mono
git pull
cd ../..
Hugo Module

Uses Go modules instead of storing theme files locally. Requires Go.

bash
hugo mod init github.com/yourusername/my-site

Then add to hugo.toml:

toml
[module]
  [[module.imports]]
    path = "github.com/yassouali/mono"

To update the theme later:

bash
hugo mod get -u

Configure and Run

Add the theme to hugo.toml:

toml
theme = "mono"

Start the development server:

bash
hugo server --noHTTPCache -D

Open http://localhost:1313 to see the site. The server watches for changes and reloads automatically.

Using the Example Site

The site will be empty at this point. The easiest way to get started is to use the included example site.

bash
cp themes/mono/exampleSite/hugo.toml .
cp -r themes/mono/exampleSite/content .
cp -r themes/mono/exampleSite/data .
cp -r themes/mono/exampleSite/static .
cp -r themes/mono/exampleSite/assets .

After running hugo server --noHTTPCache -D, the example website can then be viewed locally at http://localhost:1313. In the command above, -D flag includes drafts, and --noHTTPCache disables browser caching to see the changes immediately.

Site Structure: By starting with the provided example website, we have a ready-to-use website with the following structure inside exampleSite/:

  • content/: Pages and posts: here we can edit the relevant blog posts and pages,
    • posts/: Blog posts, each Markdown file represents a new blog post entry. The front matter at the top of each file contains metadata like title, date, tags, and description. The default archetype for new posts is defined in archetypes/default.md,
    • _index.md: The homepage content,
    • about.md: An example for the about page,
    • projects.md: An example for the projects page, the list of projects is sourced from data/projects.toml,
  • data/: Structured content, used mainly to list the projects that are rendered on the projects page,
  • static/: Images and other static files,
  • assets/: Custom CSS (processed by Hugo Pipes),
  • hugo.toml: Main configuration file.

To configure the site, edit exampleSite/hugo.toml:

toml
baseURL = "https://yourwebsite.com/"
title = "Your Name"
defaultContentLanguage = "en"  # en, es, fr, de, zh, see i18n folder

[params]
  author = "Your Name"
  description = "Your site description"

# Social links (to appear in the footer)
[[params.social]]
  name = "GitHub"
  url = "https://github.com/yourusername"
  icon = "github"
[[params.social]]
  name = "Twitter"
  url = "https://twitter.com/yourusername"
  icon = "twitter"

Mono includes translations for English, Spanish, French, German, and Chinese. To change the language, set defaultContentLanguage to the desired language code (en, es, fr, de, or zh).

Supported social icons: GitHub, Twitter, LinkedIn, Instagram, Mastodon, Bluesky, Threads, Google Scholar. For more details about theme configuration, see the Theme Configuration post.


Creating Content

New Blog Post

To write a new blog post, simply create a new Markdown file in content/posts/ or use Hugo’s new content command, which will automatically populate the new file with the front matter defined in archetypes/default.md:

bash
hugo new posts/my-post.md

We can then write our blog post in standard Markdown format. For the full functionality of Mono and all its available options, please refer to the Typography & Markdown post in this demo site.

After finishing the post, we can remove draft: true from the front matter to publish it.

New Page

To create a new page outside the standard blog posts, we can create a new markdown file directly in content/. For example, to create an About page:

bash
hugo new about.md

Pages created in content/ (and not in content/posts/) use the default single page layout (layouts/_default/single.html). We can specify a different layout in the front matter if needed:

yaml
---
title: "About"
layout: "about"
---

To create a custom layout, we add an HTML file in layouts/_default/. For example, to create the “about” layout referenced above, we create layouts/_default/about.html:

html
{{ define "main" }}
<article class="about-page">
  <h1>{{ .Title }}</h1>
  {{ .Content }}
</article>
{{ end }}

This template extends baseof.html (that contains the common structure, like <head>, <header>, and <footer>) and defines the “main” block. Hugo will automatically use this layout for any page with layout: "about" in its front matter.


Customization

How Mono Works

Understanding how Hugo themes work can help us customize Mono more effectively later on. The theme follows Hugo’s standard structure where templates in layouts/ determine how content is rendered.

Layouts

Layouts are HTML templates that define the structure of our pages. Mono includes the following key layouts in layouts/_default/:

  • baseof.html: The base template which wraps all pages. It includes our head.html, header.html, and footer.html partials, and defines a “main” block that other templates fill in,
  • single.html: Template for individual pages (like the About page for example). For blog posts specifically, Hugo uses layouts/posts/single.html instead, which adds features like Table of Contents and post navigation,
  • list.html: Template for section index pages. Hugo automatically uses this when visiting a directory URL like /posts/, listing all content in that section.
  • projects.html: Custom layout for the projects showcase. In Mono, and to make its customization simple, we use Hugo’s data templates feature to read project entries from data/projects.toml and render them as cards,
  • search.html: Layout for the search page. It uses Fuse.js for fast client-side fuzzy searching across all posts.

Partials

Partials are reusable template fragments, included by baseof.html:

  • head.html: Meta tags, CSS bundles, and scripts,
  • header.html: Navigation menu and theme toggle,
  • footer.html: Social links and copyright.

To customize these, simply edit the files in layouts/partials/.

Shortcodes

Shortcodes are reusable content snippets we can use in our markdown files. Mono provides several custom shortcodes in layouts/shortcodes/:

  • note, info, warning: Styled callout boxes for highlighting content,
  • details: Collapsible content sections,
  • aside: Side notes and annotations,
  • media: Embed YouTube, Vimeo, or other media,
  • full-width-img: Images that break out of the content width.

Example usage:

markdown
{{< note >}}
This is an important note.
{{< /note >}}

For examples of available shortcodes and other features, see the Mono Theme Features blog post.

The navigation menu is configured in hugo.toml. Each menu item has a name, URL, and weight:

toml
[menu]
  [[menu.main]]
    name = "Home"
    url = "/"
    weight = 1
  [[menu.main]]
    name = "Blog"
    url = "/posts/"
    weight = 2
  [[menu.main]]
    name = "Projects"
    url = "/projects/"
    weight = 3

The weight determines the order of menu items, lower values appear first. For each menu URL, we should point to existing content:

  • / points to content/_index.md (the homepage),
  • /posts/ points to the content/posts/ section (Hugo automatically lists all posts using the list.html layout),
  • /projects/ points to content/projects.md (a standalone page), and so on.

To add a new menu item, create the corresponding markdown file in content/ first, then add the menu entry in hugo.toml.

Custom CSS and JS

To add custom styles or scripts without modifying the theme files:

  • CSS: Create assets/css/custom.css (processed by Hugo)
  • JS: Create static/js/custom.js (copied as-is)

Then add them to the config:

toml
[params]
  customCSS = ["css/custom.css"]
  customJS = ["js/custom.js"]

For example, to change the accent color, add this to custom.css:

css
:root {
  --color-primary: #0088cc;
}

See assets/css/style.css for all available CSS variables.