Extension Architecture
This page explains where framework code belongs and how to extend the template without mixing contracts, runtime state, and rendering.
Architectural Rule
Use this separation consistently:
apiowns contract truthruntimeowns shared state and lifecycletheme/componentsowns renderingconfigowns project defaults and registration wiringdocs/srcowns examples and developer-facing documentation
If one change starts touching all five layers, move in that order instead of editing randomly.
Create a Standard Page
For a normal documentation page:
- Create the markdown file under the same relative path in both locales.
Example:
docs/src/en-US/guide/example.mdanddocs/src/zh-CN/guide/example.md. - Start with simple page frontmatter:
---
title: Example Page
layout: doc
description: What this page covers.
---- Only add nav or homepage entry points if the page should be discoverable from a top-level surface.
- If the page introduces a new frontmatter contract, update the matching
frontmatter/**reference page in the same change. - If the page lives in a new directory branch, make sure the sidebar metadata for that branch exists and builds cleanly.
Create a Home or Hero Page
For a landing page or hub page driven by the hero system:
- Use
layout: home. - Author the hero contract in frontmatter first:
---
layout: home
hero:
name: Framework Docs
text: Build and Extend the Template
tagline: Runtime, frontmatter, plugins, and component workflows
actions:
- theme: brand
text: Development Workflow
link: /frontmatter/reference/developmentWorkflow
---- Add
featuresorfeatureCardsonly after the hero content is stable. - If the page uses reusable hero action keys, extend the link-key contract in the home-link service and hero frontmatter API instead of hardcoding branches in a component.
- If the page changes nav/search/hero visuals, document the corresponding frontmatter keys immediately.
Component Extension
When adding a new component:
- Place it under the right category in
docs/.vitepress/theme/components/<category>/. - Export it from the matching barrel in
docs/.vitepress/utils/vitepress/componentRegistry/**when it should be reused internally. - Register it in
docs/.vitepress/utils/vitepress/components.tswhen markdown users should be able to reference it directly. - Add locale resources under both
docs/.vitepress/config/locale/en-US/components/**anddocs/.vitepress/config/locale/zh-CN/components/**when the component has UI text. - Keep
docs/.vitepress/config/locale/component-id-mapping.jsonsynchronized with any newuseSafeI18n("<component-id>", ...)identifier.
Register New Content
Use this deeper chain for content-style components that appear in markdown or in the document shell:
- Create the Vue file under
docs/.vitepress/theme/components/content/. - Export it from
docs/.vitepress/theme/components/content/index.tsif that barrel exists. - Export it from
docs/.vitepress/utils/vitepress/componentRegistry/contentRegistry.tswhen other theme modules should import it through the registry layer. - Register it in
docs/.vitepress/utils/vitepress/components.tswhen markdown pages should call it directly by tag name. - If it uses
useSafeI18n, add locale JSON files and add the component ID mapping entry. - If it is emitted by a markdown plugin, make sure the plugin outputs a registered tag and not an unregistered custom element.
- Add at least one real markdown usage page so the component is validated in build output instead of only in isolation.
Use this rule of thumb:
componentRegistry/contentRegistry.tsInternal reuse inside the theme layer.components.tsPublic markdown/global registration.
Function and Runtime Extension
When adding a function, composable, service, or controller:
- Put pure data normalization in
docs/.vitepress/utils/vitepress/api/**. - Put shared stateful behavior in
docs/.vitepress/utils/vitepress/runtime/**. - Export public entry points from the nearest
index.tsbarrel. - Prefer one shared runtime over many local observers inside components.
- Do not let feature components become the only place where a behavior is implemented if multiple views will need it later.
Strong examples already in the template:
- Theme sync:
docs/.vitepress/utils/vitepress/runtime/theme/** - Hero nav adaptation:
docs/.vitepress/utils/vitepress/runtime/hero/navAdaptiveState.ts - Frontmatter contract normalization:
docs/.vitepress/utils/vitepress/api/frontmatter/hero/HeroFrontmatterApi.ts
Configuration Extension
Use the configuration layer for anything that should be centrally declared and then consumed by runtime or views.
Typical files:
docs/.vitepress/config/project-config.tsGlobal project settings, feature toggles, social links, routing helpers, and search setup.docs/.vitepress/config/lang/**Locale-facing nav and theme configuration.docs/.vitepress/config/markdown-plugins.tsMarkdown plugin composition order.docs/.vitepress/config/shaders/**Built-in shader presets and shader registry wiring.
Checklist:
- Define the new field or registry entry.
- Normalize it in the correct API layer.
- Consume only normalized values in runtime or views.
- Add a docs example immediately.
- Run
yarn sync-configandyarn frontmatterwhen generated metadata depends on the change.
Style Extension
Global style ownership should stay explicit. Follow the import layering in docs/.vitepress/theme/styles/index.css.
Use the correct vehicle:
- Scoped
<style>for component-local layout and visuals - Global CSS under
docs/.vitepress/theme/styles/**for shared tokens, plugin skinning, and cross-component selectors - Frontmatter or config-backed CSS variables for theme-sensitive or runtime-sensitive values
Prefer CSS variable contracts over one-off hardcoded global selectors when a value is meant to be themed or changed by content authors.
Create a New Markdown Plugin
When adding a markdown plugin:
- Implement it in
docs/.vitepress/plugins/**. - Register it in
docs/.vitepress/config/markdown-plugins.ts. - Register any required rendering component in
docs/.vitepress/utils/vitepress/components.ts. - Add usage pages under both
docs/src/en-US/**anddocs/src/zh-CN/**. - Build and verify actual rendered output, not just the parser.
If the plugin creates a reusable authoring surface, also document:
- the markdown syntax
- the rendered component name
- any frontmatter dependency
- copy-paste-safe examples in both locales
Recommended File Ownership Table
Use this quick lookup when deciding where to edit:
- New hero field or nested frontmatter key:
docs/.vitepress/utils/vitepress/api/frontmatter/** - Theme-ready lifecycle or observer logic:
docs/.vitepress/utils/vitepress/runtime/** - New Vue block or visual surface:
docs/.vitepress/theme/components/** - New markdown syntax:
docs/.vitepress/plugins/**plusdocs/.vitepress/config/markdown-plugins.ts - New global token or shared skin:
docs/.vitepress/theme/styles/** - New usage guide or extension manual:
docs/src/**
Import Alias Reference
The project uses TypeScript path aliases configured in docs/tsconfig.json and mirrored in docs/.vitepress/config/common-config.ts (Vite resolve.alias). Always use aliases instead of deep relative paths.
| Alias | Resolves to | Example import |
|---|---|---|
@utils | docs/.vitepress/utils/ | import { getThemeRuntime } from '@utils' |
@utils/* | docs/.vitepress/utils/* | import { resolveThemeValueByMode } from '@utils/vitepress/runtime/theme/themeValueResolver' |
@utils/vitepress | docs/.vitepress/utils/vitepress/index.ts | import { useHeroTheme } from '@utils/vitepress' |
@config | docs/.vitepress/utils/config/ | import { someConfigUtil } from '@config' |
@config/* | docs/.vitepress/utils/config/* | import { someHelper } from '@config/helpers' |
@components | docs/.vitepress/theme/components/ | import MyComponent from '@components/content/MyComponent.vue' |
@components/* | docs/.vitepress/theme/components/* | import Footer from '@components/navigation/Footer.vue' |
@config does NOT point to docs/.vitepress/config/
@config resolves to docs/.vitepress/utils/config/, not docs/.vitepress/config/. Files in docs/.vitepress/config/ (such as shaders, locale data, and project-config) have no alias. Use relative imports to reach them, e.g. import { getShaderTemplate } from '../../../../config/shaders'.
Services Layer
Services are stateless or lightly-stateful modules that perform a focused task. They live under docs/.vitepress/utils/vitepress/services/.
Key services already in the template:
- home-link service — Resolves named action keys (e.g.
"get-started","guide") into actual route URLs. Used by hero action buttons so links can be authored as symbolic keys rather than hardcoded paths. - i18n service (
useSafeI18n) — Provides safe locale string lookup. Returns the key itself when a translation is missing, preventing blank UI. Paired withcomponent-id-mapping.jsonfor per-component namespacing.
When adding a new service:
- Create it in
docs/.vitepress/utils/vitepress/services/. - Export from the nearest barrel.
- Keep it decoupled from Vue component lifecycle — services should be importable from runtime, API, or component layers.
System Layer
System modules handle cross-cutting wiring that glues configuration, locale, and runtime together. They live under docs/.vitepress/utils/vitepress/system/.
Key system modules:
- component-id-mapping — Maps component identifiers to locale namespaces. When a component calls
useSafeI18n("my-component", ...), this mapping tells the i18n service which JSON file holds the strings. - locale wiring — Ensures locale JSON files under
docs/.vitepress/config/locale/{en-US,zh-CN}/are correctly loaded and made available to the i18n service.
When adding a new system module:
- Create it in
docs/.vitepress/utils/vitepress/system/. - If it affects i18n, update
component-id-mapping.jsonand both locale directories. - Export from the system barrel so other layers can consume it.
Component Registry Barrels
Components are organized into four registries under docs/.vitepress/utils/vitepress/componentRegistry/. These barrels control internal reuse across the theme layer (distinct from components.ts which controls markdown-facing global registration).
contentRegistry.ts (15 components)
CustomAlert, comment, Linkcard, PageTags, TagsPage, Bills, MdDialog, MdMultiPageDialog, ChatMessage, ChatPanel, ArticleMetadata, ResponsibleEditor, MarkMapView, VChart, ShaderEffectBlock
mediaRegistry.ts (3 components)
PdfViewer, YoutubeVideo, BilibiliVideo
navigationRegistry.ts (1 component)
Footer
uiRegistry.ts (8 components)
ProgressLinear, Buttons, State, Animation, NotFound, Preview, Carousels, Steps
When adding a new component to the registry:
- Place the Vue file in the appropriate category under
docs/.vitepress/theme/components/<category>/. - Export it from the matching registry barrel above.
- If markdown users should also use it by tag name, additionally register in
docs/.vitepress/utils/vitepress/components.ts.
Related Pages
- — Day-to-day development commands and processes
- — Step-by-step guide for extending the hero system
- — Complete listing of available frontmatter keys
- — Deep technical reference for all extension APIs