Accent as a static site generator

Most of what you read about Accent CMS talks about serving – the live server, hot reload, the admin UI. But the same binary is also a static site generator, and for a lot of sites that is all you need. This post is an introduction to that side of Accent: what it does, why it exists, and how to go from a directory of Markdown to a deployable site with a single command.

What “static” means here

A static site generator takes your content – Markdown files, some templates, a few images – and renders it ahead of time into plain HTML, CSS, and assets. There is no application running when a visitor arrives; their browser just downloads files. That is why static sites are fast, cheap to host, and hard to break: there is no server to crash, no database to migrate, and no runtime to patch.

Accent produces exactly this kind of output. You run:

accent build --output ./public

and Accent walks your content directory, renders every page through your theme, processes your images, generates a sitemap and RSS feed, builds a client-side search index, and writes the whole thing to ./public. Upload that folder to any static host – object storage, a CDN, GitHub Pages, Netlify, your own nginx – and you are live.

One binary, no toolchain

The thing that sets Accent apart from most generators is that it is a single binary written in Rust. There is no Node install, no Ruby, no Python virtualenv, no plugin ecosystem to npm install before your first build. You download one executable and you have the whole system: the Markdown renderer, the Jinja template engine, image processing, syntax highlighting, diagram rendering, and search are all compiled in.

That means a build is reproducible and self-contained. The binary you run locally is the binary your CI runs, and it has no hidden dependencies that drift between machines.

The same content, served or built

Accent’s build and serve modes render through the same code path, so what you preview locally with accent serve is what accent build writes to disk. You develop against a live server with hot reload:

accent serve --config config.yaml

Save a Markdown file or edit a template, and the browser refreshes itself. When the site looks right, you build it. There is no separate “production renderer” to surprise you – serve-vs-build parity is a first-class concern in Accent, tested explicitly.

What you get in the output

A build is more than raw HTML. From the same content, accent build emits:

  • Rendered pages with your Jinja theme applied, breadcrumbs and navigation resolved from the content hierarchy.
  • Processed, resized images in modern formats, generated once at build time and cached.
  • A sitemap and RSS feed so search engines and readers can follow along.
  • A client-side search index – search works on a purely static host, with no search server behind it.
  • Diagrams and syntax-highlighted code, rendered at build time so no JavaScript is required to display them.

This very website is built with accent build and deployed as static files.

Deploying under a path prefix

If your site does not live at the root of a domain – a GitHub Pages project site at user.github.io/repo, say, or a reverse-proxy mount – Accent can rewrite every internal URL to sit under a prefix:

accent build --base-path /repo --output ./public

Page links, theme assets, media, pagination, search, and feeds all move under the prefix together, so nothing 404s once deployed.

Static sites fail silently: a mistyped link becomes a 404 that no one sees until a visitor does. Accent can fail the build instead:

accent build --strict-links --output ./public

With --strict-links, Accent resolves every internal link in the build output and stops the build if one is broken. A bad link becomes a red CI check, not a shipped 404.

Getting the binary

There is one download per platform, and it contains the full feature set. Your license key decides which tier is unlocked at runtime, and neither building a site nor running the development server needs a key at all – so everything in this post works with an unlicensed binary.

On Linux and macOS the install script picks the right archive for your platform, checks the SHA-256 checksum (and the GPG signature when gpg is present), and installs accent into ~/.local/bin – no root, no package manager:

curl -fsSL https://raw.githubusercontent.com/AccentCMS/accent/main/install.sh | sh

On Windows, the PowerShell equivalent:

irm https://raw.githubusercontent.com/AccentCMS/accent/main/install.ps1 | iex

To pin a version rather than take the latest, pass it through to the script. Note the -s --: a flag placed after the URL is eaten by curl, which does not understand it, and never reaches sh.

curl -fsSL https://raw.githubusercontent.com/AccentCMS/accent/main/install.sh | sh -s -- --version v0.23.1

Downloading from the releases page

If you would rather not pipe a script into a shell – a reasonable instinct, and often the policy on a build server – take the archive yourself from the releases page:

PlatformArchive
Linux x86_64accent-<version>-x86_64-unknown-linux-gnu.tar.gz
Linux ARM64accent-<version>-aarch64-unknown-linux-gnu.tar.gz
macOS Intelaccent-<version>-x86_64-apple-darwin.tar.gz
macOS Apple Siliconaccent-<version>-aarch64-apple-darwin.tar.gz
Windows x86_64accent-<version>-x86_64-pc-windows-msvc.zip
Windows ARM64accent-<version>-aarch64-pc-windows-msvc.zip

Unpack it and put accent somewhere on your PATH:

