Search

Accent CMS generates a lightweight search index from your content at build time (and on-the-fly in serve mode). A bundled JavaScript library fetches the index on first interaction and scores results entirely in the browser – no backend required, works offline, zero external dependencies.

Accent CMS ships two search backends:

  • Simple Search (all editions) – JSON index with client-side substring matching. Lightweight, zero dependencies, works everywhere.
  • DocFind (Standard+ editions) – WASM-powered search using FST fuzzy matching and RAKE keyword extraction. Handles typos, scales to large sites, and produces smaller indexes.

Quick Start

Add a search form to any template with the search_form() function:

{{ search_form() }}

Or embed one in a markdown page with the [search] shortcode:

[search]

That is all you need. The search index is generated automatically and the JavaScript is loaded on demand.

How It Works

Simple Search backend:

  1. Accent CMS scans all published pages and builds a JSON index containing each page’s title, URL, lead, tags, and a truncated plain-text excerpt.
  2. The index is served at /_search/index.json (serve mode) or written to _search/index.json (build mode).
  3. A small JavaScript library (/_search/search.js, ~5 KB) is loaded when the search form first appears on the page.
  4. On the first keystroke the library fetches the index, tokenizes the query, and scores every page using configurable field weights.
  5. Results appear instantly with highlighted matching terms, keyboard navigation, and a click-to-visit link.

DocFind backend:

  1. Accent CMS scans all published pages and feeds them into the DocFind engine, which builds an FST (finite state transducer) index with RAKE keyword extraction.
  2. The index is embedded into a WASM binary (docfind_bg.wasm) that contains both the search engine and the data.
  3. The client-side JS loads the WASM module on first interaction and runs queries entirely in the browser.
  4. DocFind supports fuzzy matching – queries like “templete” still find “template” – and scales better to large sites than Simple Search.

Template Function

