feat: Complete fleet — 94 skills across 10+ domains

Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-16 16:27:42 -06:00
parent 861b28b965
commit f5792c40be
1262 changed files with 212048 additions and 61 deletions

View File

@@ -0,0 +1,5 @@
# Generation Info
- **Source:** `sources/vitepress`
- **Git SHA:** `d4796a0373eb486766cf48e63fdf461681424d43`
- **Generated:** 2026-01-28

65
skills/vitepress/SKILL.md Normal file
View File

@@ -0,0 +1,65 @@
---
name: vitepress
description: VitePress static site generator powered by Vite and Vue. Use when building documentation sites, configuring themes, or writing Markdown with Vue components.
metadata:
author: Anthony Fu
version: "2026.1.28"
source: Generated from https://github.com/vuejs/vitepress, scripts located at https://github.com/antfu/skills
---
VitePress is a Static Site Generator (SSG) built on Vite and Vue 3. It takes Markdown content, applies a theme, and generates static HTML that becomes an SPA for fast navigation. Perfect for documentation, blogs, and marketing sites.
**Key Characteristics:**
- File-based routing with `.md` files
- Vue components work directly in Markdown
- Fast HMR with instant updates (<100ms)
- Default theme optimized for documentation
- Built-in search (local or Algolia)
**Before working with VitePress projects:**
- Check `.vitepress/config.ts` for site configuration
- Look at `.vitepress/theme/` for custom theme extensions
- The `public/` directory contains static assets served as-is
> The skill is based on VitePress 1.x, generated at 2026-01-28.
## Core
| Topic | Description | Reference |
|-------|-------------|-----------|
| Configuration | Config file setup, defineConfig, site metadata | [core-config](references/core-config.md) |
| CLI | Command-line interface: dev, build, preview, init | [core-cli](references/core-cli.md) |
| Routing | File-based routing, source directory, rewrites | [core-routing](references/core-routing.md) |
| Markdown | Frontmatter, containers, tables, anchors, includes | [core-markdown](references/core-markdown.md) |
## Features
### Code & Content
| Topic | Description | Reference |
|-------|-------------|-----------|
| Code Blocks | Syntax highlighting, line highlighting, diffs, focus | [features-code-blocks](references/features-code-blocks.md) |
| Vue in Markdown | Components, script setup, directives, templating | [features-vue](references/features-vue.md) |
| Data Loading | Build-time data loaders, createContentLoader | [features-data-loading](references/features-data-loading.md) |
| Dynamic Routes | Generate pages from data, paths loader files | [features-dynamic-routes](references/features-dynamic-routes.md) |
## Theme
| Topic | Description | Reference |
|-------|-------------|-----------|
| Theme Config | Nav, sidebar, search, social links, footer | [theme-config](references/theme-config.md) |
| Customization | CSS variables, slots, fonts, global components | [theme-customization](references/theme-customization.md) |
| Custom Theme | Building themes from scratch, theme interface | [theme-custom](references/theme-custom.md) |
## Advanced
| Topic | Description | Reference |
|-------|-------------|-----------|
| Internationalization | Multi-language sites, locale configuration | [advanced-i18n](references/advanced-i18n.md) |
| SSR Compatibility | Server-side rendering, ClientOnly, dynamic imports | [advanced-ssr](references/advanced-ssr.md) |
## Recipes
| Topic | Description | Reference |
|-------|-------------|-----------|
| Deployment | GitHub Pages, Netlify, Vercel, Cloudflare, Nginx | [recipes-deploy](references/recipes-deploy.md) |

View File

@@ -0,0 +1,299 @@
---
name: vitepress-internationalization
description: Setting up multi-language sites with locale configuration and RTL support
---
# Internationalization
VitePress supports multi-language sites through locale configuration.
## Directory Structure
Organize content by locale:
```
docs/
├─ en/
│ ├─ guide.md
│ └─ index.md
├─ zh/
│ ├─ guide.md
│ └─ index.md
└─ fr/
├─ guide.md
└─ index.md
```
Or with root as default language:
```
docs/
├─ guide.md # English (root)
├─ index.md
├─ zh/
│ ├─ guide.md
│ └─ index.md
└─ fr/
├─ guide.md
└─ index.md
```
## Configuration
```ts
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
export default defineConfig({
locales: {
root: {
label: 'English',
lang: 'en'
},
zh: {
label: '简体中文',
lang: 'zh-CN',
link: '/zh/'
},
fr: {
label: 'Français',
lang: 'fr',
link: '/fr/'
}
}
})
```
## Locale-Specific Config
Override site config per locale:
```ts
locales: {
root: {
label: 'English',
lang: 'en',
title: 'My Docs',
description: 'Documentation site',
themeConfig: {
nav: [
{ text: 'Guide', link: '/guide/' }
],
sidebar: {
'/guide/': [
{ text: 'Introduction', link: '/guide/' }
]
}
}
},
zh: {
label: '简体中文',
lang: 'zh-CN',
link: '/zh/',
title: '我的文档',
description: '文档站点',
themeConfig: {
nav: [
{ text: '指南', link: '/zh/guide/' }
],
sidebar: {
'/zh/guide/': [
{ text: '介绍', link: '/zh/guide/' }
]
}
}
}
}
```
## Locale-Specific Properties
Each locale can override:
```ts
interface LocaleSpecificConfig {
lang?: string
dir?: string // 'ltr' or 'rtl'
title?: string
titleTemplate?: string | boolean
description?: string
head?: HeadConfig[] // Merged with existing
themeConfig?: ThemeConfig // Shallow merged
}
```
## Search i18n
### Local Search
```ts
themeConfig: {
search: {
provider: 'local',
options: {
locales: {
zh: {
translations: {
button: {
buttonText: '搜索',
buttonAriaLabel: '搜索'
},
modal: {
noResultsText: '没有结果',
resetButtonTitle: '重置搜索',
footer: {
selectText: '选择',
navigateText: '导航',
closeText: '关闭'
}
}
}
}
}
}
}
}
```
### Algolia Search
```ts
themeConfig: {
search: {
provider: 'algolia',
options: {
appId: '...',
apiKey: '...',
indexName: '...',
locales: {
zh: {
placeholder: '搜索文档',
translations: {
button: { buttonText: '搜索文档' }
}
}
}
}
}
}
```
## Separate Locale Directories
For fully separated locales without root fallback:
```
docs/
├─ en/
│ └─ index.md
├─ zh/
│ └─ index.md
└─ fr/
└─ index.md
```
Requires server redirect for `/``/en/`. Netlify example:
```
/* /en/:splat 302 Language=en
/* /zh/:splat 302 Language=zh
/* /en/:splat 302
```
## Persisting Language Choice
Set cookie on language change:
```vue
<!-- .vitepress/theme/Layout.vue -->
<script setup>
import DefaultTheme from 'vitepress/theme'
import { useData, inBrowser } from 'vitepress'
import { watchEffect } from 'vue'
const { lang } = useData()
watchEffect(() => {
if (inBrowser) {
document.cookie = `nf_lang=${lang.value}; expires=Mon, 1 Jan 2030 00:00:00 UTC; path=/`
}
})
</script>
<template>
<DefaultTheme.Layout />
</template>
```
## RTL Support (Experimental)
For right-to-left languages:
```ts
locales: {
ar: {
label: 'العربية',
lang: 'ar',
dir: 'rtl'
}
}
```
Requires PostCSS plugin like `postcss-rtlcss`:
```ts
// postcss.config.js
import rtlcss from 'postcss-rtlcss'
export default {
plugins: [
rtlcss({
ltrPrefix: ':where([dir="ltr"])',
rtlPrefix: ':where([dir="rtl"])'
})
]
}
```
## Organizing Config
Split config into separate files:
```
.vitepress/
├─ config/
│ ├─ index.ts # Main config, merges locales
│ ├─ en.ts # English config
│ ├─ zh.ts # Chinese config
│ └─ shared.ts # Shared config
```
```ts
// .vitepress/config/index.ts
import { defineConfig } from 'vitepress'
import { shared } from './shared'
import { en } from './en'
import { zh } from './zh'
export default defineConfig({
...shared,
locales: {
root: { label: 'English', ...en },
zh: { label: '简体中文', ...zh }
}
})
```
## Key Points
- Use `locales` object in config with `root` for default language
- Each locale can override title, description, and themeConfig
- `themeConfig` is shallow merged (define complete nav/sidebar per locale)
- Don't override `themeConfig.algolia` at locale level
- `dir: 'rtl'` enables RTL with PostCSS plugin
- Language switcher appears automatically in nav
<!--
Source references:
- https://vitepress.dev/guide/i18n
-->

