Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cackle

A Material Design 3 (MD3) documentation theme for Hexo, built for API documentation sites that need multiple versions and multiple languages at the same time.

home

Features

  • Material Design 3 — MD3 tokens (color, shape, typography), light / dark theme toggle.
  • Multi-version — any number of doc versions (e.g. v1.0, v2.0) side by side; a version switcher in the header, "latest" badge on the homepage.
  • Multi-language — any number of languages (e.g. zh-cn, en); a language switcher in the header; every doc is localized independently.
  • Custom languages — add languages the theme doesn't ship (e.g. zh-tw) by dropping a language pack into <site>/languages/; site packs also override built-in packs.
  • Automatic language negotiation — based on the visitor's Accept-Language header, / redirects to the best matching language; the matching rules, fallback language and explicit aliases are all configurable (see language_negotiation).
  • Customizable home page — the homepage is rendered from a configurable list of sections (home.sections), including arbitrary custom sections with Markdown content.
  • Client-side full-text search — no backend, instant results, covers all versions and languages; a mobile search button opens the search layer on small screens.
  • Mermaid diagrams — code blocks tagged mermaid render as diagrams using the active light/dark theme and repaint automatically when the theme changes; the CDN is configurable (mermaid_cdn).
  • Subdirectory deployment — all asset and search-index URLs derive from Hexo's root, so the site works out of the box under /docs/ or any subpath.
  • Accessibility — focus-trapped search dialog with focus restore, aria-modal, prefers-reduced-motion support and forced color-scheme.
  • Customizable branding — logo, title and favicon are configurable.

Requirements

  • Hexo 7.x
  • Node.js ≥ 14

Installation

  1. Clone / copy this folder into themes/cackle of your Hexo site.

  2. Make sure the renderer for EJS templates is installed:

    npm install hexo-renderer-ejs
  3. Activate the theme in _config.yml:

    theme: cackle
  4. (Optional) Copy theme defaults into a site-level override file so the theme stays upgradeable:

    cp themes/cackle/_config.yml _config.cackle.yml

    Settings in _config.cackle.yml override the theme's _config.yml.

  5. Run the dev server:

    hexo clean && hexo server

Document layout

All documentation lives under source/ using the convention:

source/
├── languages.yaml          # language registry (see below)
├── index.md                # homepage (layout: index)
├── v1.0/
│   ├── zh-cn/              # Simplified Chinese docs for v1.0
│   │   ├── getting-started.md
│   │   └── ...
│   └── en/                 # English docs for v1.0
├── v2.0/
│   ├── zh-cn/
│   └── en/

Rules:

  • Every language (including the default one) lives in its own subdirectory named after the language path; URLs carry the language prefix, e.g. /v2.0/zh-cn/getting-started/.

  • Each document's front-matter should declare its language:

    ---
    title: Getting Started
    lang: en
    ---

    The lang value must match a language-pack filename in themes/cackle/languages/ (e.g. en, zh-cn).

    The directory name is authoritative: a document living under <version>/<language>/ is always treated as that language, so the front-matter lang is optional (it is ignored when it disagrees with the directory).

Configuration

menu — sidebar navigation

Entries map to document filenames (without extension) inside each version/language folder:

menu:
  - title: Getting Started
    file: getting-started
  - title: Authentication
    file: authentication

If a menu entry has no matching document, it is skipped with a warning.

brand — logo, title, favicon

brand:
  logo: i-menu-book        # icon name from icons.ejs, or a path like /images/logo.svg
  text:                    # brand text (defaults to config.title)
  favicon: /images/favicon.svg

home.sections — customizable home page

The homepage is built from an ordered list of sections. Built-in section types: hero, features, versions. Default order:

home:
  sections:
    - hero
    - features
    - versions

You can reorder them, remove any of them, or insert your own custom sections with Markdown content:

