A minimalist Hugo theme focused on content, inspired by stephango.com. Features dark/light mode, breadcrumb navigation, taxonomies (topics & tags), advanced code blocks (Dracula, line highlighting, file names, copy button), responsive post listings, newsletter demo dialog, and built-in SEO (Open Graph, Twitter Cards, JSON-LD structured data, sitemap, robots.txt).
- Hugo extended v0.146.0 or higher (uses new template system
_partials,_markup).
git submodule add https://github.com/thnhan1/nhanoki.git themes/nhanoki
git add .gitmodules themes/nhanoki
git commit -m "Add nhanoki theme as submodule"Declare in your site's config:
theme = 'nhanoki'Run dev server:
hugo server -DBelow is a complete hugo.toml with all available options:
baseURL = 'https://example.com/'
locale = 'en-US'
title = 'Your Name'
theme = 'nhanoki'
# ── Sitemap ──────────────────────────────────────────
[sitemap]
changefreq = 'weekly'
priority = 0.5
filename = 'sitemap.xml'
# ── Author & SEO params ──────────────────────────────
[params]
author = "Your Name"
description = "A short bio or tagline for your site"
# Social profiles — used in JSON-LD structured data (Person.sameAs)
[params.social]
twitter = "yourhandle" # without @
github = "yourgithub"
# linkedin = "yourlinkedin"
# ── Taxonomies ───────────────────────────────────────
[taxonomies]
topic = "topics"
tag = "tags"
# ── Navigation menu (right side of header) ───────────
[menu]
[[menu.main]]
name = "About"
url = "/about"
weight = 10
[[menu.main]]
name = "Now"
url = "/now"
weight = 20
# ── Markup / Code blocks ─────────────────────────────
[markup]
[markup.goldmark.renderer]
unsafe = true # allow raw HTML in markdown
[markup.highlight]
style = "dracula" # Chroma style (dracula, github, monokai, etc.)
noClasses = true
lineNos = true
lineNumbersInTable = false
tabWidth = 2
# ── Output formats ───────────────────────────────────
[outputs]
home = ["HTML", "RSS"]
page = ["HTML"]
section = ["HTML", "RSS"]
taxonomy = ["HTML", "RSS"]
term = ["HTML"]
[outputFormats]
[outputFormats.RSS]
mediatype = "application/rss+xml"
baseName = "feed"titlein config displays on the left side of the header (breadcrumb home link).- Items in
[menu.main]display on the right side of the header, next to the dark/light mode toggle.
The social links under [params.social] are used for two purposes:
- JSON-LD
sameAs— tells Google your social profiles, strengthening your personal brand identity in search results. - Twitter card —
twitter:sitemeta tag usesparams.social.twitter.
Posts go in content/posts/. Basic front matter:
---
title: "Post title"
date: 2025-10-20T10:00:00Z
draft: false
topics: ["advice", "tools"]
---
Opening text displays as summary.
<!--more-->
Remaining content...<!--more-->sets the summary displayed on the homepage ("Keep reading →").topicsattaches the post to thetopicstaxonomy (appears as tags below the post).image(optional) — sets the Open Graph / Twitter Card image for social sharing.
Create content/about.md, content/now.md etc. with title front matter. Pages to appear in the menu should be declared in [menu.main] in config (don't use menu: main in front matter to avoid duplication).
Posts can be tagged with topics and tags:
topics: ["design", "tools"]
tags: ["blue", "green"]topicsare featured on the homepage and shown below each post.tagsexist but are not surfaced on the homepage — they have their own/tags/index.
Always use leaf bundles for posts with images: create a folder containing index.md and place images alongside it.
content/posts/my-post/
├── index.md
└── photo.jpg
In index.md:

⚠️ DO NOT placeindex.mddirectly incontent/posts/. This turns the entirepostsdirectory into a leaf bundle and hides other posts. Use_index.mdfor section landing pages.
The image render hook (_markup/render-image.html) resolves relative paths to page resources and adds loading="lazy".
Use fence syntax with attributes in curly braces:
```java {title="A.java" hl_lines="2 5"}
public class A {
private static String name = "Nhan";
public static void main(String[] args) {
System.out.println(name);
}
}
```title— shows a file name bar above the code block (optional).hl_lines— highlights specific lines; supports ranges like"2 5-7"(optional).- Line numbers and color theme come from
[markup.highlight]in config. - Copy button — appears in the top-right corner (requires JavaScript).
The header contains:
- Breadcrumb (left side) — shows
Site Nameon all pages. When inside a sub-section (e.g., a post under/posts/), it showsSite Name / Section Title. Standalone pages like/aboutand/nowonly show the site name (no redundant slash). - Navigation links (right side) — from
[menu.main]. - Theme toggle — dark/light mode switch.
On mobile (<600px), the breadcrumb wraps to its own line.
A reusable partial for rendering a list of posts with date + title. Used by both home.html and section.html.
{{ partial "post-listing.html" .Pages.ByDate.Reverse }}
- Wide screens (≥600px): date shows as "January 2, 2006" with a fixed width column.
- Narrow screens (<600px): date switches to "2006-01-02" compact format.
- Uses flex layout with a
gapbetween date and title.
The homepage (home.html) is structured as:
- Latest — the most recent post with title, date, reading time, summary, and "Keep reading →" link.
- Topics — an alphabetical list of all topics (from
site.Taxonomies.topics). - Writing — a full reverse-chronological list of all posts (reuses
post-listing.html).
A demo newsletter component placed between <main> and <footer> in baseof.html.
- Clicking Subscribe opens a modal dialog with a thank-you message.
- No actual email is sent — it's purely demonstrative.
- The dialog can be dismissed by clicking OK or clicking outside the dialog box.
- Edit the message in
_partials/newsletter.html.
The <head> section automatically includes:
| Feature | Details |
|---|---|
| Meta description | Uses page's .Description, .Summary, or falls back to site.Params.description (truncated to 160 chars) |
| Canonical URL | <link rel="canonical"> with .Permalink |
| Open Graph | og:title, og:type, og:url, og:site_name, og:description, og:author, og:image |
| Twitter Card | twitter:card, twitter:title, twitter:description, twitter:site |
| JSON-LD Person | Schema.org Person with name, url, description, sameAs (from [params.social]) |
| JSON-LD BlogPosting | On post pages, an additional BlogPosting or Article schema with headline, dates, author |
| Sitemap | Auto-generated /sitemap.xml when [sitemap] is configured in hugo.toml |
| robots.txt | Include in your site's static/robots.txt |
Create static/robots.txt in your site root:
User-agent: *
Allow: /
Sitemap: https://yourdomain.com/sitemap.xml
- Toggle button in the header, saves preference to
localStorage. - Default: follows
prefers-color-scheme. - Icons:
static/sun.svgandstatic/moon.svg— replace these to change icons. - CSS: uses
.theme-dark/.theme-lightclasses on<body>.
| Breakpoint | Behavior |
|---|---|
| ≥860px | Full desktop layout with --wrap-normal: 37em content width |
| <860px | Content width switches to 88vw; heading weight increases |
| <600px | Mobile layout: breadcrumb wraps, body padding reduced, post dates switch to compact format, nav wraps |
themes/nhanoki/
├── assets/
│ ├── css/
│ │ ├── main.css # All styles (normalize, Flexoki palette, layout, responsive)
│ │ ├── font.css # Open Sans font face
│ │ └── components/ # Standalone component CSS (not imported by default)
│ └── js/
│ └── main.js # Theme toggle, code copy button, newsletter dialog
├── layouts/
│ ├── baseof.html # Frame: <head>, <header>, <main>, <newsletter>, <footer>
│ ├── home.html # Homepage: latest post, topics, writing archive
│ ├── page.html # Single post / static page
│ ├── section.html # Post list by section (/posts)
│ ├── taxonomy.html # List of all terms in a taxonomy (/topics, /tags)
│ ├── term.html # Posts for a single term (/topics/design)
│ ├── 404.html # 404 page
│ ├── _partials/
│ │ ├── head.html # <head>: meta tags, SEO, CSS, JS
│ │ ├── header.html # Nav: breadcrumb + menu + theme toggle
│ │ ├── breadcrumb.html # Smart breadcrumb (skips home self-link)
│ │ ├── menu.html # Hugo menu renderer
│ │ ├── post-listing.html # Reusable date+title list (responsive dates)
│ │ ├── newsletter.html # Demo subscribe form + dialog
│ │ ├── footer.html # Footer content
│ │ ├── terms.html # Terms list helper
│ │ └── head/
│ │ ├── css.html # CSS build pipeline (Hugo Pipes)
│ │ └── js.html # JS build pipeline (Hugo Pipes)
│ └── _markup/
│ ├── render-codeblock.html # Code block with title, hl_lines, copy button
│ └── render-image.html # Image with lazy loading, page resource resolution
└── static/
├── favicon.ico
├── homepage.png
├── sun.svg # Light mode icon
└── moon.svg # Dark mode icon
| Goal | How |
|---|---|
| Change colors | Edit CSS variables (--flexoki-*, --color-*) in assets/css/main.css |
| Content width | Adjust --wrap-normal (default 37em) and --wrap-wide (default 54em) |
| Font size | Edit body font-size and heading sizes in main.css |
| Code theme | Change style in [markup.highlight] (see Chroma styles) |
| Social links | Set [params.social] in hugo.toml |
| Newsletter message | Edit _partials/newsletter.html |
| Footer | Edit _partials/footer.html |
| Favicon | Replace static/favicon.ico |
This theme uses the Flexoki color palette (MIT) by Steph Ango. Key design tokens:
--wrap-normal: default content width (37em)--wrap-wide: wide content width (54em)--font-content: system font stack with Open Sans--font-mono: monospace stack for code--line-height: 1.5--color-tx-normal,--color-tx-muted,--color-tx-faint: text color scale- All colors adapt via
.theme-light/.theme-darkclasses
Theme uses CSS based on design by Steph Ango and color palette Flexoki (MIT).
