This article serves as both a complete usage tutorial for matery theme content tag plugins and an automated test sandbox.
The theme’s native tags are registered inthemes/matery/scripts/tags/(unified registration viaindex.js).
Automated tests assert the rendered markers indicated below.
1. Note
Purpose
Colored callout blocks inside posts, for warning / success / info / danger emphasis.
Syntax
{% note color %}content{% endnote %}- Color (optional, default
default):default/primary/success/info/warning/danger - Alias:
{% subnote %}(secondary note)
All Colors Example
default default note — plain hint
primary primary note — key emphasis
success success note — operation complete
info info note — supplementary
warning warning note — caution
danger danger note — serious warning
Effect
Colored rounded cards with a left color bar + icon (success ✓ / warning ⚠ / danger ✕).
Test assertion: page contains
class="note"marker (6 colors, one each).
2. Timeline
Purpose
Show events / version records / growth history in chronological order.
Syntax
{% timeline title,color %}
<!-- timeline time-node -->
- event content (Markdown supported)
<!-- endtimeline -->
{% endtimeline %}- Title: overall timeline title
- Color (optional):
green/blue/red/orangeetc. - Time nodes:
<!-- timeline date -->separates each event
Example
Theme Feature Evolution
2026-08
- Added content tag test page (note/timeline/tabs/label complete tutorial)
- Gallery data refactor (galleries.yml three forms)
- Unified lightGallery image viewer library
2026-07
- Comment architecture optimization (comment: waline)
- Fixed href=/ MIME style error
Effect
Vertical timeline with dot markers, color themes supported, titles rendered with Markdown.
Test assertion: page contains
timelinemarker + 3 time nodes.
3. Tabs
Purpose
Multi-tab content switching, great for categorized display / step-by-step instructions / comparisons.
Syntax
{% tabs unique-name,active-index %}
<!-- tab tab-title -->
content (Markdown and inline tags supported)
<!-- endtab -->
{% endtabs %}- Unique name: required, used to generate ids (spaces become
-) - Active index: optional, default 1 (first tab activated)
- Tab titles:
<!-- tab title -->defines each tab
Example (first tab active by default)
- Full-featured: content tags cover 10+ plugins
- Flexible config: every feature independently toggleable
- Performance: lazy loading + CLS placeholders
- Many config options, learning curve at first
- Some features depend on external CDN
- Tech blogs: code / tutorials / comparisons
- Personal blogs: galleries / projects / history
Effect
Materialize tabs style, click to switch, active tab highlighted.
Test assertion: page contains
nav-tabsmarker + 3 tabs.
4. Label
Purpose
Inline colored mini labels for keywords / status / category annotations.
Syntax
{% label color@text %}- Color (optional, default
default):default/primary/success/info/warning/danger - ★ Note the parameter order: color BEFORE
@, text AFTER@(source usessplit('@'),classes=args[0],text=args[1])
All Colors Example
Default Primary Success Info Warning DangerEffect
Inline rounded colored labels, text on white or colored background.
Test assertion: page contains
class="label"marker (6 colors).
5. Button
Purpose
CTA buttons inside posts, for links / downloads / action entries.
Syntax
{% button link,text,icon,title %}- ★ Link is the FIRST argument (source:
url=args[0],text=args[1]) - Link: required, target URL (relative path
/allowed) - Text: button display text
- Icon (optional): Font Awesome icon name (e.g.
home,github;fa fa-prefix auto-added) - title (optional): hover tooltip text (not a color!)
- Alias:
{% btn %}
Example
Home GitHub About Text-only buttonEffect
Materialize-style buttons with icon + ripple click effect.
Test assertion: page contains
btnmarker (≥4 buttons).
6. GitHub Card
Purpose
Show a GitHub repo info card (repo name / description / stars / forks).
Syntax
{% githubCard user:username repo:repo-name %}- user: required, GitHub username
- repo: required, repository name
- Optional params (advanced):
width/height/theme/align
Example
Effect
Repo card: name + description + stars / forks, click to visit the repo. Depends on GitHub API (falls back to a link on failure).
Test assertion: page contains
github-cardor repo name link marker.
7. Mermaid Diagrams
Purpose
Render Mermaid diagrams (flowcharts / sequence diagrams / class diagrams etc.).
Front-matter Config (important)
mermaid: true # Enable in post front-matter (otherwise Mermaid won't render)Also requires enabling in the theme _config.yml:
post:
mermaid:
enable: trueSyntax
{% mermaid %}
graph TD
A --> B
{% endmermaid %}Example 1: Flowchart (graph)
graph TD
A[Build] --> B{Test}
B -->|Pass| C[Deploy]
B -->|Fail| D[Fix]
D --> B
C --> E[Monitor]
Example 2: Sequence Diagram (sequenceDiagram)
sequenceDiagram
participant U as User
participant S as Server
participant D as Database
U->>S: Send request
S->>D: Query data
D-->>S: Return result
S-->>U: Respond with page
Example 3: Class Diagram (classDiagram)
classDiagram
class User {
+String name
+login()
}
class Blog {
+String title
+publish()
}
User --> Blog
Effect
Mermaid renders to SVG diagrams, adapting to dark mode (theme already handles it).
Test assertion: page contains
class="mermaid"marker (≥3 diagrams).
8. PDF Embed
Purpose
Embed a PDF file viewer inside a post.
Syntax
{% pdf file-URL %}- File URL: PDF address (local or remote)
Example
{% pdf /medias_webp/docs/sample.pdf %}Effect
PDF.js viewer (page flip / zoom / download).
Test assertion: page contains pdf-related container marker.
9. Insert Fragment (insertmd)
Purpose
Insert Markdown template fragments from source/_template/ for content reuse.
Syntax
{% insertmd 'filename.md' %}- Filename: file under
source/_template/(including .md extension)
Example
{% insertmd 'disclaimer.md' %}Effect
Renders the template fragment content at the current position (async loading, supports nesting).
Test assertion: rendered output contains template fragment content.
10. WeChat Dialog Card
Purpose
Render a WeChat chat interface card, for dialog examples / support scenarios.
Syntax
{% wechat_dialog %}
User: what the user says
Assistant: the AI/support reply
{% endwechat_dialog %}- Line format: lines starting with
User:= right-side user bubble;Assistant:= left-side assistant bubble - Supports: multi-line content, Markdown rendering (bold / lists etc.)
- Alias:
{% wd %}equals{% wechat_dialog %}
Example
Effect
WeChat-style chat bubbles, left/right split, avatar + bubble.
Test assertion: page contains WeChat dialog container marker (
.chat-container).
11. Group Image
Purpose
Display multiple images in a row/column grid layout (auto column splitting).
Syntax
{% groupimage count layout %}


