Anna Documentation

Published on 2024-04-28

Anna is a high-performance static site generator built in Go, designed for speed and simplicity. It features parallel rendering, live reload, responsive templates with support for collections, tags, and advanced Markdown features.

Command-Line Interface

Anna provides a powerful CLI for building and managing your site. Here are the available commands and flags:

Basic Usage

# Render the site
./anna

# Serve with live reload
./anna -s

# Show version
./anna -v

# Show help
./anna -h

Flags

  • -p, --path <path> - Specify the site directory (default: site/)
  • -s, --serve - Serve the rendered site and watch for file updates with live reload
  • -d, --draft - Include draft posts (pages with draft: true in frontmatter)
  • -a, --addr <addr> - Specify the address to serve on (default: localhost:8000)
  • -v, --version - Print the current version and commit hash
  • --prof - Enable profiling to measure rendering performance
  • -h, --help - Show help message

Interactive Setup

If you don't have a site directory, Anna can help you set one up:

# Just run anna without a config and it will prompt you
./anna

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/
  • Static assets such as fonts are stored in static/
  • Scripts are stored in the scripts/ dir in static/
  • The layout of the site is configured using html files in layout/
    • The config.json file stores the configuration of the site and includes details such as the baseURL
    • The collections.html, collection-subpage.html, tags.html and other necessary layouts define the structure of the various pages of the site such as collections.html, collections/[[sub-page]].html and other SSG generated pages
    • Additional layouts can be created and set for various pages of the site using the layout frontmatter field
    • The layout files can be composed of smaller html files which are stored in the partials/ folder
  • Contents in public/ are rendered to the root of rendered/