View File

@@ -0,0 +1,228 @@
---
name: vitepress-ssr-compatibility
description: Server-side rendering compatibility, ClientOnly component, and handling browser-only code
---
# SSR Compatibility
VitePress pre-renders pages on the server during build. All Vue code must be SSR-compatible.
## The Rule
Only access browser/DOM APIs in Vue lifecycle hooks:
- `onMounted()`
- `onBeforeMount()`
```vue
<script setup>
import { onMounted, ref } from 'vue'
const windowWidth = ref(0)
onMounted(() => {
// Safe - runs only in browser
windowWidth.value = window.innerWidth
})
</script>
```
**Do NOT** access at top level:
```vue
<script setup>
// WRONG - runs during SSR where window doesn't exist
const width = window.innerWidth
</script>
```
## ClientOnly Component
Wrap non-SSR-friendly components:
```vue
<template>
<ClientOnly>
<BrowserOnlyComponent />
</ClientOnly>
</template>
```
## Libraries That Access Browser on Import
Some libraries access `window` or `document` when imported:
### Dynamic Import in onMounted
```vue
<script setup>
import { onMounted } from 'vue'
onMounted(async () => {
const lib = await import('browser-only-library')
lib.doSomething()
})
</script>
```
### Conditional Import
```ts
if (!import.meta.env.SSR) {
const lib = await import('browser-only-library')
lib.doSomething()
}
```
### In enhanceApp
```ts
// .vitepress/theme/index.ts
export default {
async enhanceApp({ app }) {
if (!import.meta.env.SSR) {
const plugin = await import('browser-plugin')
app.use(plugin.default)
}
}
}
```
## defineClientComponent
Helper for components that access browser on import:
```vue
<script setup>
import { defineClientComponent } from 'vitepress'
const BrowserComponent = defineClientComponent(() => {
return import('browser-only-component')
})
</script>
<template>
<BrowserComponent />
</template>
```
With props and slots:
```vue
<script setup>
import { ref, h } from 'vue'
import { defineClientComponent } from 'vitepress'
const componentRef = ref(null)
const BrowserComponent = defineClientComponent(
() => import('browser-only-component'),
// Props passed to h()
[
{ ref: componentRef, someProp: 'value' },
{
default: () => 'Default slot content',
header: () => h('div', 'Header slot')
}
],
// Callback after component loads
() => {
console.log('Component loaded', componentRef.value)
}
)
</script>
```
## Teleports
Teleport to body only with SSG:
```vue
<ClientOnly>
<Teleport to="body">
<div class="modal">Modal content</div>
</Teleport>
</ClientOnly>
```
For other targets, use `postRender` hook:
```ts
// .vitepress/config.ts
export default {
async postRender(context) {
// Inject teleport content into final HTML
}
}
```
## Common SSR Errors
### "window is not defined"
Code accesses `window` at module level:
```ts
// BAD
const width = window.innerWidth
// GOOD
let width: number
onMounted(() => {
width = window.innerWidth
})
```
### "document is not defined"
Same issue with `document`:
```ts
// BAD
const el = document.querySelector('#app')
// GOOD
onMounted(() => {
const el = document.querySelector('#app')
})
```
### Hydration Mismatch
Server and client render different content:
```vue
<!-- BAD - different on server vs client -->
<div>{{ typeof window !== 'undefined' ? 'client' : 'server' }}</div>
<!-- GOOD - consistent -->
<ClientOnly>
<div>Client only content</div>
</ClientOnly>
```
## Checking Environment
```ts
// In Vue component
import.meta.env.SSR // true on server, false on client
// In VitePress
import { inBrowser } from 'vitepress'
if (inBrowser) {
// Client-only code
}
```
## Key Points
- Access browser APIs only in `onMounted` or `onBeforeMount`
- Use `<ClientOnly>` for non-SSR components
- Use `defineClientComponent` for libraries that access browser on import
- Check `import.meta.env.SSR` for environment-specific code
- Teleport to body only, or use `postRender` hook
- Consistent rendering prevents hydration mismatches
<!--
Source references:
- https://vitepress.dev/guide/ssr-compat
-->

View File

@@ -0,0 +1,119 @@
---
name: vitepress-cli
description: Command-line interface for development, building, and previewing VitePress sites
---
# CLI Commands
VitePress provides four main commands: `dev`, `build`, `preview`, and `init`.
## Development Server
Start the dev server with hot module replacement:
```bash
# In current directory (dev command is optional)
vitepress
# Or explicitly
vitepress dev
# With project in subdirectory
vitepress dev docs
```
**Options:**
| Option | Description |
|--------|-------------|
| `--open [path]` | Open browser on startup |
| `--port <port>` | Specify port number |
| `--base <path>` | Override base URL |
| `--cors` | Enable CORS |
| `--strictPort` | Exit if port is in use |
| `--force` | Ignore cache and re-bundle |
```bash
vitepress dev docs --port 3000 --open
```
## Production Build
Build static files for production:
```bash
vitepress build docs
```
**Options:**
| Option | Description |
|--------|-------------|
| `--base <path>` | Override base URL |
| `--target <target>` | Transpile target (default: `modules`) |
| `--outDir <dir>` | Output directory (relative to cwd) |
| `--assetsInlineLimit <n>` | Asset inline threshold in bytes |
| `--mpa` | Build in MPA mode (no client hydration) |
```bash
vitepress build docs --outDir dist
```
## Preview Production Build
Locally preview the production build:
```bash
vitepress preview docs
```
**Options:**
| Option | Description |
|--------|-------------|
| `--port <port>` | Specify port number |
| `--base <path>` | Override base URL |
```bash
vitepress preview docs --port 4173
```
## Initialize Project
Start the setup wizard:
```bash
vitepress init
```
This creates the basic file structure:
- `.vitepress/config.js` - Configuration
- `index.md` - Home page
- Optional example pages
## Package.json Scripts
Typical scripts configuration:
```json
{
"scripts": {
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
}
}
```
## Key Points
- Dev server runs at `http://localhost:5173` by default
- Preview server runs at `http://localhost:4173`
- Production output goes to `.vitepress/dist` by default
- The `docs` argument specifies the project root directory
- Use `--base` to override base path without modifying config
<!--
Source references:
- https://vitepress.dev/reference/cli
-->

View File

