Files
Jason Woltje f5792c40be 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>
2026-02-16 16:27:42 -06:00

5.0 KiB

name, description
name description
vitepress-custom-themes 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:

// .vitepress/theme/index.ts
import Layout from './Layout.vue'

export default {
  Layout,
  enhanceApp({ app, router, siteData }) {
    // Register global components, plugins, etc.
  }
}

Theme Interface

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:

<!-- .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:

<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

<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:

// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'

export default {
  extends: DefaultTheme,
  enhanceApp({ app }) {
    // Your customizations
  }
}

Register Plugins and Components

// .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:

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:

<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:

// my-theme/index.ts
import Layout from './Layout.vue'
export default { Layout }

// Export types for config
export type { ThemeConfig } from './types'

Consumer usage:

// .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:

// .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