Appearance
VitePress Sites
Partner Documentation
VitePress is a supported framework for DCS sites. This guide covers VitePress-specific integration using the official @duffcloudservices/cms package.
Why VitePress?
VitePress offers:
- Fast — Built on Vite, near-instant hot reload
- Simple — Markdown-based content
- Flexible — Vue components for custom features
- SEO-friendly — Static generation with good defaults
Project Structure
A typical DCS VitePress site:
my-site/
├── .dcs/
│ ├── site.yaml # Site identity + Azure/deploy config
│ ├── pages.yaml # Managed page registry
│ ├── content.yaml # Portal-managed text content
│ └── seo.yaml # Portal-managed SEO
├── .github/
│ ├── copilot-instructions.md
│ └── workflows/
│ └── site-deploy.yml # Generated by `dcs init` (OIDC deploy)
├── .vitepress/
│ ├── config.ts
│ └── theme/
│ └── index.ts
├── docs/
│ ├── index.md
│ └── about.md
├── public/
│ └── images/
├── .env
└── package.jsonInstallation
1. Create a VitePress Project
bash
pnpm create vitepress2. Initialize DCS Integration
Use the DCS CLI to scaffold the .dcs config, the deployment workflow, and the integration plans:
bash
npm install -g @duffcloudservices/cli
dcs login
dcs init -s my-site -n "My Site"3. Install the CMS Package
bash
pnpm add @duffcloudservices/cmsConfigure the Vite Plugins
Register the DCS content and SEO plugins in your VitePress config. They inject .dcs/content.yaml and .dcs/seo.yaml at build time, so managed text and SEO ship with the built site at zero runtime cost:
typescript
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
import { dcsContentPlugin, dcsSeoPlugin } from '@duffcloudservices/cms/plugins'
export default defineConfig({
title: 'My Site',
description: 'A DCS-powered site',
vite: {
plugins: [
dcsContentPlugin(),
dcsSeoPlugin()
]
}
})Text Content Integration
Use the useTextContent composable for editable copy. Pass hardcoded defaults so the page renders correctly before any portal edits exist:
vue
<!-- .vitepress/theme/components/Hero.vue -->
<script setup lang="ts">
import { useTextContent } from '@duffcloudservices/cms'
const { t } = useTextContent({
pageSlug: 'home',
defaults: {
'hero.title': 'Welcome',
'hero.subtitle': 'This is the default subtitle.'
}
})
</script>
<template>
<section class="hero">
<h1>{{ t('hero.title') }}</h1>
<p>{{ t('hero.subtitle') }}</p>
</section>
</template>Register the component in your theme and use it in markdown:
typescript
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import type { Theme } from 'vitepress'
import Hero from './components/Hero.vue'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('Hero', Hero)
}
} satisfies ThemeText falls back through: runtime API overrides (runtime mode only) → build-time content from .dcs/content.yaml → the defaults you pass to the composable.
SEO Integration
Apply portal-managed SEO with the useSEO composable:
vue
<script setup lang="ts">
import { useSEO } from '@duffcloudservices/cms'
const { applyHead } = useSEO('home', '/')
applyHead()
</script>applyHead sets the meta tags, Open Graph, Twitter Cards, canonical URL, and JSON-LD schemas defined in .dcs/seo.yaml.
Environment Variables
Managed text and SEO are injected at build time, so a standard (commit-mode) site needs no runtime API configuration. The only variables you may set relate to runtime overrides:
bash
# .env
# For runtime overrides (premium tier): the API base URL only.
VITE_API_BASE_URL=https://portal.duffcloudservices.com
VITE_TEXT_OVERRIDE_MODE=commit # 'commit' (default) or 'runtime'The site slug is resolved server-side (from the request Host, or the dedicated Container App's DCS_SITE_SLUG) and is never placed in request URLs.
Deployment
dcs init generates .github/workflows/site-deploy.yml, which deploys to Azure Static Web Apps using OpenID Connect (OIDC) — there is no long-lived Static Web App token stored in the repository. The workflow authenticates with azure/login@v2, exchanges the OIDC token with the DCS API for a short-lived deployment token, builds, and deploys.
You do not hand-write this workflow. See GitHub Setup for the OIDC federated-credential setup and Deployment for the full flow.
Best Practices
Performance
- Use
content-visibility: autofor long pages - Lazy load images below the fold
- Minimize custom JavaScript
SEO
- Set portal SEO for each managed page
- Use semantic HTML and proper heading hierarchy
- Include alt text for images
Accessibility
- Use a correct heading hierarchy
- Ensure sufficient color contrast
- Test with keyboard navigation
Troubleshooting
Text Not Updating
- Confirm the DCS plugins are registered in
.vitepress/config.ts - Rebuild the site — commit-mode content is injected at build time
- In commit mode, edits appear only after the site is redeployed
Build Failures
- Check for missing dependencies
- Verify
.dcs/content.yamland.dcs/seo.yamlare valid - Run
dcs validate
Hot Reload Issues
- Clear the Vite cache:
rm -rf node_modules/.vite - Restart the dev server
- Check for syntax errors
Next Steps
- Configuration Files — Detailed config reference
- GitHub Setup — Repository and OIDC configuration
- Text Content API — Runtime override API