@@ -0,0 +1,189 @@
---
name: vitepress-configuration
description: Config file setup, defineConfig helper, site metadata, and build options
---
# Configuration
VitePress configuration is defined in `.vitepress/config.[js|ts|mjs|mts]`. Use `defineConfig` for TypeScript intellisense.
## Basic Config
```ts
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
export default defineConfig({
// Site metadata
title: 'My Docs',
description: 'Documentation site',
lang: 'en-US',
// URL base path (for GitHub Pages: '/repo-name/')
base: '/',
// Theme configuration
themeConfig: {
// See theme-config.md
}
})
```
## Site Metadata
```ts
export default defineConfig({
title: 'VitePress', // Displayed in nav, used in page titles
titleTemplate: ':title - Docs', // Page title format (:title = h1)
description: 'Site description', // Meta description
lang: 'en-US', // HTML lang attribute
head: [
['link', { rel: 'icon', href: '/favicon.ico' }],
['meta', { name: 'theme-color', content: '#5f67ee' }],
['script', { async: '', src: 'https://analytics.example.com/script.js' }]
]
})
```
## Build Options
```ts
export default defineConfig({
// Source files directory (relative to project root)
srcDir: './src',
// Exclude patterns from source
srcExclude: ['**/README.md', '**/TODO.md'],
// Output directory
outDir: './.vitepress/dist',
// Cache directory
cacheDir: './.vitepress/cache',
// Clean URLs without .html extension (requires server support)
cleanUrls: true,
// Ignore dead links during build
ignoreDeadLinks: true,
// Or specific patterns:
ignoreDeadLinks: ['/playground', /^https?:\/\/localhost/],
// Get last updated timestamp from git
lastUpdated: true
})
```
## Route Rewrites
Map source paths to different output paths:
```ts
export default defineConfig({
rewrites: {
// Static mapping
'packages/pkg-a/src/index.md': 'pkg-a/index.md',
// Dynamic parameters
'packages/:pkg/src/:slug*': ':pkg/:slug*'
}
})
```
## Appearance (Dark Mode)
```ts
export default defineConfig({
appearance: true, // Enable toggle (default)
appearance: 'dark', // Dark by default
appearance: 'force-dark', // Always dark, no toggle
appearance: 'force-auto', // Always follow system preference
appearance: false // Disable dark mode
})
```
## Vite & Vue Configuration
```ts
export default defineConfig({
// Pass options to Vite
vite: {
plugins: [],
resolve: { alias: {} },
css: { preprocessorOptions: {} }
},
// Pass options to @vitejs/plugin-vue
vue: {
template: { compilerOptions: {} }
},
// Configure markdown-it
markdown: {
lineNumbers: true,
toc: { level: [1, 2, 3] },
math: true, // Requires markdown-it-mathjax3
container: {
tipLabel: 'TIP',
warningLabel: 'WARNING',
dangerLabel: 'DANGER'
}
}
})
```
## Build Hooks
```ts
export default defineConfig({
// Transform page data
transformPageData(pageData, { siteConfig }) {
pageData.frontmatter.head ??= []
pageData.frontmatter.head.push([
'meta', { name: 'og:title', content: pageData.title }
])
},
// Transform head before generating each page
async transformHead(context) {
return [['meta', { name: 'custom', content: context.page }]]
},
// After build completes
async buildEnd(siteConfig) {
// Generate sitemap, RSS, etc.
}
})
```
## Dynamic Config
For async configuration:
```ts
export default async () => {
const data = await fetch('https://api.example.com/data').then(r => r.json())
return defineConfig({
title: data.title,
themeConfig: {
sidebar: data.sidebar
}
})
}
```
## Key Points
- Config file supports `.js`, `.ts`, `.mjs`, `.mts` extensions
- Use `defineConfig` for TypeScript support
- `base` must start and end with `/` for sub-path deployments
- `srcDir` separates source files from project root
- Build hooks enable custom transformations and post-processing
<!--
Source references:
- https://vitepress.dev/reference/site-config
- https://vitepress.dev/guide/getting-started
-->

View File

@@ -0,0 +1,277 @@
---
name: vitepress-markdown
description: Markdown extensions including frontmatter, custom containers, tables, anchors, and file includes
---
# Markdown Extensions
VitePress extends standard markdown with additional features for documentation.
## Frontmatter
YAML metadata at the top of markdown files:
```md
---
title: Page Title
description: Page description for SEO
layout: doc
outline: [2, 3]
---
# Content starts here
```
Access frontmatter in templates:
```md
# {{ $frontmatter.title }}
```
Or in script:
```vue
<script setup>
import { useData } from 'vitepress'
const { frontmatter } = useData()
</script>
```
## Custom Containers
Styled callout blocks:
```md
::: info
This is an info box.
:::
::: tip
This is a tip.
:::
::: warning
This is a warning.
:::
::: danger
This is a dangerous warning.
:::
::: details Click to expand
Hidden content here.
:::
```
Custom titles:
```md
::: danger STOP
Do not proceed!
:::
::: details Click me {open}
Open by default with {open} attribute.
:::
```
## GitHub-flavored Alerts
Alternative syntax using blockquotes:
```md
> [!NOTE]
> Highlights information users should know.
> [!TIP]
> Optional information for success.
> [!WARNING]
> Critical content requiring attention.
> [!CAUTION]
> Negative potential consequences.
```
## Header Anchors
Headers get automatic anchor links. Custom anchors:
```md
# My Heading {#custom-anchor}
[Link to heading](#custom-anchor)
```
## Table of Contents
Generate a TOC with:
```md
[[toc]]
```
## GitHub-Style Tables
```md
| Feature | Status |
|---------|--------|
| SSR | ✅ |
| HMR | ✅ |
```
## Emoji
Use shortcodes:
```md
:tada: :rocket: :100:
```
## File Includes
Include content from other files:
```md
<!--@include: ./shared/header.md-->
```
With line ranges:
```md
<!--@include: ./code.md{3,10}--> <!-- Lines 3-10 -->
<!--@include: ./code.md{3,}--> <!-- From line 3 -->
<!--@include: ./code.md{,10}--> <!-- Up to line 10 -->
```
With regions:
```md
<!-- In parts/basics.md -->
<!-- #region usage -->
Usage content here
<!-- #endregion usage -->
<!-- Include just that region -->
<!--@include: ./parts/basics.md#usage-->
```
## Code Snippet Import
Import code from files:
```md
<<< @/snippets/example.js
```
With line highlighting:
```md
<<< @/snippets/example.js{2,4-6}
```
With language override:
```md
<<< @/snippets/example.cs{1,2 c#:line-numbers}
```
Import specific region:
```md
<<< @/snippets/example.js#regionName{1,2}
```
## Code Groups
Tab groups for code variants:
````md
::: code-group
```js [config.js]
export default { /* ... */ }
```
```ts [config.ts]
export default defineConfig({ /* ... */ })
```
:::
````
Import files in code groups:
```md
::: code-group
<<< @/snippets/config.js
<<< @/snippets/config.ts
:::
```
## Math Equations
Requires setup:
```bash
npm add -D markdown-it-mathjax3@^4
```
```ts
// .vitepress/config.ts
export default {
markdown: {
math: true
}
}
```
Then use LaTeX:
```md
Inline: $E = mc^2$
Block:
$$
\frac{-b \pm \sqrt{b^2-4ac}}{2a}
$$
```
## Image Lazy Loading
```ts
export default {
markdown: {
image: {
lazyLoading: true
}
}
}
```
## Raw Container
Prevent VitePress style conflicts:
```md
::: raw
<CustomComponent />
:::
```
## Key Points
- Frontmatter supports YAML or JSON format
- Custom containers support info, tip, warning, danger, details
- `[[toc]]` generates table of contents
- `@` in imports refers to source root (or `srcDir` if configured)
- Code groups create tabbed code blocks
- Math support requires markdown-it-mathjax3 package
<!--
Source references:
- https://vitepress.dev/guide/markdown
- https://vitepress.dev/guide/frontmatter
-->

View File