Building layouts

  • Each layout file can access any data from the entire ssg
  • The tags.html page 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 from config.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 specified 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:

  1. Defining a Partial: Use {{define "partialName"}} to define a partial template
  2. 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

  1. Naming Convention: Use descriptive names that match the partial's purpose (e.g., "head", "header", "footer")

  2. Data Access: Always check if page data exists before accessing it:

    {{$PageData := index .DeepDataMerge.Templates .PageURL}}
    {{if eq $PageData nil}}
    {{$PageData = .TemplateData}}
    {{end}}
    
  3. Modularity: Keep partials focused on a single responsibility

  4. Reusability: Design partials to work across different layout types

  5. 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
  • {{$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) bool This function returns true if a search string is present in a slice of strings (items), else returns false

    Usage: {{if strSliceContains $PageData.Frontmatter.Collections "posts"}}


Frontmatter

All markdown content files must start with YAML frontmatter. Anna requires at minimum a title field, or it will throw an error.

Frontmatter is written as YAML between --- delimiters at the top of your markdown file:

---
title: "Your Page Title"
date: 2024-01-15
tags: ["golang", "web"]
collections: ["posts"]
---

Frontmatter Fields Reference

Required:

  • title - The page title (used in HTML <title> and navigation)

Optional Fields:

  • date - Publication date in YYYY-MM-DD format
  • description - Page description (meta tags, collection previews)
  • authors - Array of author names: ["Alice", "Bob"]
  • tags - Array of topic tags: ["golang", "web"]
  • collections - Array of collection names: ["posts", "featured"]
  • draft - Set to true to exclude from builds (unless -d flag used)
  • layout - Custom layout template name (without .html)
  • previewimage - URL to preview image for social sharing
  • scripts - Array of page-specific JS files to include
  • toc - Set to true to auto-generate table of contents
  • customFields - Array of custom key-value pairs for template access

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"
scripts: 
  - "/static/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"
---

Complex Example with Multiple Authors and Custom Fields

---
title: "Collaborative Technical Article"
date: 2024-01-20
authors: ["Alice Smith", "Bob Johnson"]
description: "Deep dive into static site generation"
tags: ["golang", "performance", "web"]
collections: ["tutorials", "featured"]
toc: true
previewimage: "/static/preview.png"
scripts:
  - "/static/scripts/interactive.js"
customFields:
  - difficulty: "advanced"
    readTime: "20 min"
    topic: "architecture"
---

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 & Markdown

Anna uses Goldmark to render markdown files, which is fully CommonMark compliant. This means your markdown works across different platforms and tools.

Markdown Extensions

Anna includes several powerful Goldmark extensions for enhanced functionality:

Anchors

The anchor extension automatically adds linkable anchor elements next to all headings:

## Getting Started

<!-- Renders with automatic anchor: https://example.com/page.html#getting-started -->

Figures

The figure extension converts images in paragraphs to HTML <figure> elements for semantic markup and better styling:

![A descriptive alt text](images/diagram.png)

<!-- Renders as: <figure><img src="..."/></figure> -->

Table of Contents

The toc extension enables automatic table of contents generation. Enable with frontmatter:

---
title: "Long Article"
toc: true
---

This generates a clickable table of contents from all <h2> and <h3> headings in the page.

Markdown Best Practices

  1. Use semantic headings - Start with H1 (one per page), then H2, H3, etc.
  2. Keep paragraphs short - Easier to read and understand
  3. Use code blocks with syntax highlighting - Specify language: ```go for Go, ```html for HTML, etc.
  4. Include alt text for images - Important for accessibility and SEO
  5. Use emphasis wisely - Prefer italics over underscores for consistency

Static assets

Static files like stylesheets, scripts, images, and fonts are served without modification. Anna supports multiple asset directories:

Asset Directories

  • static/ - Global static assets (recommended)
    • Copied to rendered/static/
    • Use for CSS, JS, fonts, reusable images
    • Reference as /static/...
  • content/ - Content-local assets
    • Images alongside markdown files
    • Automatically copied to rendered/
    • Use for post-specific images and diagrams
  • public/ - Root-level assets
    • Copied directly to rendered/
    • Use for robots.txt, favicon.ico, .well-known files
    • Reference from root: /file.txt

Images

Reference images in your markdown with absolute paths:

<!-- Global image -->
![Logo](/static/images/logo.png)

<!-- Post-specific image (in content/posts/) -->
![Diagram](images/diagram.png)

<!-- Root-level image -->
![Icon](/favicon.ico)

CSS

Add stylesheets to your layout files:

<!-- External stylesheet in static/ -->
<link rel="stylesheet" href="/static/styles/main.css">

<!-- Inline styles -->
<style>
  body { font-family: sans-serif; }
</style>

<!-- Inline on elements -->
<div style="color: red;">Error message</div>

Set the global theme in config.json:

{
  "themeURL": "/static/styles/theme.css"
}

JavaScript

Include scripts in layouts or pages:

<!-- Global scripts in layout files -->
<script src="/static/scripts/analytics.js"></script>

<!-- Page-specific scripts via frontmatter -->

Frontmatter:

scripts:
  - "/static/scripts/interactive.js"
  - "/static/scripts/widgets.js"

Fonts

Store font files in static/fonts/:

<style>
  @font-face {
    font-family: 'CustomFont';
    src: url('/static/fonts/custom-font.ttf');
  }
</style>

Asset Organization Best Practices

site/static/
├── styles/
│   ├── main.css
│   ├── theme.css
│   └── components/
│       ├── button.css
│       └── card.css
├── scripts/
│   ├── app.js
│   └── utils/
│       ├── helpers.js
│       └── api.js
├── images/
│   ├── logo.png
│   ├── icons/
│   └── backgrounds/
└── fonts/
    ├── opensans.ttf
    └── roboto.ttf

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:

  1. The server watches for file changes in content/, layout/, and static/
  2. When changes are detected, the site is automatically rebuilt
  3. Connected browsers automatically refresh to show changes
  4. Live reload scripts are injected into pages during development

File Watching

Anna monitors the following directories for changes:

  • content/ - Markdown files and assets
  • layout/ - HTML templates and partials
  • static/ - Static assets (CSS, JS, images)
  • site/config.json - Site configuration

Changes to any files in these directories trigger a rebuild.


Advanced Features

Custom Frontmatter Fields

Beyond the built-in frontmatter fields, you can add custom fields to support your specific needs:

---
title: "Advanced Topics"
customFields:
  - category: "Tutorial"
    difficulty: "advanced"
    readTime: "15 min"
  - prerequisites:
      - "golang-basics"
      - "web-development"
---

Access custom fields in templates:

{{range $PageData.Frontmatter.CustomFields}}
  {{range $key, $value := .}}
    <span>{{$key}}: {{$value}}</span>
  {{end}}
{{end}}

Multiple Authors

Track multiple authors for collaborative content:

---
title: "Collaborative Post"
authors: ["Alice", "Bob", "Charlie"]
---

Display in templates:

<div class="authors">
  By: {{range $PageData.Frontmatter.Authors}}{{.}}{{end}}
</div>

Page-Level Scripts

Add scripts that run only on specific pages:

---
title: "Interactive Demo"
scripts:
  - "/static/scripts/interactive.js"
  - "/static/scripts/visualization.js"
---

These scripts are injected into the page in your layout:

{{range $PageData.Frontmatter.JSFiles}}
  <script src="{{.}}"></script>
{{end}}

Nested Collections

Group content hierarchically:

---
title: "Getting Started with Golang"
collections: ["tutorials", "golang-101", "beginner-friendly"]
---

This creates:

  • collections/tutorials.html
  • collections/golang-101.html
  • collections/beginner-friendly.html

Live Reload Script Injection

During development (when using -s), Anna automatically injects a live reload script. This script:

  1. Watches for server-side changes
  2. Automatically reloads the browser
  3. Is removed in production builds

JSON Index Structure

The generated index.json contains searchable metadata:

{
  "index.html": {
    "title": "Home",
    "url": "/index.html",
    "tags": ["home"],
    "description": "Welcome to my site",
    "authors": ["Admin"],
    "date": 1234567890
  },
  "posts/golang-tutorial.html": {
    "title": "Learning Golang",
    "url": "/posts/golang-tutorial.html",
    "tags": ["golang", "tutorial"],
    "description": "A beginner's guide to Golang",
    "authors": ["Expert"],
    "date": 1234567890
  }
}

You can use this in client-side search implementations:

// Fetch the index
fetch('/index.json')
  .then(r => r.json())
  .then(index => {
    // Implement search using this index
  });

Profiling and Performance Analysis

Run Anna with profiling to identify performance bottlenecks:

anna --prof

This generates profiling data that shows:

  • Rendering time per page
  • Template processing time
  • Markdown parsing time
  • File I/O time

Collections & Tags

Anna supports two powerful organizational features for grouping content: Collections and Tags.

Collections

Collections allow you to organize related content (e.g., blog posts, documentation, tutorials) into separate sections of your site.

Creating Collections

Add collections frontmatter to your pages:

---
title: "My Blog Post"
collections: ["posts", "featured"]
---

Automatic Collection Pages

Anna automatically generates:

  • collections.html - Lists all collection categories
  • collections/[collection-name].html - Dedicated page for each collection
  • collections/[collection-name]/[page-name].html - Individual collection sub-pages

Configuring Collection Layouts

Specify custom layouts for collection pages in config.json:

{
  "collectionLayouts": {
    "collections/posts.html": "all-posts",
    "collections/tutorials.html": "tutorials-layout"
  }
}

Tags

Tags provide fine-grained categorization of content and enable topic-based navigation.

Adding Tags

Add tags frontmatter to your pages:

---
title: "Getting Started with Go"
tags: ["golang", "beginner", "tutorial"]
---

Automatic Tag Pages

Anna automatically generates:

  • tags.html - Cloud view of all tags with frequency
  • tags/[tag-name].html - Pages for each individual tag
  • Filtered content on each tag page

Using Tags in Templates

<!-- Link to a tag page -->
{{range $PageData.Frontmatter.Tags}}
  <a href="/tags/{{.}}.html">{{.}}</a>
{{end}}

<!-- Check if a page has a specific tag -->
{{if strSliceContains $PageData.Frontmatter.Tags "featured"}}
  <span class="featured-badge">Featured</span>
{{end}}

Site Generation

Automatic File Generation

When you run Anna, it automatically generates several files in the rendered/ directory:

Sitemap

rendered/sitemap.xml - XML sitemap for search engines containing all pages, their modification dates, and change frequency.

RSS/Atom Feed

rendered/feed.xml - Atom feed with recent pages for RSS readers (sorted by date, latest first)

JSON Index

rendered/index.json - Searchable index of all pages with metadata:

  • Page titles
  • URLs
  • Tags
  • Description
  • Preview images
  • Authors

This is useful for building search functionality on your site.

Robots.txt

rendered/robots.txt - Web crawler directives (copied from layout/robots.txt)


Site configuration

The config.json file stores configuration for your site's layout and behavior. It's located in site/layout/config.json and is required.

Configuration Fields

  • navbar - Array of navigation links (format: [{"Label": "path/to/page.html"}])
  • baseURL - Base URL of your site (used for sitemaps, feeds, SEO; no trailing slash)
  • siteTitle - Title of your website
  • siteScripts - Global JavaScript files to include on all pages
  • author - Default author name
  • copyright - Copyright notice for your site
  • themeURL - URL to your main stylesheet (e.g., /static/style.css)
  • socials - Social media links (format: {"twitter": "https://twitter.com/...", ...})
  • collectionLayouts - Map collection URLs to custom layout templates
  • customFields - Additional arbitrary key-value pairs for your layouts

Complete Example

{
  "navbar": [
    {"Home": "index.html"},
    {"Blog": "collections/posts.html"},
    {"Tags": "tags.html"},
    {"About": "about.html"}
  ],
  "baseURL": "https://example.com",
  "siteTitle": "My Awesome Site",
  "siteScripts": [
    "/static/scripts/analytics.js",
    "/static/scripts/search.js"
  ],
  "author": "Jane Doe",
  "copyright": "Copyright 2024. All rights reserved.",
  "themeURL": "/static/styles/main.css",
  "socials": {
    "github": "https://github.com/yourname",
    "twitter": "https://twitter.com/yourname",
    "email": "you@example.com"
  },
  "collectionLayouts": {
    "collections/posts.html": "posts-layout",
    "collections/tutorials.html": "tutorials-layout"
  },
  "customFields": [
    {
      "company": "Acme Corp",
      "supportEmail": "support@example.com"
    }
  ]
}

SEO Configuration

The baseURL field is critical for SEO:

  • Used in sitemap.xml to generate absolute URLs
  • Included in feed.xml for RSS readers
  • Used for canonical URLs in metadata
  • Helps search engines discover and index your site

Example with baseURL: "https://example.com"

  • Sitemap links: https://example.com/posts/index.html
  • Feed links: https://example.com/about.html

Best Practices

Content Organization

  1. Use Collections for major sections - e.g., blog posts, documentation, tutorials
  2. Use Tags for topics - e.g., "golang", "web-development", "performance"
  3. Keep frontmatter consistent - Set common fields across related pages
  4. Use meaningful filenames - e.g., getting-started-with-go.md rather than post1.md

Performance Optimization

  1. Minimize custom frontmatter fields - Only add what you need
  2. Optimize images - Compress images before adding to static/
  3. Defer non-critical scripts - Use defer attribute in layout partials
  4. Leverage live reload - Use -s flag during development for fast iteration

Template Design

  1. Use partials for reusable components - e.g., headers, footers, sidebars

  2. Keep layouts DRY - Don't repeat HTML across multiple templates

  3. Check data existence - Use Go template conditionals:

  4. Use semantic HTML - For better SEO and accessibility

    {{if $PageData.Frontmatter.PreviewImage}}
      <img src="{{$PageData.Frontmatter.PreviewImage}}" />
    {{end}}
    

Asset Management

  1. Static assets in static/ - Stylesheets, JavaScript, fonts
  2. Images in content/ - Keep images with related markdown
  3. Keep directory flat - Avoid deep nesting in static/
  4. Reference with absolute paths - e.g., /static/images/logo.png

SEO Optimization

  1. Set meaningful titles - Use descriptive, keyword-rich titles
  2. Write descriptions - Add description frontmatter for snippets
  3. Add preview images - Use previewimage for social sharing
  4. Use keywords in tags - Make tags searchable and relevant
  5. Structure with headings - Use proper heading hierarchy for accessibility
  6. Enable table of contents - Use toc: true for long-form content