Theme Portability

A well-written Accent CMS theme produces identical output whether you run accent serve in development, accent serve --production in production, or accent build for static deployment. This guide covers the guidelines and template features that make themes portable across all three modes.

Theme Structure

Every Accent theme consists of:

  • theme.yaml – theme configuration (name, assets, styling)
  • templates/*.html.jinja – Jinja templates
  • assets/ – CSS, JS, images, fonts

The CMS loads these files at startup and uses them for all page rendering. The rendering pipeline, template functions, and context variables are identical in serve and build modes.

What Stays the Same

Everything a theme developer interacts with works identically across serve and build:

Featureaccent serveaccent build
Jinja template renderingyesyes
Template inheritance (extends, include)yesyes
All template functions (now, get_env, get_hash, etc.)yesyes
| fingerprint filteryes (no-op)yes (active)
theme.yaml configurationyesyes
theme.assets.css / theme.assets.jsyesyes
Page context (page, site, theme, pages, etc.)yesyes
Shortcodesyesyes
Taxonomy pagesyesyes
Paginationyesyes

What Differs Between Modes

A few behaviors are mode-specific, but they require no changes to your theme:

Hot Reload (Development Only)

With dev.hot_reload: true, templates and assets refresh automatically when you save a file. In production mode and static builds, hot reload is disabled. Your templates don’t need to handle this – it’s automatic.

Asset Fingerprinting

The | fingerprint filter is active only during accent build. In accent serve, it returns the original path unchanged. Templates using | fingerprint work correctly in both modes without conditional logic:

{# Works in both modes: fingerprinted path in build, original path in serve #}
<link rel="stylesheet" href="/{{ ('theme/assets/' ~ css) | fingerprint }}">

Environment Variables

get_env() reads from the process environment in both modes. In build mode, values are baked into the static HTML. In serve mode, values are read per-request but frozen in the page cache for the duration of the cache TTL.

File Hashing

get_hash(path='...') resolves files against the theme’s assets directory. The hash output is identical for the same file content in both serve and build modes.

Writing Portable Themes

Follow these guidelines to ensure your theme works correctly in both serve and build:

Use Relative Asset Paths

Reference assets relative to the theme’s assets directory:

{# Correct: relative to theme assets #}
<link rel="stylesheet" href="/theme/assets/css/style.css">

{# With fingerprinting for cache busting #}
<link rel="stylesheet" href="/{{ ('theme/assets/css/style.css') | fingerprint }}">

Use Template Functions for Dynamic Values

Use template functions to derive values rather than hardcoding environment-specific information:

{# Cache busting via content hash #}
<link rel="stylesheet" href="/theme/assets/css/style.css?v={{ get_hash(path='theme/assets/css/style.css') }}">

{# Environment-specific configuration #}
{% set analytics_id = get_env(name="GA_ID") %}
{% if analytics_id %}
<script async src="https://www.googletagmanager.com/gtag/js?id={{ analytics_id }}"></script>
{% endif %}

Keep theme.yaml Complete

Declare all CSS and JS files in theme.yaml so the CMS knows which assets to load:

name: My Theme
assets:
  css:
    - css/main.css
    - css/components.css
  js:
    - js/app.js

Test with accent build

Run accent build to verify your theme produces correct static output. The build uses the same rendering pipeline as serve mode, so if the build output is correct, your theme works:

accent build --output ./public --clean
accent serve-static --dir ./public
# Verify the site at http://127.0.0.1:4403

Avoid Dev-Only Assumptions

Don’t assume hot reload, debug panels, or SSE reload scripts are available. Use the dev.debug context variable for development-only markup:

{% if dev.debug %}
<div class="debug-panel">
  <pre>{{ debug(page) }}</pre>
</div>
{% endif %}

Edition Availability

Theme portability across serve and build modes requires no license key.