Anna Documentation
Table of Contents #
- Directory structure
- Description of the directory structure
- Building layouts
- Accessing specific page data
- Frontmatter
- Important - The frontmatter field cannot be omitted
- Frontmatter
- Important - The frontmatter field cannot be omitted
- Body
- Static assets
- Development Workflow
- Troubleshooting
- Site configuration
Directory structure #
Anna requires the following directory structure
site
├── content
│ ├── docs.md
│ ├── index.md*
│ ├── sub0folder
│ │ ├── bench.md
│ │ ├── building-anna
│ │ │ ├── images
│ │ │ │ ├── bench.png
│ │ │ │ └── wizard.gif
│ │ │ ├── index.md
│ │ └── weekly-progress
│ │ ├── week-1.md
│ │ ├── week-2.md
│ │ └── week-3.md
├── layout
│ ├── collection-subpage.html*
│ ├── collections.html*
│ ├── config.json*
│ ├── page.html*
│ ├── partials
│ │ ├── head.html
│ ├── robots.txt*
│ ├── tag-subpage.html*
│ |── tags.html*
│ ├── collection-subpage.html*
│ └── collections.html*
├── public
└── static
├── fonts
│ ├── VictorMono
│ │ └── victormono_italics.ttf
├── scripts
│ └── light.js
├── style.css
├── styles
└── tokyo.css
Files marked * are required and cannot be omitted
Description of the directory structure #
- All of the site data, including the content, configuration and static files, are stores in site/. The rendered/ directory generated by ssg is also stored in
site/. - The markdown content for the site is stored in
content/. It can contain subdirectories along with images as the folder is recursively rendered.- The contents of this dir is rendered to the root of
rendered/
- The contents of this dir is rendered to the root of
- Static assets such as fonts are stored in
static/ - Scripts are stored in the
scripts/dir instatic/ - The layout of the site is configured using html files in
layout/- The
config.jsonfile stores the configuration of the site and includes details such as the baseURL - The
collections.html,collection-subpage.html,tags.htmland other necessary layouts define the structure of the various pages of the site such ascollections.html,collections/[[sub-page]].htmland other SSG generated pages - Additional layouts can be created and set for various pages of the site using the
layoutfrontmatter field - The layout files can be composed of smaller html files which are stored in the
partials/folder
- The
- Contents in
public/are rendered to the root ofrendered/
Building layouts #
- Each layout file can access any data from the entire ssg
- The
tags.htmlpage can access the following data:{{.DeepDataMerge}}{{.PageURL}}{{.TemplateData}}{{.TagNames}}
- The remaining pages can access the following data
{{.DeepDataMerge}}{{.PageURL}}
PageURL #
The {{.PageURL}} is of the form index.html, collections/tech/go.html and so on.
Elements stored in DeepDataMerge #
{{.DeepDataMerge.Collections}}- A map that stores the template data of the collection sub-pages for a particular collection url{{.DeepDataMerge.CollectionsMap}}- A map that stores a slice of templates of all pages for a particular collection url{{.DeepDataMerge.JSONIndex}}- Stores the JSON index generated for a particular site (primarily used for search and graphing of tags){{.DeepDataMerge.LayoutConfig}}- Stores the layout parsed fromconfig.json{{.DeepDataMerge.Templates}}- A map that stores the template data of all the pages of the site for the particular url(the URL is the PageURL for the speicified page){{.DeepDataMerge.Tags}}- A map that stores the template data of the tag sub-pages for a particular tag url{{.DeepDataMerge.TagsMap}}- A map that stores a slice of templates of all pages for a particular tag url
Layout Partials #
Anna supports layout partials, which are reusable HTML template fragments stored in the layout/partials/ directory. Partials allow you to break down complex layouts into smaller, manageable pieces that can be shared across multiple layout files.
How Partials Work #
Partials use Go's template system with the {{define}} and {{template}} directives:
- Defining a Partial: Use
{{define "partialName"}}to define a partial template - Including a Partial: Use
{{template "partialName" .}}to include the partial in a layout
Partials have access to the same data context as the main layout templates, including:
{{.DeepDataMerge}}- All site data{{.PageURL}}- Current page URL{{.TemplateData}}- Page-specific template data
Example Partial Structure #
Here's how the built-in partials are organized:
layout/partials/
├── head.html # HTML head section with meta tags, stylesheets, and scripts
├── header.html # Site header with navigation and branding
├── footer.html # Site footer with copyright and links
└── search.html # Search modal/component
Creating and Using Partials #
Example: Defining a partial in head.html
{{define "head"}}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{index .DeepDataMerge.Templates .PageURL | .Frontmatter.Title}}</title>
<!-- Additional head content -->
</head>
{{end}}
Example: Using partials in a layout file (page.html)
{{define "page"}}
{{template "head" .}}
<body>
{{template "header" .}}
<main>
<!-- Page content -->
{{index .DeepDataMerge.Templates .PageURL | .Body}}
</main>
{{template "footer" .}}
</body>
</html>
{{end}}
Best Practices for Partials #
- Naming Convention: Use descriptive names that match the partial's purpose (e.g., "head", "header", "footer")
- Data Access: Always check if page data exists before accessing it:
{{$PageData := index .DeepDataMerge.Templates .PageURL}} {{if eq $PageData nil}} {{$PageData = .TemplateData}} {{end}} - Modularity: Keep partials focused on a single responsibility
- Reusability: Design partials to work across different layout types
- Conditional Logic: Use template conditionals to show/hide content based on page data
Common Partial Patterns #
- Head Section: Meta tags, stylesheets, scripts, and SEO data
- Navigation: Site-wide navigation menus and breadcrumbs
- Footers: Copyright notices, social links, and site information
- Components: Reusable UI components like search forms, modals, or sidebars
Partials make your layouts more maintainable by separating concerns and reducing code duplication across different page types.
Accessing specific page data #
The URL for the current page can be accessed using {{.PageURL}}
To access the data for a particular page, use Go templating syntax:
{{$PageData := index .DeepDataMerge.Templates .PageURL}}
{{$PageData.CompleteURL}}
To access the page data for collections.html, tags.html and their respective partials, set
{{$PageData := .TemplateData}}
All of the following page data fields can be accessed in the above manner:
{{$PageData.CompleteURL}}: Returns the complete url of the given page{{$PageData.Date}}: Returns the last modified date of the current file{{$PageData.Frontmatter.[Tagname]}}: Returns the value of the frontmatter tag- Example:
{{$PageData.Frontmatter.Title}}: Returns the value of the title tag
- Example:
{{$PageData.Body}}: Returns the markdown body rendered to HTML
Custom template functions #
Anna has the following pre-defined template function:
-
func strSliceContains(items []string, search string) boolThis function returns true if asearchstring is present in a slice of strings (items), else returns falseUsage:
{{if strSliceContains $PageData.Frontmatter.Collections "posts"}}
Frontmatter #
Metadata such as the title of the page can be added as frontmatter to the markdown files in the YAML format. This metadata can be accessed from the layout files with the appropriate syntax.
Currently, the following frontmatter tags are supported:
Important - The frontmatter field cannot be omitted #
anna will throw an error if a page does not contain frontmatter or if the title frontmatter field is missing
authors: Stores (multiple) author/s of a particular pagecollections: Stores the collections the particular page belongs todate: The date of the current pagedescription: Stores the description of the current post previewed in html layoutsdraft: When set to 'true', the current page is not rendered unless the '-d' flag is usedlayout: Stores the layout file (*.html) to be used to render the current pagepreviewimage: Stores the preview image of the current pagescripts: Stores the page-level scripts to be addedtags: Stores the tags of the particular page
Frontmatter #
Metadata such as the title of the page can be added as frontmatter to the markdown files in the YAML format. This metadata can be accessed from the layout files with the appropriate syntax.
Currently, the following frontmatter tags are supported:
Important - The frontmatter field cannot be omitted #
anna will throw an error if a page does not contain frontmatter or if the title frontmatter field is missing
authors: Stores (multiple) author/s of a particular pagecollections: Stores the collections the particular page belongs todate: The date of the current pagedescription: Stores the description of the current post previewed in html layoutsdraft: When set to 'true', the current page is not rendered unless the '-d' flag is usedlayout: Stores the layout file (*.html) to be used to render the current pagepreviewimage: Stores the preview image of the current pagescripts: Stores the page-level scripts to be addedtags: Stores the tags of the particular pagetitle: The title of the current pagetoc: When set to 'true', a table of contents is rendered for the current page
Frontmatter Examples #
Basic Page Frontmatter #
---
title: "My Blog Post"
date: 2024-01-15
description: "A brief description of this post"
authors: ["John Doe"]
tags: ["golang", "ssg"]
collections: ["posts"]
toc: true
---
Draft Post #
---
title: "Work in Progress"
date: 2024-01-15
draft: true
---
Custom Layout #
---
title: "Special Page"
layout: "custom-layout.html"
scripts: ["special.js"]
---
Collection Page #
---
title: "Building Anna: A Static Site Generator"
date: 2024-01-01
collections: ["posts", "anna"]
tags: ["golang", "static-site", "development"]
previewimage: "/static/images/anna-preview.png"
---
Accessing Frontmatter in Templates #
Frontmatter values are accessible in layout templates through the Frontmatter field:
<!-- Access title -->
<h1>{{ $PageData.Frontmatter.Title }}</h1>
<!-- Access authors -->
{{range $PageData.Frontmatter.Authors}}
<p>Author: {{.}}</p>
{{end}}
<!-- Access tags -->
{{range $PageData.Frontmatter.Tags}}
<a href="/tags/{{.}}.html">{{.}}</a>
{{end}}
<!-- Conditional rendering -->
{{if $PageData.Frontmatter.Toc}}
<div class="toc">{{ $PageData.Body | toc }}</div>
{{end}}
Body #
Anna uses Goldmark to render markdown files, which is CommonMark compliant
A few useful goldmark extensions have been included:
- anchor
- adds support for anchors next to all headers
- figure
- parse markdown paragraphs that start with an image into HTML
<figure>elements
- parse markdown paragraphs that start with an image into HTML
- toc
- adds support for rendering a table-of-contents
Static assets #
Images #
Images can be added to the content/ dir, public/ dir or the static/ dir.
The contents of the static/ and public/ directories and the non-markdown files present in the content/ dir are copied to rendered/
The images can be referenced in the markdown files via their relative or absolute paths
CSS #
CSS can be added in the following ways:
-
In an external file in the
static/directory and linked to the layout filesExample:
<link rel="stylesheet" href="/static/style.css"> -
Placed inside
<style></style>tags in the<head></head>of the layout files -
Inline with the html elements
Development Workflow #
Building the Site #
Anna provides several ways to build and serve your site:
Basic Build #
# Build the site once
anna
# Build with drafts included
anna -d
# Build and serve locally
anna -s
# Build and serve with drafts
anna -s -d
Using Make #
The included Makefile provides convenient build targets:
# Build the site
make build
# Clean build artifacts
make clean
# Run tests
make tests
# Serve the site with live reload
make serve
Live Reloading #
When running with the -s flag, Anna includes live reload functionality:
- The server watches for file changes in
content/,layout/, andstatic/ - When changes are detected, the site is automatically rebuilt
- Connected browsers automatically refresh to show changes
- Live reload scripts are injected into pages during development
File Watching #
Anna monitors the following directories for changes:
content/- Markdown files and assetslayout/- HTML templates and partialsstatic/- Static assets (CSS, JS, images)site/config.json- Site configuration
Changes to any files in these directories trigger a rebuild.
Troubleshooting #
Common Issues #
Missing Frontmatter Error #
Error: frontmatter is required for all pages
Solution: Ensure every .md file starts with YAML frontmatter including at least a title field:
---
title: "Page Title"
---
Template Execution Errors #
template: layout/page.html:1: function "undefined" not defined
Solution: Check for typos in template function names. Available functions:
strSliceContainsfor checking if a string exists in a slice
Missing Layout Files #
Error: open site/layout/page.html: no such file or directory
Ensure required layout files exist in
site/layout/:
page.html(required)collections.html(required)tags.html(required)config.json(required)
Broken Links in Rendered Output #
Use absolute paths starting with
/for internal links:
<a href="/collections/posts.html">Posts</a>
Images Not Loading #
Place images in:
site/static/for global assetssite/content/alongside markdown filessite/public/for root-level files
Reference with absolute paths: /static/images/logo.png
Collection Pages Not Generating #
Ensure pages have
collectionsfrontmatter:
collections: ["posts", "articles"]
Tag Pages Not Working #
Ensure pages have
tagsfrontmatter:
tags: ["golang", "tutorial"]
Site configuration #
The config.json file stores additional information regarding the layout of the site It contains the following fields:
navbar: Stores the links to be added to the navbar (same name as the markdown files)baseURL: Stores the base URL of the sitesiteTitle: Stores the name of the sitesiteScripts: Stores the javascript files to be included with every pageauthor: Stores the author of the sitecopyright: Stores the copyright information of the sitethemeURL: Stores the link to the common stylesheetcustomFields: Stores a set of arbitrary key-value pairs as required by the usercollectionLayouts: Stores the names of the layouts to be used for a particular collection subpage
Sample config.json #
{
"navbar": [
{
"Index": "index.html"
},
{
"Quick Start": "quick-start.html"
},
{
"Docs": "docs.html"
},
{
"Dev Guide": "developer-guide.html"
},
{
"Tags": "tags.html"
},
{
"Collections": "collections.html"
},
{
"Posts": "collections/posts.html"
},
{
"Anna Blog": "posts/building-anna/index.html"
}
],
"CustomFields": [
{
"Github": "https://github.com/anna-ssg/anna"
}
],
# make sure no trailing slash com/
"baseURL": "https://example.com",
# Replace this with the actual canonical-url of your site
# baseURL tells search-engines (SEO), web-crawlers (robots.txt) so people can discover your site on the internet.
# It's also embeded in your sitemap / atom feed and can be used to change metadata about your site.
"siteTitle": "anna",
"siteScripts": null,
"author": "Anna",
"themeURL": "/static/style.css",
"copyright": "This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.",
# Here, `all-posts` is the name of the layout template to be used to render the `collection/posts.html` subpage
"collectionLayouts": {
"collections/posts.html": "all-posts"
}
}
