HERON

Guide 02 · a full walkthrough

Your first site

A full walkthrough: from an empty folder to a built site in two languages, English first and Russian second. Every step has a command you can copy.

You need Docker. Without it, install HERON with pip install git+https://github.com/FMSTM/HERON.git@prod and write heron wherever this page says $H.

In plain words

No Docker yet? Install it first: Docker on macOS, Windows and Linux takes about ten minutes and ends with a check that everything works. Then come back here.

This page is a recipe. You run a few commands one after another, change a few lines of text, and at the end you have a real site on your computer, in two languages, that you can open in the browser.

What you will do, in short: create the folder with new, tell HERON the name and domain in site.yaml, write the home page, add one more page by copying a file, look at the result at localhost:8000, and make the final build. Every step shows the exact command; copy it, press Enter, look at what changed.

The example has two languages. Your site may have one: then skip the steps about the second language. This guide also shows the full terminal dialogue of heron new; the short path on Get started is heron new my-site -y and then the configurator, which asks the same and more.

You need this page the first time you make a site. Skip it if you only edit texts on a site somebody already made: go to Pages and content.

Before you start

All commands below run HERON from its stable image through one short variable:

H="docker run --rm -it -v $PWD:/site -w /site --user $(id -u):$(id -g) ghcr.io/fmstm/heron:prod"
  • -v $PWD:/site lets HERON see only the current folder. So the path to the site in commands is relative to it: my-site, or . if you are already inside the site folder. A path like ~/SOURCE/… does not exist inside the container, and HERON will tell you so;
  • -it lets new ask its questions. Without it, new silently takes the default values;
  • --user keeps the new files yours. Without it they would belong to root and you could not edit them.

1. Create the site

$H new my-site          # a new folder next to you
$H new .                # or the current one, for example a git repository you have just cloned

The folder can already be under git: .git, .gitignore, README and other service files do not get in the way and are not overwritten. Any other file stops new with a list of what is in the way.

new asks a few questions; Enter accepts the value in brackets:

Notes and messages language / Язык пояснений и сообщений
  › (en, ru) [en] en
Site name — how people know the site: in the header, in the page title, in the markup
  › [my-site] Workshop
Domain — no https:// and no slashes, for example example.com. Not sure — press Enter for a placeholder
  › [workshop.example] workshop.com
Site languages — codes separated by commas: en or en, ru. Each language is a folder content/CODE/
  › [en] en, ru
Main language — served from the root (/about/), the others get a prefix (/ru/about/)
  › [en] en
  • The notes language is the language of the comments in every file HERON puts into the site and of the messages on screen. It is saved in site.yaml (build.notes). Set it in advance with --notes ru or -e HERON_NOTES=ru;
  • Without questions use flags: $H new my-site --notes en --name Workshop --domain workshop.com --lang en,ru -y. This is how AI agents and CI work.

At the end new prints what it created and a «What next» block with ready commands. What appeared:

my-site/
├── site.yaml                  settings with a comment on every key
├── heron-readme.md            a note on what this folder is; every folder has one
├── content/en/index.md        the home page with the full SEO block
├── content/en/404.md          the «not found» page
├── content/ru/…               the same for every language you named
├── media/  data/  static/  plugins/
└── theme/                     the starter theme: theme.yaml, base.html, templates, modules, styles

The site already builds, without a single warning. You can check right away: $H build my-site.

2. Describe the site in site.yaml

site.yaml is the single source of truth about the site. Your answers are already there; open it and add the rest: the name in the second language, the title suffix, the menu:

site:
  domain: example.com
  name: Workshop
  name_ru: Мастерская         # the name on Russian pages
  theme: main
  default_lang: en            # served from the root: /about/
  languages: [en, ru]         # the second language lives under /ru/
seo:
  title_suffix: " — Workshop"
  title_suffix_ru: " — Мастерская"
nav:
  main: ["/", about]          # page keys: home and about, each language gets its own address

Every key with an explanation: site.yaml in the specification and the sample docs/agents/templates/site.yaml.

3. Changed the languages? Catch the folder up

$H init my-site

init reads site.yaml and adds what is missing. Add de to languages, and you get content/de/index.md, content/de/404.md and theme/i18n/de.yaml. Existing files are not touched (to overwrite, add --force). If you created the site with the right languages from the start, you can skip this step.

init in a folder without site.yaml, for example with ready content from somewhere else, asks the same questions as new and suggests the languages from the folders already in content/.

4. Write the home page

Open content/en/index.md. At the top is the SEO block: change title (for search results), h1 (for whoever is already on the page) and description (the snippet). Do not delete the comments: they are a hint for whoever edits the file after you.

Below come the intro and the sections:

We repair roofs since 2010.

We come to measure on the day you call.

## What we do {#about}

- **Roof repair.** Patching and re-covering.
- **Gutters.** Installation and cleaning.

The Russian home page is content/ru/index.md: the same anchor {#about}, its own text. The anchor is the name of the section for the theme, so it is the same in every language; the heading is translated.

Which sections the home page shows and how is up to the theme. The starter theme prints the intro and every section in order; in your own theme every section gets its own place and module: see Theme and design.

5. Add a page

Two ways, the same result:

cp my-site/content/en/index.md my-site/content/en/about.md   # then change the values
$H page page about my-site                                   # type page, file content/en/about.md

heron page TYPE NAME puts in a file with the full SEO block and a draft of every section the theme expects for that type. The page appears in the sitemap and in llms.txt by itself, and in the menu because its key about is listed in nav.main.

A translation is a file with the same name in content/ru/ ($H page page about my-site --lang ru). The translation can have its own address: slug: o-nas in its SEO block:

content/en/about.md   →  /about/
content/ru/about.md   →  /ru/o-nas/      slug: o-nas

Both versions get hreflang to each other, and the about menu item on Russian pages leads to /ru/o-nas/ by itself.

6. Build and look

$H build my-site && docker run --rm -p 8080:8080 -v "$PWD/my-site/dist:/usr/share/nginx/html:ro" nginxinc/nginx-unprivileged:1.27-alpine

Open localhost:8080. After the build HERON prints a report: pages per language, warnings grouped by kind (images, links between pages, links, translations…) and a summary of sections left empty. Errors stop the build and name the file and the line.

$H check my-site gives the same report without building.

7. The production build

$H build my-site --env prod --strict

--env prod opens the site to search engines and turns on the counters from analytics. --strict turns warnings into errors: a site with a broken link or a picture without alt never goes live. The finished dist/ folder goes to any hosting; how to build an nginx image with the site is in Build and deploy.

Source

This page mirrors docs/guide/02-first-site.md from the prod branch. The repository is updated first, so the file there is always the latest version.

Next guide: Pages and content.