Accent CMS 0.27: your own taxonomies, Atom and JSON feeds, more data in templates

Accent CMS 0.27.0 is out. Five changes can need action when you upgrade, so they come first.

Before you upgrade

Tag URLs use slugs. A tag page moves from /tags/javascript runtime to /tags/javascript-runtime. Static builds and accent serve redirect the old URLs. If your theme builds tag links by hand, such as /tags/{{ tag.name }}, use {{ tag_url(tag) }} instead. The function adds the slug, the deployment prefix and the language prefix.

pages lists only the current language. On a site with site.languages, the pages list that a menu loops over now holds the pages of the page’s own language. To link the other languages, add a language switcher over page.translations. The starter theme shows how.

A broken component manifest fails accent build. Before, the build dropped the component with a warning and passed. Use --lax to get the old behavior.

The build manifest moved out of the output. accent build wrote .build-manifest.json into the output directory, so a deploy published it, with the URL and status of every page, drafts included. It now lives in .accent/build/ next to config.yaml, and the first build after the upgrade moves it for you. If your deploy never deletes remote files, delete /.build-manifest.json from your host. In CI, cache .accent/build/ together with the output to keep incremental builds.

YAML and dates are read more strictly.

  • A hex or octal number with an uppercase prefix, such as 0X1F, is now a string. 0x1F is still the number 31.
  • A control character in a YAML comment is an error.
  • A publish_date that is neither a date nor an ISO 8601 timestamp keeps the page a draft. Before, the page was published. Timestamps such as 2026-12-01T09:00:00Z now work.

Run accent validate after the upgrade. It reports each of these.

Your own taxonomies

Tags are no longer the only way to group pages. List more taxonomies in config.yaml:

taxonomies:
  - name: categories
    singular: category
    path: /themen      # default: /categories

Each taxonomy gets what tags have: an index page, one page per term with a slug URL, pages per language, and taxonomy.categories in every template. A page opts in with categories: praxis or categories: [praxis]. A theme can add categories.html.jinja and category.html.jinja; without them, Accent uses the tag templates. Link a term with term_url("categories", term).

More feeds

Accent now writes Atom and JSON Feed as well as RSS, and it can write several feeds at once:

feed:
  outputs:
    - { format: atom, path: /feed.xml }
    - { format: json, path: /feed.json }
  include: ["/posts/"]   # only posts, while they stay in the sitemap
  • feed.include and feed.exclude limit a feed to one section of the site.
  • A page whose content: block sets feed: true gets a feed of its collection at <its url>/feed.xml.
  • A taxonomy with feed: true gets one feed per term, such as /themen/<slug>/feed.xml.
  • feed_outputs() lists the feeds, so a theme can write one <link rel="alternate"> for each. The default theme does.
  • accent serve answers every feed at the same URL as the build. Outside dev mode, it caches the feed bodies.

A config with only feed.path writes the same RSS feed as before.

More data in templates

site.extra holds your own site-wide values:

site:
  extra:
    newsbar:
      enabled: true
      message: "No free slots at the moment."

A template reads them as site.extra.newsbar.message. site.extra is always there, so {% if site.extra.x %} is safe.

Your frontmatter in listings. Every entry in all_pages, pages, page.children and the other listings has a custom map with your own frontmatter keys. A collection’s filter also takes a custom: map, so custom: { category: praxis } lists only those pages.

Components see the page. A component template now gets site, page, pages, all_pages, taxonomy, theme, dev and accent, like a page template. A component can list the latest posts of a tag without an attribute for each value.

Dates in the page’s language. The date filter writes month and weekday names in the page’s language, so a German page shows März. date("long") and date("short") are presets for common formats, and locale="fr" picks another language.

Content and builds

  • A static/ directory. Files under ./static are served and copied to the site root, for files such as favicon.ico, a verification file or humans.txt. Incremental builds track them.
  • {% include-file %} shows a file of your site as a highlighted code block. It reads the file on every render, so the code on the page always matches the file. lines and region show part of a file.
  • Marks in code. A code block that opts in with {% process=true %} can wrap parts of its code in the mark component, for example to highlight one part of a command.
  • Files in mounts. A directory mount now publishes the non-markdown files next to its pages, as the content directory does.
  • Links to files. A relative link to a file next to the page, such as [plan](benchmark.yml), is rewritten to the file’s URL. The same link works on GitHub and on the site.
  • Netlify _redirects. With build.header_artifacts: ["netlify"], the build also writes _redirects from your redirects and page moves. Cloudflare Pages reads the same file.
  • Component manifests can declare body: none and placement: block | inline | both.

Security fixes

  • Remote content, such as a remote mount or accent serve <url>, can no longer pull a file of your site into a page with include-file or partial.
  • include-file, partial and diagram src= refuse the site’s config file, so they cannot publish a license key or credentials.
  • Theme assets no longer serve or copy dotfiles such as .env, and no longer follow a link out of the theme.
  • With the config file inside the content directory, another spelling of its name (/Config.yaml on macOS and Windows) or a link to it no longer serves it.
  • The theme asset route and the admin static route no longer read files outside their directories.
  • The build manifest no longer ends up in the deployed site (see above).

Fixes for multi-language and versioned sites

  • Tag pages exist in each language, under the language prefix.
  • A translated home page has one URL and lists its pages.
  • nav_tree(), menus and accent query tree follow the page’s language.
  • On versioned docs, accent query sees inherited pages, a .md link on an inherited page reaches its target, and accent validate reports a finding on an inherited page once.

Other fixes

  • A new release reaches returning visitors within an hour. CSS, JavaScript and font files whose URL has no content hash are no longer cached for a year.
  • /readyz answers 503 while a theme template does not compile.
  • DocFind search finds the body words of German pages, and Simple Search keeps words with non-ASCII letters whole.
  • A build without --clean follows frontmatter edits and renders again the pages that show a changed page.
  • Headings keep their { .class #id } attributes, and a raw HTML block stays where it was written.
  • A diagram’s alt text reaches screen readers.

Under the hood

The YAML parser moves to noyalib 0.0.57, and the other dependencies are updated. cargo audit reports no known vulnerabilities.

The GitHub release has the full list of changes.

Install or upgrade:

curl -fsSL https://accentcms.dev/install.sh | sh

Binaries for all platforms, checksums, and signatures are on the GitHub release page.