{# Default placeholder and limit #}
{{ search_form() }}

{# Custom placeholder text #}
{{ search_form("Search the docs...") }}

{# Custom placeholder and max results #}
{{ search_form("Find articles...", 5) }}
ArgumentTypeDefaultDescription
placeholderstring"Search..."Placeholder text for the input field
results_limitinteger10Maximum number of results to display

The function returns safe HTML that can be placed in any template position – a header, sidebar, or dedicated search page.

Shortcode

Use the [search] shortcode inside any markdown page:

[search]

[search placeholder="Search docs..." limit=5]
ParameterDefaultDescription
placeholder"Search..."Placeholder text
limit10Maximum results

Configuration

All search settings are optional. The defaults work for most sites.

search:
  enabled: true           # Generate the search index (default: true)
  backend: auto           # auto | docfind | simple (default: auto)
  min_word_length: 2      # Minimum word length to index (default: 2)
  content_length: 200     # Characters of content per page (default: 200)
  exclude:                # URL patterns to exclude from the index
    - "/404"
    - "/tags/*"
  fields:                 # Field weights for relevance scoring
    title: 3.0            # Title matches score highest (default: 3.0)
    content: 1.0          # Body content (default: 1.0)
    tags: 2.0             # Tag matches (default: 2.0)
    lead: 1.5             # Lead/excerpt text (default: 1.5)
  docfind:
    category_field: section  # Frontmatter field for result categories (default: section)

Search Backend

The backend setting controls which search engine is used:

ValueBehavior
autoUses DocFind when available (Standard+ edition with a valid license or dev mode), otherwise falls back to Simple Search. This is the default.
docfindRequires the DocFind backend. Falls back to Simple Search with a warning if the binary was not compiled with DocFind support or if no Standard+ license is active in production mode.
simpleAlways uses Simple Search (JSON-based), even on Standard+ editions.

In dev mode (accent serve without --production), DocFind is available regardless of license status so you can evaluate it before purchasing.

DocFind Settings

The docfind section configures the DocFind backend:

SettingDefaultDescription
category_field"section"Frontmatter field used to group results into categories. If the field is not present on a page, the top-level URL segment is used instead.

Field Weights

Weights control how much a match in each field contributes to a page’s relevance score. Higher values mean matches in that field rank higher. The weights are embedded in the JSON index and applied by the client-side scorer.

Exclude Patterns

Use glob patterns to keep pages out of the index. The * wildcard matches any characters except /:

  • "/404" – exact match
  • "/tags/*" – matches /tags/rust, /tags/web, but not /tags itself

Set search.enabled: false to skip index generation entirely. The /_search/index.json route will return 404.

Editions

FeatureCore (free)StandardPro
Simple Search (accent serve)yesyesyes
Simple Search (accent build)yesyesyes
DocFind search (accent serve, dev mode)yesyesyes
DocFind search (accent serve --production)–yesyes
DocFind search (accent build)–yesyes

In dev mode (accent serve without --production), DocFind is available on all editions so you can evaluate it locally. Production serving and static builds require a Standard or Pro license. Without a valid license, accent build and accent serve --production automatically fall back to Simple Search.

Build Mode

When you run accent build, the search files are written to the output directory. The exact files depend on the active backend and your license:

  • Standard/Pro license with backend: auto or backend: docfind – generates DocFind WASM output.
  • No license (Core edition) – always generates Simple Search JSON output, regardless of the backend setting.

Simple Search output:

output/
  _search/
    index.json    # JSON search index
    search.js     # Client-side library

DocFind backend output:

output/
  _search/
    docfind_bg.wasm  # WASM search engine with embedded index
    docfind.js       # JS glue for loading the WASM module
    search.js        # Client-side library (dual-backend)

The static build is fully self-contained – open index.html from a file server and search works without any backend.

Styling

The search UI uses CSS classes prefixed with acms-search-. The default theme includes styles in _search.scss. You can override them in your own theme:

ClassElement
.acms-search-formOuter container
.acms-search-inputText input field
.acms-search-resultsDropdown results container
.acms-search-resultIndividual result link
.acms-search-result-titleResult title
.acms-search-result-snippetResult snippet text
.acms-search-highlightHighlighted matching text
.acms-search-empty“No results found” message
.acms-search-staleAdded to the form when the DocFind index is being rebuilt (serve mode only)

The styles use CSS custom properties (--color-border, --color-bg, --color-accent, etc.) so they adapt to light and dark themes automatically.

Stale Index Indicator (DocFind)

In serve mode with the DocFind backend, the search form receives the .acms-search-stale CSS class while the WASM index is being rebuilt after a content change. You can style this to show a subtle indicator:

.acms-search-form.acms-search-stale .acms-search-input {
  border-color: var(--color-warning, #e6a817);
}

The stale class is removed automatically once the rebuild completes.

Keyboard Navigation

The search input supports keyboard navigation:

KeyAction
Arrow DownMove to next result
Arrow UpMove to previous result
EnterNavigate to the selected result
EscapeClose the results dropdown

The Simple Search JSON index uses short keys to minimize file size:

{
  "pages": [
    {
      "u": "/blog/my-post",
      "t": "My Blog Post",
      "l": "A short lead text",
      "c": "Plain text content truncated to content_length...",
      "g": ["rust", "web"]
    }
  ],
  "weights": { "t": 3.0, "c": 1.0, "g": 2.0, "l": 1.5 }
}
KeyFull nameDescription
uURLPage URL path
tTitlePage title
lLeadLead/excerpt (omitted if empty)
cContentPlain-text content, truncated
gTagsTag list (omitted if empty)

Pages with status: draft, status: archived, or matching an exclude pattern are not included. Only published, listable pages appear in the index.

Tips

  • Keep content_length reasonable. The default of 200 characters per page keeps the index small. Increase it if you want better snippet coverage at the cost of a larger download.
  • Exclude utility pages. Add /404, /tags/*, and other non-content pages to the exclude list to keep results relevant.
  • Place the form in your header. Using search_form() in your base template’s header makes search available on every page.
  • Check index size. After building, inspect _search/index.json to verify the file size is reasonable relative to your content. A typical 50-page site produces an index under 20 KB.