{% endgroupimage %}- First parameter: number of images (2–10)
- Second parameter (optional): layout (e.g.
2-1means 2 on the first row, 1 on the second) - Images: standard Markdown image syntax inside the tag body
- Alias:
{% gi %}equals{% groupimage %}
Example (3 images, 2+1 layout)



Effect
Image grid layout (2+1 columns), zoom on hover.
Test assertion: page contains
group-imagecontainer marker.
12. URL Card (cardurl)
Purpose
Show a summary card for a link (title, description, icon), for recommended links / cited resources.
Syntax
{% cardurl [url=target-link] [title=title] [desc=description] [avatar=icon-URL] %}- Params: bracket
[key=value]formaturlrequired, target linktitleoptional, card title (defaults to url)descoptional, description textavataroptional, icon image URL
Example
Effect
Link card: site icon + title + description, click to jump.
Test assertion: page contains URL card container marker (
.card-url-wrapper).
13. Admonition (markdown-it-container)
Purpose
Render callout containers using the ::: type syntax via the markdown-it-container plugin (warning / note / info / attention / error), distinct from the note tag.
Syntax
::: warning
*put warning content here*
:::- Types:
warning/note/info/attention/erroretc.
Example
This is a warning container — content that needs attention!
This is a note container — supplementary info.
This is an info container — informational.
This is an attention container — caution.
This is an error container — serious error.
Effect
Callout containers with a left color bar + icon + light background.
Test assertion: page contains
admonitioncontainer marker.
14. Encrypt Fragment Tag ({% encrypt %})
Purpose
Encrypt a segment of markdown content within a post. Enter the password to decrypt and reveal the content. Each fragment has its own independent password.
Syntax
{% encrypt password "title" "description" %}
encrypted markdown (can contain nested tags like note/timeline)
{% endencrypt %}| Parameter | Required | Description |
|---|---|---|
| password | ✅ | Decryption password (Hexo auto-strips quotes) |
| title (second quoted param) | ❌ | Prompt text shown above the password input |
| description (third quoted param) | ❌ | Placeholder text shown inside the input field |
Example (test fragment)
Effect
- After rendering, the segment becomes an encrypted container (password input); enter
test-secretto decrypt and reveal - Nested tag support: fragments can contain
{% note %}and other tags (Method Y: after_post_render secondary rendering, verified working) - No plaintext leakage: the rendered HTML contains no segment plaintext (encryption happens on the final HTML)
- No re-entry needed: after decryption, refreshing within 1 day skips the password prompt (stores derived dk, not the password, with TTL mechanism)
Automated Testing
Encryption unit tests tools/tests/unit/unit-encrypt.test.js + unit/unit-encrypt2.test.js cover: AES-GCM + PBKDF2 round-trip / fragment decryption / internal note rendering / markdown bold / dk storage without password; post-decrypt runtime behaviour is verified by gate layer L2y.
Wiki Full-Page Encryption (docs-sidebar)
Wiki document pages (layout/wiki.ejs) support full-page encryption: configure encrypt_password in the sidebar data source (source/_data/docs-sidebar.yml) to encrypt all pages under that wiki, enter password to decrypt and render.
# source/_data/docs-sidebar.yml
encrypt_password: "your-password" # Required, encrypts entire wiki section
encrypt_message: This page is encrypted # Optional, prompt text
encrypt_placeholder: Enter password # Optional, input placeholderCross-page sharing: after decryption, the derived key (dk) is stored in localStorage; other pages under the same wiki auto-decrypt without re-entering the password.
Test assertion: L2y (wiki encrypted page decryption) + L8 8c (encrypted wiki accessible).
Unified Post-Decryption Refresh
After decrypting encrypted content, page components need re-initialization (before decryption the DOM contains only ciphertext). The unified trigger point: hbe-bundle onSuccess callback (shared across post / gallery / wiki / fragment); each listener keeps only its specific handling; config keys with the encrypt_ prefix are skipped during sidebar rendering (not shown as table of contents items).
| Component | Post-Decryption Action |
|---|---|
| Code blocks | codeWidget() scans new pre elements and generates toolbar |
| Image lightbox | articleInit() wraps img elements → lightGallery |
| Mermaid | events.refresh() re-renders (idempotent) |
| Layout | refreshLayout() re-arranges |
See the Layout chapter §8 “Unified Post-Decryption Refresh” for the full table. Test assertion: L2y (post-decryption component initialization).
15. Math Formulas (MathJax / KaTeX dual channel)
Purpose
LaTeX formula rendering in the body + formulas embedded in mind maps; MathJax and KaTeX coexist as a dual channel.
Configuration (root _config.yml + theme _config.yml)
# Root _config.yml: markdown-it-mathjax3 (Node-side SSR outputs MathJax SVG)
markdown:
plugins:
- name: markdown-it-mathjax3
options:
tex:
inlineMath: [['$', '$']]
displayMath: [['$$', '$$']]
processEscapes: true
# Theme _config.yml: post.math engine (Fluid style, pick one)
post:
math:
enable: false # When enabled, posts can use math by default
specific: true # When true, requires front-matter `math: true` to activate
engine: mathjax # Options: mathjax | katex- engine: mathjax (default):
$...$inline /$$...$$block (mathjax3 5.x hardcodes$/$$,\(...\)not supported) - engine: katex: when switching, comment out markdown-it-mathjax3 and enable texmath — supports
$...$/$$...$$+\(...\)/\[...\](brackets must be standalone blocks), frontend loads KaTeX CSS (SSR already rendered, no JS needed) - markmap mind map embedded formulas:
hexo_markmap.featuresincludesmath→ formulas inside mind maps render via KaTeX (.katex)
Syntax
Inline formula: $E=mc^2$
Block formula:
$$
\int_0^1 x^2 dx = \frac{1}{3}
$$Example
Inline formula:
Implementation Effects
- Body formulas render as MathJax SVG (
<mjx-container>), no frontend JS needed (SSR output) - Mind map embedded formulas render as KaTeX (
.katex) - Dark/light theme adaptive
Test assertion:
tools/tests/rich-content-integration.test.js(L2o): formula pagemjx-container ≥ 10+katex ≥ 1(dual channel).
16. Mind Map markmap
Purpose
Render Markdown lists as interactive mind maps (zoom/collapse/links).
Configuration (root _config.yml hexo_markmap)
hexo_markmap:
CDN: 'jsdelivr' # 2026-08-24: fastly.jsdelivr.net unreachable, switched to cdn.jsdelivr.net
features:
- math # formulas inside mind maps (KaTeX)
- prism # code highlighting
- zoom # zoom
theme: autoSyntax
{% markmap 350px %}
- Node 1
- Child node 1.1
- Child node 1.2
- Node 2
{% endmarkmap %}- Height parameter:
{% markmap 350px %}(optional, default height) - Content: standard Markdown unordered list (supports links / inline code / bold / KaTeX formulas)
Example
Implementation Effects
Markdown lists render as interactive mind maps (.markmap-container svg), supporting zoom/collapse/link jumps, dark-mode adaptive.
Test assertion:
tools/tests/rich-content-integration.test.js(L2o):.markmap svgrenders.
Coexistence with encrypted segments (important): markmap’s four scripts (d3 / markmap-view / markmap-toolbar / init) are localized by an after_render:html filter. Pages containing {% encrypt %} use a serial onload-chain loader instead of <script defer> — because per the HTML spec a dynamically inserted <script> ignores defer (its async defaults to true). When the hbe decryption flow re-inserts them, they execute in download-completion order, exactly inverting the dependency chain (init at 1.8KB finishes first → window.markmap undefined; d3 at 280KB finishes last → markmap-view throws d3 is not defined). Static pages keep defer (parallel download, better first paint). Full root cause and debugging lessons: docs/EXPERIENCE.md §58.
Appendix: Non-Tag Theme Features
The following three items are not “article-embedded tag plugins” (§1–§16) but rather standalone theme-level features. They are grouped here to distinguish the boundary between tag and non-tag features.
17. Typewriter typing (fun_features)
Purpose
Homepage banner subtitle typewriter animation (same source as subtitle.typed in Interaction & Visual §3, config aggregated under fun_features.typing).
Configuration (theme _config.yml fun_features)
fun_features:
typing:
enable: true # Typewriter switch
typeSpeed: 70 # Typing speed, larger = slower
cursorChar: "_" # Cursor character
loop: false # Whether to loopImplementation Effects
- Homepage banner subtitle types out character by character + cursor blinks (
.typed-cursor) - Supports
index.slogan.apiendpoint for the subtitle (falls back to thetextfield on request failure) page.subtitle: falsedisables the typewriter on a single page
Test assertion:
tools/tests/rich-content-integration.test.js(L2o): homepage.typed-cursorexists.
18. Social Share sharejs
Purpose
Social share buttons at the bottom of posts (Weibo/WeChat/QQ/Douban, etc.), one-click sharing of the current article.
Configuration (theme _config.yml)
sharejs:
enable: true
# Supported order: twitter, facebook, google, qq, qzone, wechat, weibo, douban, linkedin
sites: twitter,facebook,qq,qzone,wechat,weibo,douban
# addthis alternative (pick one of sharejs / addthis)
addthis:
enable: false
pubid: 613a04428f842fe1Implementation Effects
.social-shareshare bar at the bottom of posts (thedata-sitesattribute controls which platforms show)- WeChat share generates a QR code (
data-wechat-qrcode-helperhint text) - Rendered only on post pages (
page.layout == 'post')
Test assertion:
tools/tests/rich-content-integration.test.js(L2o):.social-sharecomponent exists.
19. Live2D Mascot
Purpose
Live2D mascot in the bottom-right corner (pure static models, no PHP backend), interactive chat / model switching.
Configuration (theme _config.yml)
live2dWidget:
enable: trueImplementation Effects
- Mascot container (
#live2dcanvas/#waifu) fixed in the bottom-right corner - Returning-visitor guard: loads only when
localStorage.viewCount > 1(not on first visit, performance protection) - Models are static in
source/live2d/(Cubism2/Cubism5 dual runtime), dark-mode adaptive
Test assertion:
tools/tests/rich-content-integration.test.js(L2o): after settingviewCount=3, the mascot container exists.
⚠️ Known Pitfalls
Pitfall 1: Double Section Numbering
Symptom: Headings render as “1. 1. Overview” (two numbers).
Cause: Theme CSS auto-adds numbers to h2–h6 by default; hand-written numbers (e.g. ## 1. Overview) stack on top.
Fix: Pages with hand-written numbering add closeAutoTocNum: true in front-matter; pages without hand-written numbering must not add this field.
This article already correctly sets
closeAutoTocNum: true.
Pitfall 5: Config Changes Require Clean Build
Symptom: After modifying tag plugin configuration (e.g. post.mermaid.enable, live2dWidget.enable, sharejs.enable etc.), the page doesn’t update.
Cause: Hexo’s caching mechanism doesn’t auto-detect configuration changes.
Fix: After changing config, you must npm run clean && npm run build.
Appendix: Tag Plugin Quick Reference
| Plugin | Syntax | Key Params | Needs frontmatter |
|---|---|---|---|
| note | {% note color %}content{% endnote %} | 6 colors | no |
| timeline | {% timeline title,color %}...{% endtimeline %} | title / color | no |
| tabs | {% tabs name,index %}...{% endtabs %} | name / index | no |
| label | {% label color@text %} | color first, text after | no |
| button | {% button link,text,icon,title %} | link first / text second | no |
| githubCard | {% githubCard user:xx repo:yy %} | user / repo | no |
| mermaid | {% mermaid %}...{% endmermaid %} | diagram type | yes (mermaid: true) |
{% pdf URL %} | URL | no | |
| insertmd | {% insertmd 'file.md' %} | filename | no |
| wechat_dialog | {% wechat_dialog %}User:/Assistant: lines{% endwechat_dialog %} | User/Assistant lines | no |
| groupimage | {% groupimage count layout %}{% endgroupimage %} | count / layout | no |
| cardurl | {% cardurl [url=xx] [title=xx] [desc=xx] %} | url / title / desc | no |
| admonition | ::: type content ::: | warning/note/info/attention/error | no |
| encrypt | {% encrypt password "title" "desc" %}...{% endencrypt %} | password / title / desc | no |
| math | $...$ / $$...$$ | mathjax3 (SSR SVG) / katex engine | yes (mathjax: true) |
| markmap | {% markmap 350px %}list{% endmarkmap %} | height / features (math/prism/zoom) | no |
| typewriter | fun_features.typing | typeSpeed / cursorChar / loop | no |
| share | sharejs.sites | twitter / wechat / weibo etc. | no (post pages only) |
| Live2D | live2dWidget.enable | returning-visitor guard viewCount>1 | no |
References
系列教程
Hexo 主题功能测试系列
- Hexo主题功能测试-布局与页面篇 —— 页面布局、导航、侧边栏、卡片等结构功能
- Hexo主题功能测试-交互视觉篇 —— 代码高亮、图片灯箱、动画、明暗切换等交互
- Hexo主题功能测试-内容tag篇 —— note / tabs / timeline / label / button 等 tag 插件
- Hexo主题功能测试-Markdown语法篇 —— Markdown 渲染与扩展语法
- Hexo主题功能测试-自动化测试篇 —— 主题功能的自动化测试方案