@@ -0,0 +1,169 @@
---
name: vitepress-routing
description: File-based routing, source directory structure, clean URLs, and route rewrites
---
# Routing
VitePress uses file-based routing where markdown files map directly to HTML pages.
## File to URL Mapping
```
.
├─ index.md → /index.html (/)
├─ about.md → /about.html
├─ guide/
│ ├─ index.md → /guide/index.html (/guide/)
│ └─ getting-started.md → /guide/getting-started.html
```
## Project Structure
```
.
├─ docs # Project root
│ ├─ .vitepress # VitePress directory
│ │ ├─ config.ts # Configuration
│ │ ├─ theme/ # Custom theme
│ │ ├─ cache/ # Dev server cache (gitignore)
│ │ └─ dist/ # Build output (gitignore)
│ ├─ public/ # Static assets (copied as-is)
│ ├─ index.md # Home page
│ └─ guide/
│ └─ intro.md
```
## Source Directory
Separate source files from project root:
```ts
// .vitepress/config.ts
export default {
srcDir: './src' // Markdown files live in ./src/
}
```
With `srcDir: 'src'`:
```
.
├─ .vitepress/ # Config stays at project root
└─ src/ # Source directory
├─ index.md → /
└─ guide/intro.md → /guide/intro.html
```
## Linking Between Pages
Use relative or absolute paths. Omit file extensions:
```md
<!-- Recommended -->
[Getting Started](./getting-started)
[Guide](/guide/)
<!-- Works but not recommended -->
[Getting Started](./getting-started.md)
[Getting Started](./getting-started.html)
```
## Clean URLs
Remove `.html` extension from URLs (requires server support):
```ts
export default {
cleanUrls: true
}
```
**Server requirements:**
- Netlify, GitHub Pages: Supported by default
- Vercel: Enable `cleanUrls` in `vercel.json`
- Nginx: Configure `try_files $uri $uri.html $uri/ =404`
## Route Rewrites
Customize the mapping between source and output paths:
```ts
export default {
rewrites: {
// Static mapping
'packages/pkg-a/src/index.md': 'pkg-a/index.md',
'packages/pkg-a/src/foo.md': 'pkg-a/foo.md',
// Dynamic parameters
'packages/:pkg/src/:slug*': ':pkg/:slug*'
}
}
```
This maps `packages/pkg-a/src/intro.md``/pkg-a/intro.html`.
**Important:** Relative links in rewritten files should be based on the rewritten path, not the source path.
Rewrites can also be a function:
```ts
export default {
rewrites(id) {
return id.replace(/^packages\/([^/]+)\/src\//, '$1/')
}
}
```
## Public Directory
Files in `public/` are copied to output root as-is:
```
docs/public/
├─ favicon.ico → /favicon.ico
├─ robots.txt → /robots.txt
└─ images/logo.png → /images/logo.png
```
Reference with absolute paths:
```md
![Logo](/images/logo.png)
```
## Base URL
For sub-path deployment (e.g., GitHub Pages):
```ts
export default {
base: '/repo-name/'
}
```
All absolute paths are automatically prefixed with base. For dynamic paths in components, use `withBase`:
```vue
<script setup>
import { withBase } from 'vitepress'
</script>
<template>
<img :src="withBase('/logo.png')" />
</template>
```
## Key Points
- `index.md` files map to directory root (`/guide/` instead of `/guide/index`)
- Use paths without extensions in links for flexibility
- `srcDir` separates source from config
- `cleanUrls` removes `.html` but requires server support
- `rewrites` enables complex source structures with clean output URLs
<!--
Source references:
- https://vitepress.dev/guide/routing
- https://vitepress.dev/guide/asset-handling
-->

View File

