gohan themes are built with Go's standard html/template package.
日本語版: templates.ja.md
All .html files inside the theme directory (themes/default/templates/ by default) are loaded automatically.
| File | URL pattern | Description |
|---|---|---|
index.html |
/ |
Site top page (full article list) |
article.html |
/posts/<slug>/ |
Individual article page |
tag.html |
/tags/<name>/ |
Tag article list page |
category.html |
/categories/<name>/ |
Category article list page |
archive.html |
/archive/<year>/ |
Year-based archive page |
All template files are optional. If a template does not exist, that page is simply not generated (no error is raised).
Every template receives a value of type model.Site.
type Site struct {
Config Config // Settings from config.yaml
Articles []*ProcessedArticle // Articles for the current page (filtered)
Tags []Taxonomy // All tags across the site
Categories []Taxonomy // All categories across the site
ArchiveYears []int // Unique years that have articles, sorted newest-first
Pagination *Pagination // Paging metadata; nil when pagination is disabled or for non-listing pages
CurrentLocale string // Locale for the current page (e.g. "en", "ja"); empty when i18n is not configured
RelatedArticles []*ProcessedArticle // Articles sharing at least one category with the current article (article pages only; nil on all other pages)
CurrentTaxonomy *Taxonomy // The tag or category being listed; nil on all other pages
CurrentArchivePath string // Set on archive pages; locale-aware path, e.g. "/archives/2024/01/" (EN) or "/ja/archives/2024/01/" (JA); empty on all other pages
CurrentArchiveIsMonth bool // true on month archive pages (e.g. /archives/2024/01/); false on year archive pages (e.g. /archives/2024/)
}type Pagination struct {
CurrentPage int
TotalPages int
PerPage int
TotalItems int
PrevURL string // empty string if no previous page
NextURL string // empty string if no next page
BaseURL string // URL path prefix used to construct PrevURL/NextURL
}See docs/features/pagination.md for the full pagination guide.
type Config struct {
Site SiteConfig
Theme ThemeConfig
Build BuildConfig
}
type SiteConfig struct {
Title string // .Config.Site.Title
Description string // .Config.Site.Description
BaseURL string // .Config.Site.BaseURL
Language string // .Config.Site.Language
}
type ThemeConfig struct {
Name string
Dir string
Params map[string]string // .Config.Theme.Params
}type ProcessedArticle struct {
FrontMatter FrontMatter // Parsed YAML Front Matter
HTMLContent template.HTML // Rendered HTML
Summary string // First ~200 characters
OutputPath string // Output file path
FilePath string // Source Markdown file path
LastModified time.Time // Last modified time
ContentPath string // Content-dir-relative Markdown path (e.g. "posts/hello.md"); used for GitHub edit links
Locale string // Locale code (e.g. "en", "ja"); empty when i18n is not configured
URL string // Canonical URL path (e.g. "/posts/hello/" or "/ja/posts/hello/")
Translations []LocaleRef // Translated variants; populated by BuildTranslationMap; empty when not i18n
PluginData map[string]interface{} // Per-article data injected by enabled plugins; access via {{index .PluginData "plugin_name"}}
}
// LocaleRef holds the locale code and canonical URL for a translated variant.
type LocaleRef struct {
Locale string
URL string
}
type FrontMatter struct {
Title string
Date time.Time
Draft bool
Tags []string
Categories []string
Description string
Author string
Slug string
Template string
TranslationKey string // Links this article to its translations in other locales
Extra map[string]interface{} // Any front-matter keys not listed above; used by plugins
}type Taxonomy struct {
Name string // Tag or category name
Description string // Optional description
URL string // Locale-aware canonical URL path set at render time, e.g. "/ja/tags/go/"; empty outside paginatedJobs
}| Template | .Articles contains |
Extra fields available |
|---|---|---|
index.html |
All articles on the site | .Pagination, .ArchiveYears, .Tags, .Categories |
article.html |
The single article being rendered | .RelatedArticles, .CurrentLocale |
tag.html |
Articles that have this tag | .Pagination, .CurrentTaxonomy (.CurrentTaxonomy.URL set) |
category.html |
Articles that belong to this category | .Pagination, .CurrentTaxonomy (.CurrentTaxonomy.URL set) |
archive.html |
Articles published in this year/month | .CurrentLocale, .CurrentArchivePath |
Note on
article.html: inside a{{range .Articles}}loop, use$to access root-level fields — e.g.$.RelatedArticles,$.CurrentLocale,$.Config.
| Function | Example | Description |
|---|---|---|
formatDate |
{{formatDate "2006-01-02" .FrontMatter.Date}} |
Format a time.Time value |
tagURL |
{{tagURL .CurrentLocale "go"}} → /tags/go/ (EN) or /ja/tags/go/ (JA) |
Generate a locale-aware tag page URL |
categoryURL |
{{categoryURL .CurrentLocale "tech"}} → /categories/tech/ (EN) |
Generate a locale-aware category page URL |
markdownify |
{{markdownify "**bold**"}} |
Convert a Markdown string to HTML |
formatDate uses Go's reference time layout:
"2006-01-02"→2024-01-15"January 2, 2006"→January 15, 2024
<!DOCTYPE html>
<html lang="{{.Config.Site.Language}}">
<head>
<meta charset="UTF-8">
<meta name="description" content="{{.Config.Site.Description}}">
<title>{{.Config.Site.Title}}</title>
<link rel="stylesheet" href="/assets/style.css">
<link rel="alternate" type="application/atom+xml" title="{{.Config.Site.Title}}" href="/atom.xml">
</head>
<body>
<header>
<h1><a href="/">{{.Config.Site.Title}}</a></h1>
<p>{{.Config.Site.Description}}</p>
</header>
<main>
<ul>
{{range .Articles}}
<li>
<time>{{formatDate "2006-01-02" .FrontMatter.Date}}</time>
<a href="/posts/{{.FrontMatter.Slug}}/">{{.FrontMatter.Title}}</a>
{{if .FrontMatter.Tags}}
<span>
{{range .FrontMatter.Tags}}<a href="{{tagURL $.CurrentLocale .}}">#{{.}}</a> {{end}}
</span>
{{end}}
</li>
{{end}}
</ul>
</main>
<footer>
<a href="/sitemap.xml">Sitemap</a> · <a href="/atom.xml">Feed</a>
{{if .Config.Theme.Params.footer_text}}
<p>{{.Config.Theme.Params.footer_text}}</p>
{{end}}
</footer>
</body>
</html><!DOCTYPE html>
<html lang="{{.Config.Site.Language}}">
<head>
<meta charset="UTF-8">
{{with (index .Articles 0)}}
<meta name="description" content="{{.FrontMatter.Description}}">
<title>{{.FrontMatter.Title}} — {{$.Config.Site.Title}}</title>
{{end}}
<link rel="stylesheet" href="/assets/style.css">
</head>
<body>
<header><nav><a href="/">← {{.Config.Site.Title}}</a></nav></header>
<main>
{{with (index .Articles 0)}}
<article>
<h1>{{.FrontMatter.Title}}</h1>
<time>{{formatDate "January 2, 2006" .FrontMatter.Date}}</time>
{{if .FrontMatter.Author}}<span> · {{.FrontMatter.Author}}</span>{{end}}
{{if .FrontMatter.Tags}}
<ul class="tags">
{{range .FrontMatter.Tags}}<li><a href="{{tagURL $.CurrentLocale .}}">{{.}}</a></li>{{end}}
</ul>
{{end}}
<div class="content">{{.HTMLContent}}</div>
</article>
{{end}}
</main>
</body>
</html><!DOCTYPE html>
<html lang="{{.Config.Site.Language}}">
<head>
<meta charset="UTF-8">
<title>Tag: {{(index .Articles 0).FrontMatter.Tags}} — {{.Config.Site.Title}}</title>
<link rel="stylesheet" href="/assets/style.css">
</head>
<body>
<header><nav><a href="/">← {{.Config.Site.Title}}</a></nav></header>
<main>
<h2>Articles ({{len .Articles}})</h2>
<ul>
{{range .Articles}}
<li>
<time>{{formatDate "2006-01-02" .FrontMatter.Date}}</time>
<a href="/posts/{{.FrontMatter.Slug}}/">{{.FrontMatter.Title}}</a>
</li>
{{end}}
</ul>
</main>
</body>
</html>Write a fenced code block with the mermaid language identifier:
```mermaid
graph TD
A[Write article] --> B[gohan build]
B --> C[HTML in public/]
C --> D[Deploy]
```
gohan automatically injects the Mermaid runtime script when it detects a mermaid block.
Fenced code blocks are highlighted automatically using chroma. Styles are applied via inline CSS — no external stylesheet is required.
Create reusable partial templates using {{define}} and {{template}}:
themes/default/templates/
├── index.html
├── article.html
└── _partials/
├── header.html ← defines "header"
└── footer.html ← defines "footer"
<!-- _partials/header.html -->
{{define "header"}}
<header>
<h1><a href="/">{{.Config.Site.Title}}</a></h1>
</header>
{{end}}<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
{{template "header" .}}
<main>...</main>
</body>
</html>