VERSION=v0.23.1
BASE=https://github.com/AccentCMS/accent/releases/download/$VERSION
curl -fsSLO $BASE/accent-$VERSION-x86_64-unknown-linux-gnu.tar.gz
tar xzf accent-$VERSION-x86_64-unknown-linux-gnu.tar.gz
install -m755 accent ~/.local/bin/accent
accent --version

Every release also ships checksums-<version>.txt and a detached signature checksums-<version>.txt.asc made with the Accent CMS release key, so you can verify the archive before you run it:

curl -fsSLO https://raw.githubusercontent.com/AccentCMS/accent/main/release-signing-key.asc
gpg --import release-signing-key.asc

curl -fsSLO $BASE/checksums-$VERSION.txt
curl -fsSLO $BASE/checksums-$VERSION.txt.asc
gpg --verify checksums-$VERSION.txt.asc checksums-$VERSION.txt
sha256sum -c --ignore-missing checksums-$VERSION.txt

Starting from scratch with accent init

You do not need to assemble a project by hand. accent init scaffolds a complete, buildable site – content, theme, and config.yaml – so your first accent build produces real output:

# Scaffold a starter site into ./my-site
accent init my-site
cd my-site

# Preview it live, with hot reload
accent serve

# Build the static output
accent build --output ./public

accent init ships more than one starting point. List what is available, then pick one:

# See the bundled templates
accent init --list

# Scaffold the full documentation site instead of the simple starter
accent init my-docs --docs

The simple starter is a good base for a blog or a small marketing site; the --docs template gives you the full documentation layout with navigation and search wired up. Either way you get a directory that builds on the first try – no missing theme, no empty config.yaml to fill in before anything renders.

Deploying to GitHub Pages

GitHub Pages is the shortest path from a repository to a live static site, and Accent is built to deploy there cleanly – including the awkward case that trips up most generators.

GitHub Pages serves in one of two shapes, and the difference decides how you build:

  • User or organization sites (username.github.io) serve at the domain root. Nothing special is needed – build as you would for any domain.
  • Project sites (username.github.io/repo) serve under a /repo sub-path. Every page link, theme asset, media file, and search fetch has to carry that prefix, or the deployed site 404s on its own assets.

Accent handles the project-site case for you. The path component of --base-url sets the site’s base path, so every internal URL Accent emits is prefixed to match – no post-build rewriting step:

accent build --output ./public --base-url https://username.github.io/repo

Before you push, preview the prefixed build exactly as it will be served. accent serve-static reads the resolved base path from the build, so no extra flags are needed:

accent serve-static --dir ./public
# Visit http://127.0.0.1:4403/repo

To automate the deploy, commit a GitHub Actions workflow that installs Accent, builds, and publishes the output:

# .github/workflows/pages.yml
name: Deploy to GitHub Pages

on:
  push:
    branches: [main]

permissions:
  contents: read
  pages: write
  id-token: write

# Let a running deploy finish rather than cancelling it half-published.
concurrency:
  group: pages
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7

      - name: Install Accent CMS
        run: |
          curl -fsSL https://raw.githubusercontent.com/AccentCMS/accent/main/install.sh | sh -s -- --version v0.23.1
          echo "$HOME/.local/bin" >> "$GITHUB_PATH"

      - name: Build site
        run: accent build --output ./public --base-url https://username.github.io/repo --strict-links

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v5
        with:
          path: ./public

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v5

Pinning the Accent version with --version is deliberate: a CI job that silently follows the latest release is a build that can change under you without a commit. Bump it when you choose to.

Then enable Pages in the repository under Settings > Pages and select GitHub Actions as the source. On the next push to main, the workflow builds the site and publishes it.

Two Accent touches make this pipeline safe. The --strict-links flag above fails the build if any internal link is broken, so a bad link stops the deploy instead of shipping a 404. And whenever a base path is in effect – as it is for a project site – the build finishes with a conformance check over its own output: if any emitted page, the search index, or llms.txt still carries an unprefixed root-absolute URL, the build fails and names the offending files. You find out at build time, not when a visitor hits a broken asset on the sub-path. (Both guards are no-ops for a root deployment, where there is no prefix to get wrong.)

One limitation worth knowing: GitHub Pages cannot set custom response headers, so the security headers Accent’s server would send (CSP, frame options) are absent on Pages. For a content site that is usually fine; if you need those headers, host somewhere that supports them.

Where to go next

The whole loop – scaffold, preview, build, deploy – is free, with no license key. You pay to serve in production, not to build, so you can take a project from an empty directory all the way to a live GitHub Pages site without one:

accent init my-site
cd my-site
accent build --output ./public --base-url https://username.github.io/repo --strict-links

We will go deeper on templates, content modeling, and other deployment targets in future posts. For now, the documentation covers the full command set, and the news page tracks every release.