@@ -0,0 +1,243 @@
---
name: vitepress-code-blocks
description: Syntax highlighting, line highlighting, colored diffs, focus, and line numbers
---
# Code Blocks
VitePress uses Shiki for syntax highlighting with powerful code block features.
## Syntax Highlighting
Specify language after opening backticks:
````md
```js
export default {
name: 'MyComponent'
}
```
````
Supports [all languages](https://shiki.style/languages) available in Shiki.
## Line Highlighting
Highlight specific lines:
````md
```js{4}
export default {
data() {
return {
msg: 'Highlighted!' // Line 4 highlighted
}
}
}
```
````
Multiple lines and ranges:
````md
```js{1,4,6-8}
// Line 1 highlighted
export default {
data() {
return { // Line 4
msg: 'Hi',
foo: 'bar', // Lines 6-8
baz: 'qux'
}
}
}
```
````
Inline highlighting with comment:
````md
```js
export default {
data() {
return {
msg: 'Highlighted!' // [!code highlight]
}
}
}
```
````
## Focus
Blur other code and focus specific lines:
````md
```js
export default {
data() {
return {
msg: 'Focused!' // [!code focus]
}
}
}
```
````
Focus multiple lines:
```js
// [!code focus:3]
```
## Colored Diffs
Show additions and removals:
````md
```js
export default {
data() {
return {
msg: 'Removed' // [!code --]
msg: 'Added' // [!code ++]
}
}
}
```
````
## Errors and Warnings
Color lines as errors or warnings:
````md
```js
export default {
data() {
return {
msg: 'Error', // [!code error]
msg: 'Warning' // [!code warning]
}
}
}
```
````
## Line Numbers
Enable globally:
```ts
// .vitepress/config.ts
export default {
markdown: {
lineNumbers: true
}
}
```
Per-block override:
````md
```ts:line-numbers
// Line numbers enabled
const a = 1
```
```ts:no-line-numbers
// Line numbers disabled
const b = 2
```
````
Start from specific number:
````md
```ts:line-numbers=5
// Starts at line 5
const a = 1 // This is line 5
const b = 2 // This is line 6
```
````
## Code Groups
Tabbed code blocks:
````md
::: code-group
```js [JavaScript]
export default { /* ... */ }
```
```ts [TypeScript]
export default defineConfig({ /* ... */ })
```
:::
````
## Import Code Snippets
From external files:
```md
<<< @/snippets/snippet.js
```
With highlighting:
```md
<<< @/snippets/snippet.js{2,4-6}
```
Specific region:
```md
<<< @/snippets/snippet.js#regionName{1,2}
```
With language and line numbers:
```md
<<< @/snippets/snippet.cs{1,2,4-6 c#:line-numbers}
```
In code groups:
```md
::: code-group
<<< @/snippets/config.js [JavaScript]
<<< @/snippets/config.ts [TypeScript]
:::
```
## File Labels
Add filename labels to code blocks:
````md
```js [vite.config.js]
export default defineConfig({})
```
````
## Key Points
- Use `// [!code highlight]` for inline highlighting
- Use `// [!code focus]` to focus with blur effect
- Use `// [!code ++]` and `// [!code --]` for diffs
- Use `// [!code error]` and `// [!code warning]` for status
- `:line-numbers` and `:no-line-numbers` control line numbers per block
- `@` in imports refers to source root
- Code groups create tabbed interfaces
<!--
Source references:
- https://vitepress.dev/guide/markdown#syntax-highlighting-in-code-blocks
- https://vitepress.dev/guide/markdown#line-highlighting-in-code-blocks
-->

View File

@@ -0,0 +1,220 @@
---
name: vitepress-data-loading
description: Build-time data loaders for fetching remote data or processing local files
---
# Data Loading
VitePress data loaders run at build time to load arbitrary data that's serialized as JSON in the client bundle.
## Basic Usage
Create a file ending with `.data.js` or `.data.ts`:
```ts
// example.data.ts
export default {
load() {
return {
hello: 'world',
timestamp: Date.now()
}
}
}
```
Import the `data` named export:
```vue
<script setup>
import { data } from './example.data.ts'
</script>
<template>
<pre>{{ data }}</pre>
</template>
```
## Async Data
Fetch remote data:
```ts
// api.data.ts
export default {
async load() {
const response = await fetch('https://api.example.com/data')
return response.json()
}
}
```
## Local Files with Watch
Process local files with hot reload:
```ts
// posts.data.ts
import fs from 'node:fs'
import { parse } from 'csv-parse/sync'
export default {
watch: ['./data/*.csv'],
load(watchedFiles) {
// watchedFiles = array of absolute paths
return watchedFiles.map(file => {
return parse(fs.readFileSync(file, 'utf-8'), {
columns: true,
skip_empty_lines: true
})
})
}
}
```
## createContentLoader
Helper for loading markdown content (common for blogs/archives):
```ts
// posts.data.ts
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md')
```
Returns array of `ContentData`:
```ts
interface ContentData {
url: string // e.g. /posts/hello.html
frontmatter: Record<string, any>
src?: string // raw markdown (opt-in)
html?: string // rendered HTML (opt-in)
excerpt?: string // excerpt HTML (opt-in)
}
```
With options:
```ts
// posts.data.ts
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', {
includeSrc: true, // Include raw markdown
render: true, // Include rendered HTML
excerpt: true, // Include excerpt (content before first ---)
transform(rawData) {
// Sort by date, newest first
return rawData
.sort((a, b) => +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date))
.map(page => ({
title: page.frontmatter.title,
url: page.url,
date: page.frontmatter.date,
excerpt: page.excerpt
}))
}
})
```
## Usage Example: Blog Index
```ts
// posts.data.ts
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', {
excerpt: true,
transform(data) {
return data
.filter(post => !post.frontmatter.draft)
.sort((a, b) => +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date))
}
})
```
```vue
<!-- posts/index.md -->
<script setup>
import { data as posts } from './posts.data.ts'
</script>
<template>
<ul>
<li v-for="post in posts" :key="post.url">
<a :href="post.url">{{ post.frontmatter.title }}</a>
<span>{{ post.frontmatter.date }}</span>
</li>
</ul>
</template>
```
## Typed Data Loaders
```ts
// example.data.ts
import { defineLoader } from 'vitepress'
export interface Data {
posts: Array<{ title: string; url: string }>
}
declare const data: Data
export { data }
export default defineLoader({
watch: ['./posts/*.md'],
async load(): Promise<Data> {
// ...
return { posts: [] }
}
})
```
## In Build Hooks
Use in config for generating additional files:
```ts
// .vitepress/config.ts
import { createContentLoader } from 'vitepress'
export default {
async buildEnd() {
const posts = await createContentLoader('posts/*.md').load()
// Generate RSS feed, sitemap, etc.
}
}
```
## Accessing Config
```ts
// example.data.ts
import type { SiteConfig } from 'vitepress'
export default {
load() {
const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG
return { base: config.site.base }
}
}
```
## Key Points
- Data loaders run only at build time in Node.js
- File must end with `.data.js` or `.data.ts`
- Import the `data` named export (not default)
- Use `watch` for local file hot reload during dev
- `createContentLoader` simplifies loading markdown collections
- Keep data small - it's inlined in the client bundle
- Heavy data should use `transform` to reduce payload
<!--
Source references:
- https://vitepress.dev/guide/data-loading
-->

View File

@@ -0,0 +1,235 @@
---
name: vitepress-dynamic-routes
description: Generate multiple pages from a single markdown template using paths loader files
---
# Dynamic Routes
Generate many pages from a single markdown file and dynamic data. Useful for blogs, package docs, or any data-driven pages.
## Basic Setup
Create a template file with parameter in brackets and a paths loader:
```
.
└─ packages/
├─ [pkg].md # Route template
└─ [pkg].paths.js # Paths loader
```
The paths loader exports a `paths` method returning route parameters:
```js
// packages/[pkg].paths.js
export default {
paths() {
return [
{ params: { pkg: 'foo' }},
{ params: { pkg: 'bar' }},
{ params: { pkg: 'baz' }}
]
}
}
```
Generated pages:
- `/packages/foo.html`
- `/packages/bar.html`
- `/packages/baz.html`
## Multiple Parameters
```
.
└─ packages/
├─ [pkg]-[version].md
└─ [pkg]-[version].paths.js
```
```js
// packages/[pkg]-[version].paths.js
export default {
paths() {
return [
{ params: { pkg: 'foo', version: '1.0.0' }},
{ params: { pkg: 'foo', version: '2.0.0' }},
{ params: { pkg: 'bar', version: '1.0.0' }}
]
}
}
```
## Dynamic Path Generation
From local files:
```js
// packages/[pkg].paths.js
import fs from 'node:fs'
export default {
paths() {
return fs.readdirSync('packages').map(pkg => ({
params: { pkg }
}))
}
}
```
From remote API:
```js
// packages/[pkg].paths.js
export default {
async paths() {
const packages = await fetch('https://api.example.com/packages').then(r => r.json())
return packages.map(pkg => ({
params: {
pkg: pkg.name,
version: pkg.version
}
}))
}
}
```
## Accessing Params in Page
Template globals:
```md
<!-- packages/[pkg].md -->
# Package: {{ $params.pkg }}
Version: {{ $params.version }}
```
In script:
```vue
<script setup>
import { useData } from 'vitepress'
const { params } = useData()
</script>
<template>
<h1>{{ params.pkg }}</h1>
</template>
```
## Passing Content
For heavy content (raw markdown/HTML from CMS), use `content` instead of params to avoid bloating the client bundle:
```js
// posts/[slug].paths.js
export default {
async paths() {
const posts = await fetch('https://cms.example.com/posts').then(r => r.json())
return posts.map(post => ({
params: { slug: post.slug },
content: post.content // Raw markdown or HTML
}))
}
}
```
Render content in template:
```md
<!-- posts/[slug].md -->
---
title: {{ $params.title }}
---
<!-- @content -->
```
The `<!-- @content -->` placeholder is replaced with the content from the paths loader.
## Watch Option
Auto-rebuild when template or data files change:
```js
// posts/[slug].paths.js
export default {
watch: [
'./templates/**/*.njk',
'../data/**/*.json'
],
paths(watchedFiles) {
const dataFiles = watchedFiles.filter(f => f.endsWith('.json'))
return dataFiles.map(file => {
const data = JSON.parse(fs.readFileSync(file, 'utf-8'))
return {
params: { slug: data.slug },
content: renderTemplate(data)
}
})
}
}
```
## Complete Example: Blog
```js
// posts/[slug].paths.js
import fs from 'node:fs'
import matter from 'gray-matter'
export default {
watch: ['./posts/*.md'],
paths(files) {
return files
.filter(f => !f.includes('[slug]'))
.map(file => {
const content = fs.readFileSync(file, 'utf-8')
const { data, content: body } = matter(content)
const slug = file.match(/([^/]+)\.md$/)[1]
return {
params: {
slug,
title: data.title,
date: data.date
},
content: body
}
})
}
}
```
```md
<!-- posts/[slug].md -->
---
layout: doc
---
# {{ $params.title }}
<time>{{ $params.date }}</time>
<!-- @content -->
```
## Key Points
- Template file uses `[param]` syntax in filename
- Paths loader file must be named `[param].paths.js` or `.ts`
- `paths()` returns array of `{ params: {...}, content?: string }`
- Use `$params` in templates or `useData().params` in scripts
- Use `content` for heavy data to avoid client bundle bloat
- `watch` enables HMR for template/data file changes
<!--
Source references:
- https://vitepress.dev/guide/routing#dynamic-routes
-->

View File

@@ -0,0 +1,224 @@
---
name: vue-in-vitepress-markdown
description: Using Vue components, script setup, directives, and templating in markdown files
---
# Vue in Markdown
VitePress markdown files are compiled as Vue Single-File Components, enabling full Vue functionality.
## Interpolation
Vue expressions work in markdown:
```md
{{ 1 + 1 }}
{{ new Date().toLocaleDateString() }}
```
## Directives
HTML with Vue directives:
```md
<span v-for="i in 3">{{ i }}</span>
<div v-if="$frontmatter.showBanner">
Banner content
</div>
```
## Script and Style
Add `<script setup>` and `<style>` after frontmatter:
```md
---
title: My Page
---
<script setup>
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'
const count = ref(0)
</script>
# {{ $frontmatter.title }}
Count: {{ count }}
<button @click="count++">Increment</button>
<MyComponent />
<style module>
.button {
color: red;
}
</style>
```
**Note:** Use `<style module>` instead of `<style scoped>` to avoid bloating page size.
## Importing Components
Local import (code-split per page):
```md
<script setup>
import CustomComponent from '../components/CustomComponent.vue'
</script>
<CustomComponent />
```
## Global Components
Register in theme for use everywhere:
```ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import MyGlobalComponent from './MyGlobalComponent.vue'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('MyGlobalComponent', MyGlobalComponent)
}
}
```
Then use in any markdown:
```md
<MyGlobalComponent />
```
**Important:** Component names must contain a hyphen or be PascalCase to avoid being treated as inline HTML elements.
## Runtime API
Access VitePress data:
```md
<script setup>
import { useData, useRoute, useRouter } from 'vitepress'
const { page, frontmatter, theme, site } = useData()
const route = useRoute()
const router = useRouter()
</script>
Current page: {{ page.relativePath }}
```
## Global Variables
Available without import:
```md
# {{ $frontmatter.title }}
Params: {{ $params.id }}
```
## Components in Headers
```md
# My Title <Badge type="tip" text="v2.0" />
```
## Escaping Vue Syntax
Prevent Vue interpolation:
```md
<span v-pre>{{ will be displayed as-is }}</span>
```
Or use container:
```md
::: v-pre
{{ this won't be processed }}
:::
```
## Vue in Code Blocks
Enable Vue processing in fenced code with `-vue` suffix:
````md
```js-vue
Hello {{ 1 + 1 }}
```
````
## CSS Pre-processors
Supported out of the box (install the preprocessor):
```bash
npm install -D sass # for .scss/.sass
npm install -D less # for .less
npm install -D stylus # for .styl/.stylus
```
```vue
<style lang="scss">
.title {
font-size: 20px;
}
</style>
```
## Using Teleports
Teleport to body only with SSG:
```md
<ClientOnly>
<Teleport to="#modal">
<div>Modal content</div>
</Teleport>
</ClientOnly>
```
## VS Code IntelliSense
Enable Vue language features for `.md` files:
```json
// tsconfig.json
{
"include": ["docs/**/*.ts", "docs/**/*.vue", "docs/**/*.md"],
"vueCompilerOptions": {
"vitePressExtensions": [".md"]
}
}
```
```json
// .vscode/settings.json
{
"vue.server.includeLanguages": ["vue", "markdown"]
}
```
## Key Points
- Markdown files are Vue SFCs - use `<script setup>` and `<style>`
- Access page data via `useData()` or `$frontmatter` global
- Import components locally or register globally in theme
- Use `<style module>` instead of `<style scoped>`
- Wrap non-SSR components in `<ClientOnly>`
- Component names must be PascalCase or contain hyphens
<!--
Source references:
- https://vitepress.dev/guide/using-vue
- https://vitepress.dev/reference/runtime-api
-->

View File

@@ -0,0 +1,240 @@
---
name: vitepress-deployment
description: Deploying VitePress sites to various platforms including GitHub Pages, Netlify, Vercel, and more
---
# Deployment
Deploy VitePress static sites to various hosting platforms.
## Build and Preview
```bash
# Build production files
npm run docs:build
# Preview locally
npm run docs:preview
```
Output is in `.vitepress/dist` by default.
## Setting Base Path
For sub-path deployment (e.g., `https://user.github.io/repo/`):
```ts
// .vitepress/config.ts
export default {
base: '/repo/'
}
```
## GitHub Pages
Create `.github/workflows/deploy.yml`:
```yaml
name: Deploy VitePress
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run docs:build
- uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/deploy-pages@v4
id: deployment
```
Enable GitHub Pages in repository settings → Pages → Source: "GitHub Actions".
For pnpm, add before setup-node:
```yaml
- uses: pnpm/action-setup@v4
with:
version: 9
```
## Netlify / Vercel / Cloudflare Pages
Configure in dashboard:
| Setting | Value |
|---------|-------|
| Build Command | `npm run docs:build` |
| Output Directory | `docs/.vitepress/dist` |
| Node Version | `20` (or above) |
**Warning:** Don't enable "Auto Minify" for HTML - it removes Vue hydration comments.
### Vercel Configuration
For clean URLs, add `vercel.json`:
```json
{
"cleanUrls": true
}
```
## GitLab Pages
Create `.gitlab-ci.yml`:
```yaml
image: node:18
pages:
cache:
paths:
- node_modules/
script:
- npm install
- npm run docs:build
artifacts:
paths:
- public
only:
- main
```
Set `outDir: '../public'` in config if needed.
## Firebase
```json
// firebase.json
{
"hosting": {
"public": "docs/.vitepress/dist",
"ignore": []
}
}
```
```bash
npm run docs:build
firebase deploy
```
## Nginx
```nginx
server {
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
listen 80;
server_name _;
index index.html;
location / {
root /app;
try_files $uri $uri.html $uri/ =404;
error_page 404 /404.html;
error_page 403 /404.html;
}
# Cache hashed assets
location ~* ^/assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
```
**Important:** Don't default to `index.html` like SPAs - use `$uri.html` for clean URLs.
## HTTP Cache Headers
For hashed assets (immutable):
```
Cache-Control: max-age=31536000, immutable
```
### Netlify `_headers`
Place in `docs/public/_headers`:
```
/assets/*
cache-control: max-age=31536000
cache-control: immutable
```
### Vercel `vercel.json`
```json
{
"headers": [
{
"source": "/assets/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=31536000, immutable"
}
]
}
]
}
```
## Other Platforms
| Platform | Guide |
|----------|-------|
| Azure Static Web Apps | Set `app_location: /`, `output_location: docs/.vitepress/dist` |
| Surge | `npx surge docs/.vitepress/dist` |
| Heroku | Use `heroku-buildpack-static` |
| Render | Build: `npm run docs:build`, Publish: `docs/.vitepress/dist` |
| Kinsta | Follow [Kinsta docs](https://kinsta.com/docs/vitepress-static-site-example/) |
## Key Points
- Set `base` for sub-path deployments
- GitHub Pages requires workflow file and enabling Pages in settings
- Most platforms: Build `npm run docs:build`, output `docs/.vitepress/dist`
- Don't enable HTML minification (breaks hydration)
- Cache `/assets/*` with immutable headers
- For clean URLs on Nginx, use `try_files $uri $uri.html $uri/ =404`
<!--
Source references:
- https://vitepress.dev/guide/deploy
-->

View File

@@ -0,0 +1,315 @@
---
name: vitepress-theme-configuration
description: Default theme configuration for navigation, sidebar, search, social links, and footer
---
# Theme Configuration
Configure the default theme via `themeConfig` in your VitePress config.
## Navigation
```ts
export default {
themeConfig: {
// Site title in nav (overrides config.title)
siteTitle: 'My Docs',
siteTitle: false, // Hide title
// Logo
logo: '/logo.svg',
logo: { light: '/light-logo.svg', dark: '/dark-logo.svg', alt: 'Logo' },
// Nav links
nav: [
{ text: 'Guide', link: '/guide/' },
{ text: 'API', link: '/api/' },
{ text: 'GitHub', link: 'https://github.com/...' }
]
}
}
```
### Dropdown Menu
```ts
nav: [
{
text: 'Dropdown',
items: [
{ text: 'Item A', link: '/item-a' },
{ text: 'Item B', link: '/item-b' }
]
},
// With sections
{
text: 'Versions',
items: [
{
text: 'v2.x',
items: [
{ text: 'v2.0', link: '/v2/' },
{ text: 'v2.1', link: '/v2.1/' }
]
}
]
}
]
```
### Active Match
Control when nav item shows as active:
```ts
nav: [
{
text: 'Guide',
link: '/guide/',
activeMatch: '/guide/' // Regex pattern
}
]
```
## Sidebar
### Simple Sidebar
```ts
sidebar: [
{
text: 'Guide',
items: [
{ text: 'Introduction', link: '/guide/' },
{ text: 'Getting Started', link: '/guide/getting-started' }
]
}
]
```
### Multiple Sidebars
Different sidebar per section:
```ts
sidebar: {
'/guide/': [
{
text: 'Guide',
items: [
{ text: 'Introduction', link: '/guide/' },
{ text: 'Getting Started', link: '/guide/getting-started' }
]
}
],
'/api/': [
{
text: 'API Reference',
items: [
{ text: 'Config', link: '/api/config' },
{ text: 'Methods', link: '/api/methods' }
]
}
]
}
```
### Collapsible Groups
```ts
sidebar: [
{
text: 'Section A',
collapsed: false, // Open by default, can collapse
items: [...]
},
{
text: 'Section B',
collapsed: true, // Collapsed by default
items: [...]
}
]
```
### Base Path
Simplify links with common base:
```ts
sidebar: {
'/guide/': {
base: '/guide/',
items: [
{ text: 'Intro', link: 'intro' }, // /guide/intro
{ text: 'Setup', link: 'getting-started' } // /guide/getting-started
]
}
}
```
## Search
### Local Search
```ts
themeConfig: {
search: {
provider: 'local'
}
}
```
With options:
```ts
search: {
provider: 'local',
options: {
miniSearch: {
searchOptions: {
fuzzy: 0.2,
prefix: true
}
}
}
}
```
### Algolia DocSearch
```ts
search: {
provider: 'algolia',
options: {
appId: 'YOUR_APP_ID',
apiKey: 'YOUR_API_KEY',
indexName: 'YOUR_INDEX_NAME'
}
}
```
## Social Links
```ts
socialLinks: [
{ icon: 'github', link: 'https://github.com/...' },
{ icon: 'twitter', link: 'https://twitter.com/...' },
{ icon: 'discord', link: 'https://discord.gg/...' },
// Custom SVG
{
icon: { svg: '<svg>...</svg>' },
link: 'https://...',
ariaLabel: 'Custom Link'
}
]
```
## Footer
```ts
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2024 My Project'
}
```
Footer only displays on pages without sidebar.
## Edit Link
```ts
editLink: {
pattern: 'https://github.com/org/repo/edit/main/docs/:path',
text: 'Edit this page on GitHub'
}
```
`:path` is replaced with the page's source file path.
## Last Updated
Enable in site config:
```ts
export default {
lastUpdated: true // Get timestamp from git
}
```
Customize display:
```ts
themeConfig: {
lastUpdated: {
text: 'Updated at',
formatOptions: {
dateStyle: 'full',
timeStyle: 'medium'
}
}
}
```
## Outline (Table of Contents)
```ts
outline: {
level: [2, 3], // Which heading levels to show
label: 'On this page'
}
```
Or just the level:
```ts
outline: 'deep' // Same as [2, 6]
outline: 2 // Only h2
outline: [2, 4] // h2 through h4
```
## Doc Footer Navigation
```ts
docFooter: {
prev: 'Previous page',
next: 'Next page'
}
// Or disable:
docFooter: {
prev: false,
next: false
}
```
## External Link Icon
```ts
externalLinkIcon: true // Show icon on external links
```
## Appearance Toggle Labels
```ts
darkModeSwitchLabel: 'Appearance',
lightModeSwitchTitle: 'Switch to light theme',
darkModeSwitchTitle: 'Switch to dark theme',
sidebarMenuLabel: 'Menu',
returnToTopLabel: 'Return to top'
```
## Key Points
- `nav` defines top navigation links
- `sidebar` can be array (single) or object (multiple sidebars)
- Use `collapsed` for collapsible sidebar sections
- Local search works out of the box
- `editLink.pattern` uses `:path` placeholder
- Enable `lastUpdated` in site config, customize in themeConfig
<!--
Source references:
- https://vitepress.dev/reference/default-theme-config
- https://vitepress.dev/reference/default-theme-nav
- https://vitepress.dev/reference/default-theme-sidebar
- https://vitepress.dev/reference/default-theme-search
-->

View File

@@ -0,0 +1,269 @@
---
name: vitepress-custom-themes
description: Building custom themes from scratch with the theme interface, Layout component, and enhanceApp
---
# Custom Themes
Build a theme from scratch when the default theme doesn't fit your needs.
## Theme Entry
Create `.vitepress/theme/index.ts`:
```ts
// .vitepress/theme/index.ts
import Layout from './Layout.vue'
export default {
Layout,
enhanceApp({ app, router, siteData }) {
// Register global components, plugins, etc.
}
}
```
## Theme Interface
```ts
interface Theme {
// Required: Root layout component
Layout: Component
// Optional: Enhance Vue app instance
enhanceApp?: (ctx: EnhanceAppContext) => Awaitable<void>
// Optional: Extend another theme
extends?: Theme
}
interface EnhanceAppContext {
app: App // Vue app instance
router: Router // VitePress router
siteData: Ref<SiteData> // Site-level metadata
}
```
## Basic Layout
The Layout component must render `<Content />` for markdown:
```vue
<!-- .vitepress/theme/Layout.vue -->
<script setup>
import { useData } from 'vitepress'
const { page, frontmatter } = useData()
</script>
<template>
<div class="layout">
<header>
<nav>My Site</nav>
</header>
<main>
<div v-if="page.isNotFound">
<h1>404 - Page Not Found</h1>
</div>
<div v-else-if="frontmatter.layout === 'home'">
<h1>Welcome!</h1>
</div>
<article v-else>
<Content />
</article>
</main>
<footer>
<p>© 2024 My Site</p>
</footer>
</div>
</template>
```
## Runtime API
Access VitePress data in your theme:
```vue
<script setup>
import { useData, useRoute, useRouter } from 'vitepress'
// Page and site data
const {
site, // Site config (title, description, etc.)
theme, // Theme config
page, // Current page data
frontmatter, // Current page frontmatter
title, // Page title
description, // Page description
lang, // Current language
isDark, // Dark mode state
params // Dynamic route params
} = useData()
// Routing
const route = useRoute()
const router = useRouter()
// Navigate programmatically
const goToGuide = () => router.go('/guide/')
</script>
```
## Built-in Components
```vue
<script setup>
import { Content } from 'vitepress'
</script>
<template>
<!-- Renders markdown content -->
<Content />
<!-- Renders slot only on client (SSR-safe) -->
<ClientOnly>
<NonSSRComponent />
</ClientOnly>
</template>
```
## Extend Another Theme
Build on top of default theme or any other:
```ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
// Your customizations
}
}
```
## Register Plugins and Components
```ts
// .vitepress/theme/index.ts
import Layout from './Layout.vue'
import GlobalComponent from './GlobalComponent.vue'
export default {
Layout,
enhanceApp({ app }) {
// Register global component
app.component('GlobalComponent', GlobalComponent)
// Register plugin
app.use(MyPlugin)
// Provide/inject
app.provide('key', value)
}
}
```
## Async enhanceApp
For plugins that need async initialization:
```ts
export default {
Layout,
async enhanceApp({ app }) {
if (!import.meta.env.SSR) {
// Client-only plugin
const plugin = await import('browser-only-plugin')
app.use(plugin.default)
}
}
}
```
## Theme-Aware Layout
Handle different page layouts:
```vue
<script setup>
import { useData } from 'vitepress'
import Home from './Home.vue'
import Doc from './Doc.vue'
import Page from './Page.vue'
import NotFound from './NotFound.vue'
const { page, frontmatter } = useData()
</script>
<template>
<NotFound v-if="page.isNotFound" />
<Home v-else-if="frontmatter.layout === 'home'" />
<Page v-else-if="frontmatter.layout === 'page'" />
<Doc v-else />
</template>
```
## Distributing a Theme
As npm package:
```ts
// my-theme/index.ts
import Layout from './Layout.vue'
export default { Layout }
// Export types for config
export type { ThemeConfig } from './types'
```
Consumer usage:
```ts
// .vitepress/theme/index.ts
import Theme from 'my-vitepress-theme'
export default Theme
// Or extend it
export default {
extends: Theme,
enhanceApp({ app }) {
// Additional customization
}
}
```
## Theme Config Types
For custom theme config types:
```ts
// .vitepress/config.ts
import { defineConfigWithTheme } from 'vitepress'
import type { ThemeConfig } from 'my-theme'
export default defineConfigWithTheme<ThemeConfig>({
themeConfig: {
// Type-checked theme config
}
})
```
## Key Points
- Theme must export `Layout` component
- `<Content />` renders the markdown content
- Use `useData()` to access page/site data
- `enhanceApp` runs on both server and client
- Check `import.meta.env.SSR` for client-only code
- Use `extends` to build on existing themes
<!--
Source references:
- https://vitepress.dev/guide/custom-theme
-->

View File

@@ -0,0 +1,290 @@
---
name: extending-vitepress-default-theme
description: Customize CSS variables, use layout slots, register global components, and override theme fonts
---
# Extending Default Theme
Customize the default theme through CSS, slots, and Vue components.
## Theme Entry File
Create `.vitepress/theme/index.ts` to extend the default theme:
```ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default DefaultTheme
```
## CSS Variables
Override root CSS variables:
```css
/* .vitepress/theme/custom.css */
:root {
/* Brand colors */
--vp-c-brand-1: #646cff;
--vp-c-brand-2: #747bff;
--vp-c-brand-3: #9499ff;
/* Backgrounds */
--vp-c-bg: #ffffff;
--vp-c-bg-soft: #f6f6f7;
/* Text */
--vp-c-text-1: #213547;
--vp-c-text-2: #476582;
}
.dark {
--vp-c-brand-1: #747bff;
--vp-c-bg: #1a1a1a;
}
```
See [all CSS variables](https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css).
## Home Hero Customization
```css
:root {
/* Gradient name color */
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: linear-gradient(120deg, #bd34fe, #41d1ff);
/* Hero image glow */
--vp-home-hero-image-background-image: linear-gradient(-45deg, #bd34fe 50%, #47caff 50%);
--vp-home-hero-image-filter: blur(44px);
}
```
## Custom Fonts
Remove Inter font and use your own:
```ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme-without-fonts'
import './fonts.css'
export default DefaultTheme
```
```css
/* .vitepress/theme/fonts.css */
@font-face {
font-family: 'MyFont';
src: url('/fonts/myfont.woff2') format('woff2');
}
:root {
--vp-font-family-base: 'MyFont', sans-serif;
--vp-font-family-mono: 'Fira Code', monospace;
}
```
Preload fonts in config:
```ts
// .vitepress/config.ts
export default {
transformHead({ assets }) {
const fontFile = assets.find(file => /myfont\.[\w-]+\.woff2/.test(file))
if (fontFile) {
return [
['link', { rel: 'preload', href: fontFile, as: 'font', type: 'font/woff2', crossorigin: '' }]
]
}
}
}
```
## Global Components
Register components available in all markdown:
```ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import MyComponent from './components/MyComponent.vue'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('MyComponent', MyComponent)
}
}
```
Use in markdown:
```md
<MyComponent :prop="value" />
```
## Layout Slots
Inject content into specific locations:
```ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import MyLayout from './MyLayout.vue'
export default {
extends: DefaultTheme,
Layout: MyLayout
}
```
```vue
<!-- .vitepress/theme/MyLayout.vue -->
<script setup>
import DefaultTheme from 'vitepress/theme'
const { Layout } = DefaultTheme
</script>
<template>
<Layout>
<template #aside-outline-before>
<div>Above outline</div>
</template>
<template #doc-before>
<div>Before doc content</div>
</template>
<template #doc-after>
<div>After doc content</div>
</template>
</Layout>
</template>
```
### Available Slots
**Doc layout (`layout: doc`):**
- `doc-top`, `doc-bottom`
- `doc-before`, `doc-after`
- `doc-footer-before`
- `sidebar-nav-before`, `sidebar-nav-after`
- `aside-top`, `aside-bottom`
- `aside-outline-before`, `aside-outline-after`
- `aside-ads-before`, `aside-ads-after`
**Home layout (`layout: home`):**
- `home-hero-before`, `home-hero-after`
- `home-hero-info-before`, `home-hero-info`, `home-hero-info-after`
- `home-hero-actions-after`, `home-hero-image`
- `home-features-before`, `home-features-after`
**Page layout (`layout: page`):**
- `page-top`, `page-bottom`
**Always available:**
- `layout-top`, `layout-bottom`
- `nav-bar-title-before`, `nav-bar-title-after`
- `nav-bar-content-before`, `nav-bar-content-after`
- `not-found` (404 page)
## Using Render Functions
Alternative to template slots:
```ts
// .vitepress/theme/index.ts
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import MyComponent from './MyComponent.vue'
export default {
extends: DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'aside-outline-before': () => h(MyComponent)
})
}
}
```
## Override Internal Components
Replace default theme components with Vite aliases:
```ts
// .vitepress/config.ts
import { fileURLToPath, URL } from 'node:url'
export default {
vite: {
resolve: {
alias: [
{
find: /^.*\/VPNavBar\.vue$/,
replacement: fileURLToPath(
new URL('./theme/components/CustomNavBar.vue', import.meta.url)
)
}
]
}
}
}
```
## View Transitions
Custom dark mode toggle animation:
```vue
<!-- .vitepress/theme/Layout.vue -->
<script setup>
import { useData } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import { nextTick, provide } from 'vue'
const { isDark } = useData()
provide('toggle-appearance', async ({ clientX: x, clientY: y }) => {
if (!document.startViewTransition) {
isDark.value = !isDark.value
return
}
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y))}px at ${x}px ${y}px)`
]
await document.startViewTransition(async () => {
isDark.value = !isDark.value
await nextTick()
}).ready
document.documentElement.animate(
{ clipPath: isDark.value ? clipPath.reverse() : clipPath },
{ duration: 300, easing: 'ease-in', pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)` }
)
})
</script>
<template>
<DefaultTheme.Layout />
</template>
```
## Key Points
- Import `vitepress/theme-without-fonts` to use custom fonts
- Use layout slots to inject content without overriding components
- Global components are registered in `enhanceApp`
- Override CSS variables for theming
- Use Vite aliases to replace internal components
<!--
Source references:
- https://vitepress.dev/guide/extending-default-theme
-->