diff --git a/README.md b/README.md
index 9ad13df..d66c2c2 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,38 @@
-
Zona
+zona
-[Zona](https://sr.ht/~ficd/zona) is an opinionated static site generator written
-in Python. From a structured directory of Markdown content, Zona builds a simple
-static website. It's designed to get out of your way and let you focus on
-writing.
+[zona](https://sr.ht/~ficd/zona) is an _opinionated_ static site generator
+written in Python. From a structured directory of Markdown content, zona builds
+a simple static website. It's designed to get out of your way and let you focus
+on writing.
-**Note:** This project is in early development, and there are no versioned
-releases yet. For an example of a website built with Zona, please see
-[ficd.ca](https://ficd.ca)
+**What do I mean by opinionated?** I built zona primarily for myself. I've tried
+making it flexible by exposing many variables as possible to the template
+engine. However, if you're looking for something stable, complete, and fully
+configurable, zona may not be for you. If you want a minimal Markdown blog and
+are comfortable with modifying `jinja2` templates and CSS, then you're in luck.
+
+**Note:** This project is in early development, there are no versioned releases
+yet, and breaking changes are likely. Versioned releases will be made and zona
+will be published to PyPI once it's stable.
+
+For an example of a website built with zona, please see
+[ficd.ca](https://ficd.ca).
-
- [Features](#features)
+- [Installation](#installation)
+- [Usage](#usage)
+ - [Getting Started](#getting-started)
+ - [Site Layout](#site-layout)
- [Internal Link Resolution](#internal-link-resolution)
- [Syntax Highlighting](#syntax-highlighting)
- [Image Labels](#image-labels)
-- [Installation](#installation)
-- [Usage](#usage)
- - [Site Layout](#site-layout)
- [Frontmatter](#frontmatter)
- - [Configuration](#configuration)
-
+ - [Post List](#post-list)
+- [Configuration](#configuration)
+ - [Sitemap](#sitemap)
+ - [Ignore List](#ignore-list)
+ - [Drafts](#drafts)
## Features
@@ -33,16 +45,78 @@ releases yet. For an example of a website built with Zona, please see
- Easily configurable sitemap header.
- Site footer written in Markdown.
- Smart site layout discovery.
- - Blog posts automatically discovered and rendered accordingly (can be
+ - Blog posts are automatically discovered and rendered accordingly (can be
overridden in frontmatter).
- Extended Markdown renderer:
- Smart internal link resolution.
- Syntax highlighting.
+ - Includes Kakoune syntax and [Ashen] highlighting.
- [Image labels](#image-labels).
+ - Many `python-markdown` extensions enabled, including footnotes, tables,
+ abbreviations, etc.
+
+## Installation
+
+zona is not yet packaged on PyPI. You may use `uv` to install it from this
+repository:
+
+```sh
+uv tool install 'git+https://git.sr.ht/~ficd/zona'
+```
+
+## Usage
+
+_Note: you may provide the `--help` option to any subcommand to see the
+available options and arguments._
+
+### Getting Started
+
+To set up a new website, create a new directory and run `zona init` inside of
+it. This creates the required directory structure and writes the default
+configuration file. The default templates and default stylesheet are also
+written.
+
+To build the website, run `zona build`. The project root is discovered according
+to the location of `config.yml`. By default, the output directory is called
+`public`, and saved inside the root directory.
+
+To start a live preview session, execute `zona serve`. The server will run until
+it's killed by the user, and the website is rebuilt if any source files are
+modified. _Note: if you change `config.yml` or any templates, you will need to
+restart the preview server_.
+
+### Site Layout
+
+The following demonstrates a simple zona project layout:
+
+```
+config.yml
+content/
+templates/
+public/
+```
+
+The **root** of the zona **project** _must_ contain the configuration file,
+`config.yml`, and a directory called `content`. A directory called `templates`
+is optional, and prioritized if it exists. `public` is the built site output —
+it's recommended to add this path to your `.gitignore`.
+
+The `content` directory is the **root of the website**. Think of it as the
+**content root**. For example, suppose your website is hosted at `example.com`.
+`content/blog/index.md` corresponds to `example.com/blog`,
+`content/blog/my-post.md` becomes `example.com/blog/my-post`, etc.
+
+- Internal links are resolved **relative to the `content` directory.**
+- Templates are resolved relative to the `template` directory.
+
+Markdown files inside a certain directory (`content/blog` by default) are
+automatically treated as _blog posts_. This means they are rendered with the
+`page` template, and included in the `post_list`, which can be included in your
+site using the `post_list` template.
### Internal Link Resolution
-When Zona encounters links in Markdown documents, it attempts to resolve them as
+When zona encounters links in Markdown documents, it attempts to resolve them as
internal links. Links beginning with `/` are resolved relative to the content
root; otherwise, they are resolved relative to the Markdown file. If the link
resolves to an existing file that is part of the website, it's replaced with an
@@ -51,11 +125,12 @@ appropriate web-server-friendly link. Otherwise, the link isn't changed.
For example, suppose the file `blog/post1.md` has a link `./post2.md`. The HTML
output will contain the link `/blog/post2` (which corresponds to
`/blog/post2/index.html`). Link resolution is applied to _all_ internal links,
-including those pointing to static resources like images.
+including those pointing to static resources like images. Links are only
+modified if they point to a real file that's not included in the ignore list.
### Syntax Highlighting
-Zona uses [Pygments] to provide syntax highlighting for fenced code blocks. The
+zona uses [Pygments] to provide syntax highlighting for fenced code blocks. The
following Pygments plugins are included:
- [pygments-kakoune](https://git.sr.ht/~ficd/pygments-kakoune)
@@ -65,8 +140,9 @@ following Pygments plugins are included:
- An implementation of the [Ashen](https://git.sr.ht/~ficd/ashen) theme for
Pygments.
-If you want to use any external Pygments styles or lexers, they must be available
-in Zona's Python environment. For example, you can give Zona access to [Catppucin](https://github.com/catppuccin/python):
+If you want to use any external Pygments styles or lexers, they must be
+available in zona's Python environment. For example, you can give zona access to
+[Catppucin](https://github.com/catppuccin/python):
```yaml
# config.yml
@@ -75,16 +151,26 @@ markdown:
theme: catppucin-mocha
```
-Then, run Zona with the following `uv` command:
+Then, run zona with the following `uv` command:
```sh
uvx --with catppucin zona build
```
+Inline syntax highlighting is also provided via a `python-markdown` extension.
+If you prefix inline code with a shebang followed by the language identifier, it
+will be highlighted. For example:
+
+```
+`#!python print(f"I love {foobar}!", end="")`
+will be rendered as
+`print(f"I love {foobar}!", end="")`
+(the #!lang is stripped)
+```
### Image Labels
-A feature unique to Zona is **image labels**. They make it easy to annotate
+A feature unique to zona is **image labels**. They make it easy to annotate
images in your Markdown documents. The alt text Markdown element is rendered as
the label — with support for inline Markdown. Consider this example:
@@ -101,57 +187,131 @@ The above results in the following HTML:
```
The `image-container` class is provided as a convenience for styling. The
-default stylesheet centers the label under the image.
-
-## Installation
-
-Zona is not yet packaged on PyPI. You may use `uv` to install it from this
-repository:
-
-```sh
-uv tool install 'git+https://git.sr.ht/~ficd/zona'
-```
-
-## Usage
-
-_Note: you may provide the `--help` option to any subcommand to see the
-available options and arguments.
-
-To set up a new website, create a new directory and run `zona init` inside of
-it. This creates the required directory structure and writes the default
-configuration file. The default templates and default stylesheet are also
-written.
-
-To build the website, run `zona build`. The project root is discovered according
-to the location of `config.yml`. By default, the output directory is called
-`public`, and saved inside the root directory.
-
-To start a live preview session, execute `zona serve`. The server will run until
-it's killed by the user, and the website is rebuilt if any source files are
-modified.
-
-### Site Layout
-
-The **root** of the **Zona project** _must_ contain the configuration file,
-`config.yml`, and a directory called `content`. A directory called `templates`
-is optional, and prioritized if it exists. `public` is the built site output —
-it's recommended to add this path to your `.gitignore`.
-
-The `content` directory is the root of the website. For example, suppose your
-website is hosted at `example.com`. `content/blog/index.md` corresponds to
-`example.com/blog`, `content/blog/my-post.md` becomes
-`example.com/blog/my-post`, etc. Internal links are resolved **relative to the
-`content` directory.**
-
-Markdown files inside a certain directory (`content/blog` by default) are
-automatically treated as _blog posts_. This means they are rendered with the
-`page.html` template, and included in the `post_list`, which can be included in
-your site using the `post_list.html` template.
+default stylesheet centers the label under the image. Note: _links_ inside image
+captions are not currently supported. I am looking into a solution.
### Frontmatter
-WIP
+YAML frontmatter can be used to configure the metadata of documents. All of them
+are optional. `none` is used when the option is unset. The following options are
+available:
-### Configuration
+| Key | Type & Default | Description |
+| ------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------ |
+| `title` | `str` = title-cased filename. | Title of the page. |
+| `date` | Date string = file modified time. | Displayed on blog posts and used for post_list sorting. |
+| `show_title` | `bool` = `true` | Whether `metadata.title` should be included in the template. |
+| `header` | `bool` = `true` | Whether the header sitemap should be rendered. |
+| `footer` | `bool` = `true` | Whether the footer should be rendered. |
+| `template` | `str \| none` = `none` | Template to use for this page. Relative to `templates/`, `.html` extension optional. |
+| `post` | `bool \| none` = `none` | Whether this page is a **post**. `true`/`false` is _absolute_. Leave it unset for automatic detection. |
+| `draft` | `bool` = `false` | Whether this page is a draft. See [drafts](#drafts) for more. |
-WIP
+**Note**: you can specify the date in any format that can be parsed by
+[`python-dateutil`](https://pypi.org/project/python-dateutil/).
+
+### Post List
+
+Suppose you want `example.com/blog` to be a _post list_ page, and you want
+`example.com/blog/my-post` to be a post. You would first create
+`content/blog/index.md` and add the following frontmatter:
+
+```markdown
+---
+title: Blog
+post: false
+template: post_list
+
+Welcome to my blog! Please find a list of my posts below.
+---
+```
+
+Setting `post: false` is necessary because, by default, all documents inside
+`content/blog` are considered to be posts unless explicitly disabled in the
+frontmatter. We don't want the post list to list _itself_ as a post.
+
+Then, you'd create `content/blog/my-post.md` and populate it:
+
+```markdown
+---
+title: My First Post
+date: July 5, 2025
+---
+```
+
+Because `my-post` is inside the `blog` directory, `post: true` is implied. If
+you wanted to put it somewhere outside `blog`, you would need to set
+`post: true` for it to be included in the post list.
+
+## Configuration
+
+Zona is configured in YAML format. The configuration file is called `config.yml`
+and it **must** be located in the root of the project — in the same directory as
+`content` and `templates`.
+
+Your configuration will be merged with the defaults. `zona init` also writes a
+copy of the default configuration to the correct location. If it exists, you'll
+be prompted before overwriting it.
+
+**Note:** Currently, not every configuration value is actually used. Only the
+useful settings are listed here.
+
+Please see the default configuration:
+
+```yaml
+sitemap:
+ Home: /
+ignore:
+ - .marksman.toml
+markdown:
+ image_labels: true
+ tab_length: 2
+ syntax_highlighting:
+ enabled: true
+ theme: ashen
+ wrap: false
+blog:
+ dir: blog
+```
+
+| Name | Description |
+| -------------------------------------- | --------------------------------------------------------------------------------------------- |
+| `sitemap` | Sitemap dictionary. See [Sitemap](#sitemap). |
+| `ignore` | List of paths to ignore. See [Ignore List](#ignore-list). |
+| `markdown.tab_length` | How many spaces should be considered an indentation level. |
+| `markdown.syntax_highlighting.enabled` | Whether code should be highlighted. |
+| `markdown.syntax_highlighting.theme` | [Pygments] style for highlighting. |
+| `markdown.syntax_highlighting.wrap` | Whether the resulting code block should be word wrapped. |
+| `blog.dir` | Name of a directory relative to `content/` whose children are automatically considered posts. |
+
+### Sitemap
+
+You can define a sitemap in the configuration file. This is a list of links that
+will be rendered at the top of every page. The `sitemap` is a dictionary of
+`string` to `string` pairs, where each key is the displayed text of the link,
+and the value if the `href`. Consider this example:
+
+```yaml
+sitemap:
+ Home: /
+ About: /about
+ Blog: /blog
+ Git: https://git.sr.ht/~ficd
+```
+
+### Ignore List
+
+You can set a list of glob patterns in the [configuration](#configuration) that
+should be ignored by zona. This is useful because zona makes a copy of _every_
+file it encounters inside the `content` directory, regardless of its type. The
+paths must be relative to the `content` directory.
+
+### Drafts
+
+zona allows you to begin writing content without including it in the final build
+output. If you set `draft: true` in a page's frontmatter, it will be marked as a
+draft. Drafts are completely excluded from `zona build` and `zona serve` unless
+the `--draft` flag is specified.
+
+[Ashen]: https://sr.ht/~ficd/ashen
+[Pygments]: https://pygments.org/