Hero Extension Playbook
Hero work should start from the contract layer and flow downward into runtime and rendering.
Core Hero Extension Points
.vitepress/utils/vitepress/api/frontmatter/hero/HeroFrontmatterApi.ts.vitepress/utils/vitepress/api/frontmatter/hero/HeroTypographyRegistryApi.ts.vitepress/utils/vitepress/api/frontmatter/hero/FloatingElementRegistryApi.ts.vitepress/utils/vitepress/runtime/hero/navAdaptiveState.ts.vitepress/theme/components/hero/background/BackgroundLayer.vue.vitepress/config/shaders/index.ts.vitepress/config/shaders/templates/base-shader.ts
Add a Typography Style
Do not add a new if styleType === ... branch directly in hero components.
Register the style through the typography registry and let the runtime resolve it.
Use one of these registration modes:
- Shared built-in style
Add the definition to
DEFAULT_TYPOGRAPHY_STYLE_DEFINITIONSin.vitepress/utils/vitepress/api/frontmatter/hero/HeroTypographyRegistryApi.ts. - Project-local bootstrap style
Call
heroTypographyRegistry.registerStyle(...)from a startup module imported by.vitepress/theme/index.ts.
Keep the canonical type and all aliases lowercase because the registry normalizes them.
import { heroTypographyRegistry } from "@utils/vitepress/api/frontmatter/hero";
heroTypographyRegistry.registerStyle({
type: "editorial-soft",
aliases: ["soft-editorial"],
motion: {
intensity: 0.9,
title: { x: 6, y: -4, scale: 1.03 },
text: { x: 8, y: 3, scale: 1.02 },
tagline: { x: 4, y: 6, scale: 1.01 },
image: { x: 5, y: -2, scale: 1.015 },
transitionDuration: 520,
transitionDelayStep: 36,
transitionEasing: "cubic-bezier(0.2, 0.9, 0.2, 1)",
},
});Registration checklist:
- Define motion defaults in
HeroTypographyRegistryApi.ts. - Keep the canonical type and aliases lowercase.
- If the new style needs different structure or class hooks, update hero content components or the shared hero typography CSS.
- Verify the resolved style through
.vitepress/utils/vitepress/runtime/hero/typographyState.ts, not by copying motion logic into multiple components. - Add a real frontmatter example and update the docs.
Create a New Hero Page
Use a hero page when the page itself is a landing surface instead of a normal article.
---
layout: home
hero:
name: Developer Docs
text: Extend Hero, Runtime, and Nav
tagline: Contract-first configuration with shared runtime behavior
typography:
type: floating-tilt
actions:
- theme: brand
text: Hero Extension
link: /en/doc/heroExtension
---Checklist:
- Start from
layout: home. - Keep hero configuration in frontmatter instead of page-local component hacks.
- If the page becomes a main entry point, update the docs hub and locale nav in the same change.
Add a Floating Element Type
import { floatingElementRegistry } from "@utils/vitepress/api/frontmatter/hero";
floatingElementRegistry.registerType({
type: "keyword-chip",
renderAs: "badge",
className: "floating-keyword-chip",
});Add a New Hero Feature
When the feature is author-facing and reusable:
- Add the field to
.vitepress/utils/vitepress/api/frontmatter/hero/HeroFrontmatterApi.tsand normalize it there. - If it needs shared state, timing, observers, or viewport logic, add or extend
.vitepress/utils/vitepress/runtime/hero/**. - Render the feature only after the contract shape is stable.
- Add real examples in both locale trees.
- If it changes home hero actions or named links, update the related nav/home docs at the same time.
Add a Shader Preset
- Create or extend a preset under
.vitepress/config/shaders/**. - Reuse helpers from
.vitepress/config/shaders/templates/base-shader.tswhere possible. - Export the preset from
.vitepress/config/shaders/index.ts. - Reference the preset through normalized frontmatter rather than page-local imports.
Add a New Background Renderer Type
- Add the type to
HeroFrontmatterApi.ts. - Add the rendering branch in
.vitepress/theme/components/hero/background/BackgroundLayer.vue. - Create a dedicated renderer under
.vitepress/theme/components/hero/background/. - Move observers or theme-sensitive lifecycle into shared runtime modules.
- Add real examples in both locale trees.
Extend Nav and Search Visuals
- Put adaptive calculations in
.vitepress/utils/vitepress/runtime/hero/navAdaptiveState.ts. - Put theme-safe value resolution in the shared theme runtime.
- Expose author-facing colors through frontmatter-backed CSS variables.
- Avoid direct DOM theme reads inside nav, search, or hero child components.
Hero Extension Checklist
- The new field or type is normalized in the API layer.
- Rendering components consume normalized values only.
- Theme and resize behavior use shared runtime helpers.
- The change is documented in both
docs/enanddocs/zh. yarn buildpasses before syncing the change elsewhere.
Registry Deep Dive: HeroTypographyRegistry
Source file: .vitepress/utils/vitepress/api/frontmatter/hero/HeroTypographyRegistryApi.ts Singleton import: import { heroTypographyRegistry } from "@utils/vitepress/api/frontmatter/hero";
The HeroTypographyRegistryApi class manages hero typography styles — each style defines how hero text elements (title, text, tagline, image) move in response to mouse/viewport interaction (parallax motion). Instead of scattering if styleType === ... branches across hero components, styles are registered here and resolved at runtime through a single lookup.
Built-in Styles
| Canonical Type | Aliases | Description |
|---|---|---|
"floating-tilt" | ["default"] | Default parallax effect with tilting motion for all hero text nodes. Used when no type is specified or an unknown type falls back. |
"grouped-float" | — | All text nodes move as a group with uniform floating motion. |
"slanted-wrap" | — | Diagonal motion pattern with wrapped text alignment. |
"none" | ["static"] | Disables all motion. Use when the page needs a completely static hero. |
If frontmatter specifies an unrecognized type (e.g., type: banana), the registry falls back to "floating-tilt".
Motion Fields Explained
Each style defines motion defaults for four per-node targets and four global controls:
Per-node motion (defined for title, text, tagline, image):
| Field | Type | Meaning |
|---|---|---|
x | number | Horizontal pixel offset for parallax. Positive = moves right on mouse move. |
y | number | Vertical pixel offset for parallax. Positive = moves down on mouse move. |
scale | number | Zoom factor. 1.0 = no zoom, 1.03 = 3% zoom-in on hover. |
Global motion controls:
| Field | Type | Meaning |
|---|---|---|
intensity | number | Multiplier for all motion values. 1.0 = full, 0.5 = half intensity. |
transitionDuration | number | Duration (ms) for motion transitions. |
transitionDelayStep | number | Stagger delay (ms) between each node's transition start. Creates a cascading effect. |
transitionEasing | string | CSS easing function for transitions. |
API Methods
import { heroTypographyRegistry } from "@utils/vitepress/api/frontmatter/hero";
// Register a single style with motion defaults
heroTypographyRegistry.registerStyle({
type: "editorial-soft",
aliases: ["soft-editorial"],
motion: {
intensity: 0.9,
title: { x: 6, y: -4, scale: 1.03 },
text: { x: 8, y: 3, scale: 1.02 },
tagline: { x: 4, y: 6, scale: 1.01 },
image: { x: 5, y: -2, scale: 1.015 },
transitionDuration: 520,
transitionDelayStep: 36,
transitionEasing: "cubic-bezier(0.2, 0.9, 0.2, 1)",
},
});
// Register multiple styles at once
heroTypographyRegistry.registerStyles([
{ type: "cinematic", motion: { /* ... */ } },
{ type: "minimal-slide", aliases: ["slide"], motion: { /* ... */ } },
]);
// Check if a style exists
heroTypographyRegistry.hasStyle("editorial-soft"); // true
heroTypographyRegistry.hasStyle("default"); // true (alias for floating-tilt)
// Resolve a type name to its canonical form (follows aliases, applies fallback)
heroTypographyRegistry.resolveStyleType("default"); // "floating-tilt"
heroTypographyRegistry.resolveStyleType("static"); // "none"
heroTypographyRegistry.resolveStyleType("banana"); // "floating-tilt" (fallback)
// Get a CLONED copy of motion defaults (safe to mutate)
const motion = heroTypographyRegistry.resolveMotionDefaults("floating-tilt");
motion.intensity = 0.5; // Safe — this is a deep clone, not the original
// List all registered style type names
heroTypographyRegistry.listStyleTypes(); // ["floating-tilt", "grouped-float", "slanted-wrap", "none", ...]Resolution Flow
- Frontmatter
hero.typography.typeis read (e.g.,"default"). resolveStyleType("default")checks aliases → finds"floating-tilt".resolveMotionDefaults("floating-tilt")returns a deep clone of the style's motion config.- The runtime applies these values to hero text nodes for parallax rendering.
- If the type is unknown at step 2, the registry silently falls back to
"floating-tilt".
Clone safety:
resolveMotionDefaultsalways returns a new object. Components can freely mutate the returned motion config without affecting the registry's stored defaults.
Registry Deep Dive: FloatingElementRegistry
Source file: .vitepress/utils/vitepress/api/frontmatter/hero/FloatingElementRegistryApi.ts Singleton import: import { floatingElementRegistry } from "@utils/vitepress/api/frontmatter/hero";
The FloatingElementRegistryApi class manages floating element types — the decorative items (badges, icons, images, code snippets, etc.) that can appear around the hero area. Each type definition controls how the element is rendered.
Built-in Types
| Type | Description |
|---|---|
"text" | Plain text element. Default fallback when an unknown type is used. |
"card" | Card-style container with optional shadow and border. |
"image" | Image element for hero decorations. |
"lottie" | Lottie animation player. |
"badge" | Small label-style element (tags, keywords). |
"icon" | Icon element using the project's icon system. |
"stat" | Statistic display (number + label). |
"code" | Code snippet block. |
"shape" | Geometric SVG shape. |
If frontmatter specifies an unrecognized type, the registry falls back to "text".
Type Definition Shape
interface FloatingElementTypeDefinition {
type: string; // Canonical type name (lowercase)
renderAs?: string; // Map to another type's renderer (e.g., renderAs: "badge" uses badge rendering)
component?: Component; // Vue component for fully custom rendering
className?: string; // Additional CSS class applied to the floating wrapper
}renderAs: Reuses another type's renderer. Example:{ type: "keyword-chip", renderAs: "badge" }— renders keyword-chip items using the badge renderer.component: Overrides rendering entirely with a custom Vue component. Use this whenrenderAsis insufficient.className: Injects a CSS class for type-specific styling without needing a custom renderer.
API Methods
import { floatingElementRegistry } from "@utils/vitepress/api/frontmatter/hero";
// Register a single type
floatingElementRegistry.registerType({
type: "keyword-chip",
renderAs: "badge",
className: "floating-keyword-chip",
});
// Register multiple types at once
floatingElementRegistry.registerTypes([
{ type: "author-avatar", renderAs: "image", className: "floating-avatar" },
{ type: "live-metric", component: LiveMetricWidget },
]);
// Resolve a type to its definition (returns fallback "text" definition if unknown)
const def = floatingElementRegistry.resolveType("keyword-chip");
// { type: "keyword-chip", renderAs: "badge", className: "floating-keyword-chip" }
const unknown = floatingElementRegistry.resolveType("banana");
// Returns the "text" type definition (fallback)
// List all registered type names
floatingElementRegistry.listRegisteredTypes();
// ["text", "card", "image", "lottie", "badge", "icon", "stat", "code", "shape", "keyword-chip", ...]Resolution Flow
- Frontmatter
hero.floating.items[].typeis read (e.g.,"keyword-chip"). resolveType("keyword-chip")looks up the registry → returns itsFloatingElementTypeDefinition.- If the type has
renderAs, the renderer delegates to that type's visual logic. - If the type has
component, the custom Vue component is mounted instead. - If the type is unknown, the registry silently falls back to the
"text"definition.
Registry Deep Dive: Shader Registry
Source file: .vitepress/config/shaders/index.ts Import path: No alias exists for .vitepress/config/. Use relative imports from your file location (e.g., '../../../../config/shaders' from a component in the hero directory).
The shader registry manages WebGL shader presets for hero backgrounds. Each preset defines fragment shader code and configurable parameters (colors, speed, scale, etc.). Presets are referenced by name in frontmatter — the runtime resolves them at render time.
Built-in Shader Presets
| Preset Name | Description |
|---|---|
water | Animated water surface with wave distortion. |
noise | Perlin/simplex noise pattern with animated evolution. |
galaxy | Cosmic starfield with swirling nebula effects. |
plasma | Colorful plasma waves with gradient blending. |
ripple | Concentric ripple effects emanating from center. |
silk | Smooth flowing silk/fabric animation. |
API Functions
// NOTE: No @config alias resolves to .vitepress/config/.
// Use a relative import from your file's location.
import {
listShaderTemplates,
getShaderTemplate,
getShaderTemplateByType,
registerShaderTemplate,
} from "../../../../config/shaders";
import type { ShaderTemplate } from "../../../../config/shaders";
// List all registered shader preset names
const names = listShaderTemplates();
// ["water", "noise", "galaxy", "plasma", "ripple", "silk"]
// Get a shader template by name
const silk = getShaderTemplate("silk");
// Get a shader template by its type field
const waterShader = getShaderTemplateByType("water");
// Register a custom shader preset
registerShaderTemplate({
name: "aurora",
type: "aurora",
// ... shader configuration (fragment code, uniforms, params)
});Frontmatter Usage
hero:
background:
type: shader
shader:
preset: silkThe runtime reads hero.background.shader.preset, calls getShaderTemplate("silk") to load the shader configuration, and passes it to the WebGL renderer in BackgroundLayer.vue.
Extension Steps
- Create a new shader file (e.g.,
.vitepress/config/shaders/aurora.ts). - Reuse helpers from
.vitepress/config/shaders/templates/base-shader.tsfor common uniforms and setup. - Export the preset from
.vitepress/config/shaders/index.tsviaregisterShaderTemplate(...). - Reference the preset in frontmatter:
shader.preset: aurora. - Test rendering in both light and dark themes — shader colors often need per-theme adjustments.
Import path warning: The
@configalias resolves to.vitepress/utils/config/, NOT.vitepress/config/. Shader files live at.vitepress/config/shaders/, so you must use relative paths. See the in the Extension Architecture doc for details.
Related Pages
- — Theme sync standard, resize standard, and full extension API reference.
- — File ownership rules, import alias reference, and layer placement guide.
- — Change ordering, verification commands, and upstream sync rules.
- — All available Markdown plugins including shader-effect containers.