home:
  sections:
    - hero
    - type: custom
      title: Changelog
      content: |
        - **2026-08**: added multi-language support
        - [v2.0](https://example.com/v2.0/zh-cn/getting-started/) released
    - versions
  • Custom sections render title as an <h2> and content through Hexo's Markdown renderer.
  • Other home settings (badge / title / subtitle / CTA / version block texts / feature cards) can be customized under home.*; empty values fall back to the language pack, then to built-in defaults.

Note on arrays: Hexo merges _config.cackle.yml over the theme's _config.yml with a deep-merge that combines arrays by index. Defining the same array (e.g. home.sections) in both files can produce surprising results. Keep array settings in a single file — preferably the theme's _config.yml.

Other settings

search: true               # enable client-side search
code_block_line_number: true
toc_max_depth: 3           # max heading depth in the on-page TOC (h2–h4)
mermaid_cdn: https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js
footer:
  text: My API © 2026
  • toc_max_depth controls how deep the on-page table of contents goes (default 3 → h2/h3; set 4 to include h4). It is consumed client-side via window.CACKLE_CFG.
  • mermaid_cdn is the URL of the Mermaid library loaded on pages that contain mermaid diagrams. Set it to an empty string to skip loading Mermaid altogether.
  • meta.description in a language pack is used as the page description fallback when neither the document nor the site defines one.
  • config.root (Hexo site config) is respected by all asset URLs and the search index, enabling subdirectory deployment (e.g. root: /docs/).

Multi-language

source/languages.yaml

languages:
  - lang: zh_CN            # BCP-47 / locale code
    path: zh-cn            # URL prefix & doc directory name (also matches the language-pack filename)
    DisplayAs: 简体中文     # shown in the language switcher
  - lang: en_US
    path: en
    DisplayAs: English

The first language in the list is the default language.

Language negotiation

When a visitor requests /, the theme tries to match the browser's Accept-Language header against the configured languages and redirects (302) to the first document of the latest version in the matched language (requests already in the default language are served without redirect; a Vary: Accept-Language header is included).

The behavior is fully configurable via language_negotiation:

language_negotiation:
  enabled: true          # set to false to disable automatic redirection entirely
  fallback: en           # language (path from languages.yaml) used when nothing matches
  aliases:               # explicit browser-tag → language-path mappings (optional)
    zh: zh-cn
    zh-CN: zh-cn
    en: en
    en-US: en

Matching is tried in this order:

  1. aliases — an explicitly mapped browser tag wins over everything else.
  2. Exact match — browser tag (e.g. zh-CN) equals a lang (zh_CN) or path (zh-cn).
  3. Primary-language prefix — e.g. zh matches zh_CN.
  4. fallback — the configured language (defaults to en; if that language is not registered, the default language is used instead).

When running hexo server, the middleware scripts/lang-redirect.js handles this server-side. For pure static hosting (no server middleware), the same rules are applied client-side by an inline script on the home page, using navigator.language.

Language packs

UI strings live in themes/cackle/languages/<lang>.yml (one file per language). Add a new file for every language registered in languages.yaml.

Custom languages via <site>/languages/

To add a language the theme doesn't ship (e.g. Traditional Chinese, zh-tw), drop a language pack into the site root's languages/ folder:

<site>/
├── _config.yml
├── languages/
│   └── zh-tw.yml          # your custom language pack
└── source/
    ├── languages.yaml     # (optional) register language metadata
    ├── v1.0/zh-tw/…       # docs for the new language
    └── v2.0/zh-tw/…

The pack filename defines the language path (URL prefix & doc directory). You may attach metadata under a reserved cackle_lang key:

# <site>/languages/zh-tw.yml
cackle_lang:
  lang: zh_TW            # BCP-47 / locale code, used by language negotiation
  path: zh-tw            # URL prefix & doc dir (defaults to the filename)
  display_as: 繁體中文     # shown in the language switcher (defaults to path)

# UI strings — for a brand-new language they may be partial; missing keys fall
# back to the default language
header:
  search_placeholder: 搜尋文件…
  version_switch: 切換版本

Behavior:

  • Same filename ⇒ user wins. If you provide a pack whose name matches a built-in language (e.g. languages/en.yml), your file replaces the built-in pack entirely — there is no per-key merging, your custom config takes priority over the theme's. Provide a complete pack in that case (copy the built-in file from themes/cackle/languages/ as a starting point).
  • A pack whose name is not shipped registers a brand-new language. As long as a doc directory <version>/<language-path>/ exists, the language appears automatically in the language switcher, the search index and the Accept-Language negotiation — no need to touch source/languages.yaml.
  • If source/languages.yaml also lists the language, its entry takes precedence for ordering / DisplayAs / BCP-47 code; otherwise auto-registered languages are appended as non-default languages.
  • JSON packs (languages/zh-tw.json) are supported as well.
  • Like other site files, the folder must be watched by your deployment workflow; a hexo clean is recommended after adding packs.

The theme's built-in packs live in themes/cackle/languages/. The site folder is loaded by the theme scripts (scripts/languages.js + scripts/lib/site-languages.js) and merged into the theme's i18n at generation time.

Theme structure

themes/cackle/
├── _config.yml               # theme defaults (overridable per-site)
├── layout/
│   ├── index.ejs             # home page (section-driven)
│   ├── doc.ejs               # documentation page
│   ├── page.ejs              # plain page
│   └── _partial/             # head, header, sidebar, footer, switchers, icons
├── scripts/
│   ├── home-sections.js      # parses home.sections → api_home_sections
│   ├── lang-redirect.js      # Accept-Language negotiation middleware
│   ├── languages.js          # injects <site>/languages/ packs into theme i18n
│   ├── versions.js           # version & language grouping → api_versions
│   ├── generator.js          # emits search-index.json (all versions & languages, strips Markdown/HTML)
│   └── lib/
│       └── site-languages.js # shared helpers for custom site languages
├── _config.yml               # also holds the language_negotiation settings
├── languages/                # language packs (UI strings)
└── source/
    ├── css/                  # base, tokens, components, highlight
    ├── js/                   # main.js (UI), search.js (client search, reads CACKLE_CFG)
    └── images/

Known caveats

  • Changes to scripts/ require a server restart — hexo server watches documents and configs, but not scripts.
  • Site-level _config.cackle.yml changes also require a restart — hexo server only watches source/ and themes/; the theme's own _config.yml is re-merged automatically, but the site-level override file is only read at startup.
  • After changing language packs or source/languages.yaml, run hexo clean to flush the generated assets.
  • The console error <path> attribute d: Expected number comes from an inline SVG icon and is harmless.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages