Theming Guide

Learn how to create custom themes for your Accent CMS site.

Styling for Markdown-Based Content

Since Accent CMS renders markdown to HTML, the best CSS frameworks are those that style semantic HTML elements directly. Classless CSS frameworks work exceptionally well because they require no special markup - your markdown automatically looks great.

Key considerations:

  • Semantic HTML elements (<article>, <header>, <nav>, <main>, <footer>) are styled automatically
  • Typography is handled out of the box - headings, paragraphs, lists, blockquotes
  • Code blocks with syntax highlighting work with most frameworks
  • Tables and forms render properly without custom classes
  • Dark mode support via prefers-color-scheme or data attributes

Avoid frameworks that require heavy class annotations (like Tailwind or Bootstrap utilities) since markdown output cannot include arbitrary classes.

Available Themes

accent init installs the simple starter theme (and the default theme powers the documentation site). Starter templates with ready-made themes are available via accent init --list.

ThemeFrameworkBest For
simpleSimple.cssClean, classless styling with dark mode (installed by accent init)
defaultCustom CSSStarting point, minimal styling (powers the docs site)

Beyond the bundled themes, published themes install straight from the ecosystem registry – signature- and checksum-verified:

accent theme search dark       # find a theme by name, description, or tag
accent theme list --remote     # or browse everything the registry publishes
accent theme install agentic   # install one
accent theme outdated          # see which installed themes are behind
accent theme update agentic    # move one to the latest version

Installing does not activate: set theme.name in config.yaml to switch to it.

A theme is yours to edit once installed – which is exactly why accent theme update will not silently overwrite your work. If any installed file was modified, added, or removed since install, the update refuses and lists each difference; re-run with --force to discard the listed changes, or copy them somewhere safe first. See The Hub for the full theme, plugin, and template distribution story, including how retracted versions are handled.

Simple and Pico themes use classless CSS frameworks that style HTML elements directly. Your markdown content renders beautifully without any template modifications:

  • Automatic dark/light mode based on system preference
  • Responsive typography and spacing
  • Styled tables, code blocks, and forms
  • Minimal CSS footprint

Traditional Frameworks

The Cinder theme demonstrates using a class-based framework (Bootstrap). This approach requires more template customization but offers more control over component styling.

Lightweight Grid Frameworks

The Skeleton theme uses a minimal CSS boilerplate (~400 lines) that provides:

  • 12-column responsive grid system
  • Base typography for markdown elements
  • Styled buttons, forms, tables, and code blocks
  • Raleway font for clean typography

How Skeleton maps to markdown:

MarkdownHTML OutputSkeleton Styling
# Heading<h1>4-5rem, weight 300, letter-spacing
**bold**<strong>Inherited
[link](url)<a>Cyan color (#1EAEDB)
`code`<code>Gray background, rounded corners
```code<pre><code>Block display, padding
- list<ul><li>Circle markers, proper spacing
> quote<blockquote>2.5rem margin
`table`

Using the grid in templates:

Skeleton’s grid is available for custom layouts in templates:

<div class="row">
    <div class="eight columns">
        {{ page.content | safe }}
    </div>
    <div class="four columns">
        <aside>Sidebar content</aside>
    </div>
</div>

Grid classes: .one.column through .twelve.columns, plus .one-half, .one-third, .two-thirds.

Theme Structure

A theme consists of templates, assets, and a configuration file:

site/themes/my-theme/
├── theme.yaml           # Theme configuration
├── shortcodes/          # Components: a manifest and a template each
├── templates/           # Jinja2 templates
│   ├── base.html.jinja
│   ├── default.html.jinja
│   └── blog.html.jinja
└── assets/              # Static files
    ├── css/
    │   └── style.css
    ├── js/
    │   └── main.js
    └── images/

Theme Configuration

The theme.yaml file defines your theme:

name: My Theme
description: A custom theme for my site
author: Your Name
version: 1.0.0

assets:
  css:
    - css/style.css
  js:
    - js/main.js

Components and the canonical set

A theme’s components live in shortcodes/, a YAML manifest and a template for each; Declared components covers the format. Every theme implements the canonical components: callout, accordion, button, card, tag, image, breadcrumb, tabs, and tab, with their canonical attribute names, plus styles for the built-in table. Implementing them is what lets a site switch to your theme without its pages breaking.

accent validate checks this, and needs no content to do it:

accent validate --templates

Each canonical component also carries the markup landmarks: data-canonical on its root element and data-part on its parts, as the markup landmarks table lists. The classes and the structure around them stay yours. accent validate does not check the landmarks, so compare your templates against that table; the default theme’s templates carry every one.

A theme whose CSS framework has no tabs starts from the default theme’s assets/js/tabs.js, which switches panels, supports keyboard navigation, and remembers the selection.

A theme that provides no components declares it, and the check does not apply:

# theme.yaml
name: Marketing
components: none

Templates

Accent CMS uses MiniJinja, a Jinja2-compatible template engine.

Base Template

Create templates/base.html.jinja:

<!DOCTYPE html>
<html lang="{{ site.language }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}{{ page.title }} - {{ site.name }}{% endblock %}</title>
    {% for css in theme.assets.css %}
    <link rel="stylesheet" href="/theme/assets/{{ css }}">
    {% endfor %}
</head>
<body>
    <header>
        <nav>
            <a href="/" class="site-title">{{ site.name }}</a>
            <ul>
            {% for p in pages %}
                {% if p.menu.visible %}
                <li><a href="{{ p.url }}">{{ p.title }}</a></li>
                {% endif %}
            {% endfor %}
            </ul>
        </nav>
    </header>

    <main>
        {% block content %}{% endblock %}
    </main>

    <footer>
        <p>&copy; {{ site.name }}</p>
    </footer>

    {% for js in theme.assets.js %}
    <script nonce="{{ nonce }}" src="/theme/assets/{{ js }}"></script>
    {% endfor %}
</body>
</html>

The nonce="{{ nonce }}" attribute lets the script run under the strict script policy. The next section explains it.

Scripts and the content security policy

accent serve sends every page a Content-Security-Policy with script-src 'self' 'nonce-...', where the nonce is a random value that changes on every response. A browser then runs a script only when it meets one of these conditions:

  • It is an external script from the site’s own origin, such as <script src="/theme/assets/app.js">.
  • It carries the response’s nonce, which your template writes with nonce="{{ nonce }}".

Anything else is blocked, including an inline <script> without the nonce, an inline event handler such as onclick="..." or onload="...", and a javascript: URL. A script that reaches a page through content or an escaping bug therefore does not run.

To make a theme work under the policy:

  1. Add nonce="{{ nonce }}" to every <script> element in your templates, inline or external. A script from a CDN that carries the nonce needs no allowlist entry.

    <script nonce="{{ nonce }}">
        document.documentElement.dataset.theme = localStorage.getItem('theme') || 'light';
    </script>
    <script nonce="{{ nonce }}" defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
    
  2. Move inline event handlers into a script. A nonce cannot allow an onclick or onchange attribute.

    <select class="version-select">...</select>
    <script nonce="{{ nonce }}">
        document.currentScript.previousElementSibling.addEventListener('change', function () {
            window.location.href = this.value;
        });
    </script>
    
  3. Check the browser console for Content-Security-Policy errors. To find them without blocking anything, set http_headers.security.script_policy.report_only: true.

{{ nonce }} is available in every template, including partials and shortcode components. A script that a script creates, such as a <script> element the page adds at run time or a library loaded with import(), is allowed when it comes from the site’s own origin or from a nonced module. A library that loads more scripts from another origin, such as the Google tag, needs that origin in script_policy.sources.

accent build writes no nonce, because a built page has no response to stamp one into. It removes the nonce="{{ nonce }}" attribute instead and allows each marked script by source: a 'sha256-...' hash of every marked inline script, and the origin of every marked script from another host. The header artifacts (build.header_artifacts) and accent serve-static send those sources in script-src. The attribute is what tells the build a script belongs to your theme, so a script without it is blocked in static output too.

Each distinct inline script adds one hash to the policy. A script that interpolates a per-page value, such as var id = "{{ page.url }}", produces a different hash on every page. Keep per-page values in data attributes and read them from a script whose text does not change, or move the script into a file under assets/.

For the configuration, including how to turn the policy off, see the configuration reference.

Page Template

Create templates/default.html.jinja:

{% extends "base.html.jinja" %}

{% block content %}
<article>
    <h1>{{ page.title }}</h1>
    {% if page.date %}
    <time>{{ page.date }}</time>
    {% endif %}

    <div class="content">
        {{ page.content | safe }}
    </div>

    {% if page.tags %}
    <div class="tags">
        {% for tag in page.tags %}
        <span class="tag">{{ tag }}</span>
        {% endfor %}
    </div>
    {% endif %}
</article>
{% endblock %}

Available Template Variables

The tables in this section are generated from the context types the renderer serialises, so they list every variable and field a template can read, with its type. accent docs template-context prints the same reference, and MCP clients read it as the accent://templates/context resource. The subsections after the tables cover the variables that need more than a row.

Every template receives the variables below. The tables are generated from the context types, so each one lists every field a template can read. Types are written as a template experiences them: String? is a value that can be none, Vec<T> is a list, Map<String, T> is a mapping iterated in key order, Value is any JSON-shaped value, and URL is a string already joined with site.base_path. Nested types are listed under Types.

site

Site configuration and metadata.

Type: SiteContext

VariableTypeDescription
site.base_pathStringDeployment path prefix for sub-path hosting.
site.built_atString?Timestamp when the site was built or the server started.
site.current_languageStringThe language of the currently rendered page.
site.descriptionStringSite description.
site.languageStringSite language.
site.languagesVec<String>All configured language codes.
site.last_modifiedString?Most recent page modification date across all content.
site.main_siteMainSiteContext?Optional main-site link block from site.main_site in config.
site.nameStringSite name.
site.productionboolWhether the site is being served or built in production mode.
site.urlStringSite URL.

page

Current page data.

Type: PageContext

VariableTypeDescription
page.authorString?Author name from frontmatter.
page.backlinksVec<Backlink>Pages that link to this page via wikilinks.
page.body_schemaBodySchemaContext?Body and taxonomy schema metadata for the resolved document model.
page.breadcrumbsVec<PageMeta>Breadcrumb trail from root to parent.
page.canonical_urlURLThe URL this page is advertised under.
page.childrenVec<PageMeta>Direct children pages, sorted by menu.order.
page.collectionVec<PageMeta>Collection items for listing pages.
page.contentStringRendered HTML content (markdown converted to HTML).
page.customMap<String, Value>Custom frontmatter fields (arbitrary key-value pairs), in key order.
page.dateString?Publication date from frontmatter.
page.descriptionStringPage-specific meta description.
page.directionStringWriting direction for the page’s content: "ltr" or "rtl".
page.has_mathboolWhether the page contains math expressions.
page.islandsVec<IslandRef>Island components referenced in this page’s markdown. Used by templates for conditional script loading.
page.json_ldStringPre-computed JSON-LD structured data HTML.
page.languageStringLanguage code for this page.
page.leadString?Short summary for listings, feeds, and the meta description.
page.localeStringTypographic locale used for smart-quote remapping.
page.markdown_urlURL?URL to view this page’s raw markdown source.
page.mediaVec<MediaAssetContext>Media files in the same directory as this page.
page.meta_tagsVec<MetaTag>Merged, ordered social/SEO <meta> tags (site defaults + page meta:).
page.modelString?Resolved document model name.
page.modified_dateString?File modification date as YYYY-MM-DD string.
page.modulesVec<ModuleContext>Page modules from _-prefixed subdirectories.
page.nav_titleStringShort navigation label for the current page, preferred over title in navigation surfaces (the page’s own breadcrumb crumb, active top-nav item). Resolves to menu.title when set, otherwise title.
page.nextPageMeta?Next page in the sibling sequence.
page.noindexboolWhether the page asked to be kept out of indexes.
page.orderintMenu sort order from menu.order frontmatter.
page.paginationPagination?Pagination metadata for collection pages.
page.parentPageMeta?Parent page metadata.
page.prevPageMeta?Previous page in the sibling sequence.
page.reading_timeintEstimated reading time in minutes.
page.redirectURL?Redirect target URL, flattened from RedirectConfig.
page.relationsMap<String, Vec<PageMeta>>Named relationships to other pages.
page.rewriteURL?Rewrite target URL.
page.sectionsMap<String, String>Named content sections, each rendered to HTML.
page.siblingsVec<PageMeta>Sibling pages (same parent), excluding self.
page.social_imageURL?Absolute URL of the page’s social-card image, if any.
page.statusStringPublishing status as a string.
page.tagsVec<String>Tags from frontmatter.
page.titleStringPage title from frontmatter.
page.tocVec<TocEntry>Table of contents entries extracted from headings.
page.translationsVec<TranslationLink>Available translations of this page.
page.urlURLPage URL path (e.g., /docs/getting-started), joined with the deployment base path.
page.word_countintWord count of the raw markdown content.

theme

Theme configuration.

Type: ThemeContext

VariableTypeDescription
theme.assetsAssetsContextTheme assets.
theme.heading_anchor_styleStringHeading anchor presentation style: one of "copy", "gutter", "hash", "pilcrow".
theme.nameStringTheme name.
theme.stylingStylingContextStyling configuration exposed to templates.

dev

Development settings.

Type: DevContext

VariableTypeDescription
dev.browser_reloadboolWhether browser auto-reload is enabled.
dev.debugboolWhether debug mode is enabled.
dev.hot_reloadboolWhether theme hot reload is enabled.

taxonomy

Taxonomy data for tag navigation.

Type: TaxonomyContext

VariableTypeDescription
taxonomy.current_tagString?The current tag being viewed (on /tags/{tag} pages).
taxonomy.pagesVec<PageMeta>Pages matching the current tag (on /tags/{tag} pages), URLs joined.
taxonomy.tagsVec<TagInfo>All tags with usage counts, sorted alphabetically.

version

Version information for versioned content.

Type: VersionInfo?

VariableTypeDescription
version.allVec<VersionOption>Every version of the root, for switchers.
version.badgeString?Badge of the current version (latest, previous, …), when set.
version.currentStringThe version the page is rendered for.
version.is_fallbackboolWhether the page was served from another version as a fallback.
version.labelStringDisplay label of the current version.
version.rootURLThe versioning root URL, joined with the base path.
version.sourceStringThe version the content actually came from.
version.stateStringRelease state of the current version (released, dev, …).

error

Error context for custom error pages.

Type: ErrorContext?

VariableTypeDescription
error.languageStringDetected language code for this request.
error.messageStringSanitized error message. In dev mode, includes full details. In production, contains only safe-to-display text.
error.pathStringThe request path that triggered the error.
error.statusintHTTP status code (e.g., 404, 500).
error.titleStringHuman-readable error title (e.g., “Page Not Found”).

request

HTTP request context (path and query parameters).

Type: RequestContext?

VariableTypeDescription
request.pathStringURL path (e.g., “/contact”). Not joined: it is the path requested, which already carries any base path.
request.queryMap<String, String>Query parameters as key-value pairs, in key order. Duplicate keys: last value wins.

diagrams

Diagram renderer settings surfaced to templates (diagrams.mermaid.cdn_url and friends).

Type: DiagramsContext

VariableTypeDescription
diagrams.mermaidDiagramsMermaidContextMermaid client-side loader configuration.

accent

Build and version information about the running Accent binary (accent.version, accent.version_string, …).

Type: AccentContext

VariableTypeDescription
accent.build_timeStringUTC build timestamp.
accent.git_hashStringShort git commit hash the binary was built from.
accent.versionStringSemantic version (e.g. 0.26.0-dev).
accent.version_stringStringCombined version and hash (e.g. 0.26.0-dev (abc1234)).

Other variables

Root values that are not structs, read directly by name.

VariableTypeDescription
pagesVec<PageMeta>Menu-visible pages (for navigation), URLs joined.
all_pagesVec<PageMeta>All pages including hidden ones (for listings), URLs joined.
markdown_view_enabledboolWhether Markdown View is enabled.
diagram_passthrough_countintNumber of <pre class="diagram-passthrough"> blocks the dispatcher emitted while rendering this page’s markdown.
_json_ldStringThe page’s JSON-LD block, at the top level for the json_ld() template function. The same string as page.json_ld.

Types

Shapes referenced by the tables above. A field of type PageMeta? is either none or a mapping with the fields listed here.

MainSiteContext

Main-site link data exposed as site.main_site.

FieldTypeDescription
badge_labelString?Back-to-home chip text; the default theme falls back to "Docs" when None.
cta_labelString?Call-to-action button text.
cta_urlString?Call-to-action target URL.
hostStringDisplay host derived from url: scheme, userinfo, port, path, query, and fragment stripped (e.g. https://a:b@x.dev:8443/p?q -> x.dev). Falls back to the raw URL (sans trailing /) when no host can be extracted. Themes use it for compact link text.
labelString?Wordmark text; templates fall back to site.name when None. An explicit empty string is honored (icon-only wordmark).
urlStringMain-site URL the brand wordmark links to.

A page that links to the current page through a wikilink.

FieldTypeDescription
titleStringTitle of the linking page.
urlURLURL of the linking page, joined with the base path.

BodySchemaContext

Read-only schema view exposed to templates.

FieldTypeDescription
bodyBodySchema?Body structure constraints, or None when the model has no body: section.
taxonomyTaxonomySchema?Taxonomy field constraints, or None when the model has no taxonomy: section.

PageMeta

A page as it appears in a listing: navigation, children, breadcrumbs, collections, relations, taxonomy pages.

FieldTypeDescription
authorString?Author, when set.
dateString?Publication date (YYYY-MM-DD), when set.
hierarchy_urlStringThe directory-derived URL the hierarchy is keyed on: root-relative (no base path) and a key, not a link. The same as url less the base path unless a permalink pattern or a frontmatter url: moved the page’s public URL; page.parent, page.children, page.breadcrumbs, and nav_tree() all nest on it.
leadString?Short summary for listings.
modelString?Resolved document model name, when the page has one.
modified_dateString?File modification date (YYYY-MM-DD).
nav_titleStringShort navigation label, menu.title when set, otherwise title.
orderintMenu sort order.
tagsVec<String>Tags.
titleStringPage title.
urlURLPage URL, joined with the deployment base path.

IslandRef

Reference to an island component used in a page.

FieldTypeDescription
componentStringComponent name (maps to JS module in theme or plugin).
js_pathStringJS asset path relative to plugin dir (empty for theme islands).
pluginStringPlugin name (empty for theme islands).
sourceStringSource of the island: "theme" or "plugin".

MediaAssetContext

Template-facing media asset context.

FieldTypeDescription
filenameStringFilename without directory (e.g., hero.jpg).
heightint?Image height in pixels (images only).
media_typeStringType label: "image", "video", "audio", "document", "archive", "other".
metadataMap<String, Value>Plugin-contributed metadata.
mime_typeStringMIME content type (e.g., image/jpeg).
size_bytesintFile size in bytes.
urlURLServing URL (e.g., /content-media/blog/post/hero.jpg), joined with the deployment base path.
widthint?Image width in pixels (images only).

MetaTag

A single resolved social/SEO <meta> tag for the head-meta partial.

FieldTypeDescription
keyStringThe tag key (e.g. og:type, twitter:site, fediverse:creator).
propertybooltrue to emit property=, false to emit name=.
valueStringThe tag value (the content="..." attribute).

ModuleContext

Rendered module available in template context.

FieldTypeDescription
contentStringRendered HTML content.
customMap<String, Value>Custom frontmatter fields, in key order.
nameStringModule name (e.g., "hero" from _hero directory).
titleStringModule title from frontmatter.

Pagination

Pagination state of a collection page.

FieldTypeDescription
current_pageintThe current page number, starting at 1.
items_per_pageintItems per page.
next_urlURL?URL of the next page, joined with the base path; None on the last page.
prev_urlURL?URL of the previous page, joined with the base path; None on the first page.
total_itemsintTotal number of items across all pages.
total_pagesintTotal number of pages.

TocEntry

A single entry in the table of contents.

FieldTypeDescription
childrenVec<TocEntry>Child entries (sub-headings).
idStringGenerated anchor ID (e.g., “getting-started”).
levelintHeading level (1-6).
textStringHeading text content.

A translation of the current page, for language switchers.

FieldTypeDescription
activeboolWhether this entry is the page being rendered.
languageStringLanguage code of the translation.
urlURLURL of the translated page, joined with the base path.

AssetsContext

Theme assets context.

FieldTypeDescription
cssVec<String>CSS files.
jsVec<String>JavaScript files.

StylingContext

Styling context exposed to templates.

FieldTypeDescription
tailwind_cdnboolWhether the Tailwind v4 CDN is enabled.
tailwind_configString?Optional Tailwind CDN custom theme config.

TagInfo

Tag metadata for template rendering.

FieldTypeDescription
countintNumber of pages tagged with this tag.
nameStringNormalized tag name (lowercase).

VersionOption

One version in a version switcher.

FieldTypeDescription
badgeString?Badge, when set.
idStringVersion identifier.
is_currentboolWhether this is the version being rendered.
labelStringDisplay label.
stateStringRelease state.
urlURLURL of this page in that version, joined with the base path.

DiagramsMermaidContext

Mermaid loader values surfaced to templates.

FieldTypeDescription
cdn_urlStringMermaid.js URL imported by the default-theme loader.
sri_hashStringSubresource Integrity hash for cdn_url. Surfaced to templates for forward-compatibility; the default-theme loader does not emit integrity= yet (ES import cannot carry SRI directly – the planned path is <link rel="modulepreload" integrity="...">).
themeStringTheme name passed to mermaid.initialize({ theme }).

BodySchema

Body structure schema for a document model.

FieldTypeDescription
elementsMap<String, ElementConstraint>Unordered element count constraints, keyed by element type.
forbidVec<BodyElementType>Element types forbidden at the top level of the body.
sequenceVec<SequenceStep>Ordered sequence pattern. Parsed at model-load time; validation against an actual body structure is performed by a later iteration.

TaxonomySchema

Taxonomy constraints for a document model.

FieldTypeDescription
fieldsMap<String, TaxonomyFieldConstraint>Per-taxonomy-field constraints, keyed by field name (e.g., "tags").

ElementConstraint

Count constraints for a body element type.

FieldTypeDescription
maxint?Most occurrences allowed; None leaves the upper bound open.
minint?Fewest occurrences allowed; None leaves the lower bound open.

BodyElementType

Top-level markdown element type for body structure analysis.

ValueDescription
h1
h2
h3
h4
h5
h6
paragraph
ordered_list
unordered_list
blockquote
table
footnote_definition
listMatches OrderedList OR UnorderedList during constraint application.
codeFenced or indented code block.
imageTop-level image (a paragraph containing exactly one image and no other inline content).
hrThematic break.
htmlTop-level raw HTML block.
definition_listDefinition list block (term\n: definition).
math_blockDisplay math block ($$ ... $$); inline math is never recorded.
admonitionGitHub-style admonition blockquote (> [!NOTE], > [!WARNING]). Plain blockquotes (no kind marker) continue to map to Self::Blockquote; the Self::Blockquote constraint matches both via the union semantic in Self::matches.
metadata_blockYAML / TOML metadata block embedded in the body (---\nkey: value\n--- outside the page frontmatter).

SequenceStep

A single step in an ordered body sequence pattern. Parsed at model-load time; validation against an actual body structure is performed by a later iteration.

FieldTypeDescription
elementVec<BodyElementType>Element types that satisfy this step (OR semantics).
maxint?Maximum elements to consume (None = unbounded).
minintEffective minimum elements to consume. When the raw YAML sets min: N it is taken verbatim; otherwise the minimum is 1 for required: true (the default) and 0 for required: false.

TaxonomyFieldConstraint

Constraints on a single taxonomy array field.

FieldTypeDescription
allowedVec<String>If non-empty, only these values are allowed (case-insensitive).
countint?Exact number of values required; None when only the bounds apply.
maxint?Most values allowed; None leaves the upper bound open.
minint?Fewest values allowed; None leaves the lower bound open.
required_one_ofVec<String>At least one value from this list must be present.

Linking Back to a Main Site

A docs (or other satellite) site often fronts a separate product or marketing site. Declaring that relationship in config.yaml lets the theme render navigation back to it:

site:
  main_site:
    url: https://example.com
    label: Example              # optional; defaults to site.name
    badge_label: Docs           # optional; text of the back-to-home chip
    cta_label: Get a license    # optional; CTA renders only with both cta_* keys
    cta_url: https://example.com/pricing

The bundled default theme reacts to the block in three places: the header wordmark links to main_site.url while a small chip (badge_label, default “Docs”) next to it keeps the way back to the site’s own home page, an accent-colored button in the header links cta_label to cta_url, and the footer carries main_site.host – the host name derived from the URL. Without the block, none of this renders and the wordmark links to the site’s own home page. In your own templates, gate on the block the same way:

{% if site.main_site %}
<a href="{{ site.main_site.url }}">{{ site.main_site.host }}</a>
{% endif %}

Page URL and Canonical URL

page.url and page.canonical_url differ for one page. A home page in a home/ directory is reachable at both /home and the site root, because accent serve resolves the root through it and accent build writes the root index.html from it. Both return 200 with the same content, so the page needs a single URL to be advertised under, and that is the root. Point your canonical tag at page.canonical_url and the tag will agree with what sitemap.xml lists; point it at page.url and the two will name different URLs for one page, which is worse than either on its own – a search engine then has two contradictory claims and picks between them itself.

Tag Routes

Tag index and per-tag listing pages are served at /tags and /tags/{tag}. Create tags.html.jinja and tag.html.jinja templates to customize their appearance. See the Templating Guide for full examples.

Built-in Styling Pipeline

Accent CMS includes a built-in CSS processing pipeline that handles Sass/SCSS compilation, Tailwind-style utility CSS, and production minification – no Node.js required. Configure it in your theme.yaml:

styling:
  sass:
    enabled: true
  utilities:
    enabled: true
    scan:
      - templates/**/*.jinja
  processing:
    minify: true

See the Styling Guide for full documentation.

Styling Tips

  1. Mobile-first - Start with mobile styles, then add breakpoints
  2. CSS Variables - Use custom properties for easy theming
  3. Semantic HTML - Use proper elements for accessibility
  4. Minimize dependencies - Keep your CSS lean and fast

Activating Your Theme

Set your theme in config.yaml:

theme:
  name: my-theme

Then restart the server to see your changes.