# VitePress Configuration Changes

Exact changes needed in `website/.vitepress/config.mts` to integrate all branding assets.

## Current State

The config currently has:

- One favicon reference: `['link', { rel: 'icon', href: '/favicon.ico' }]` (file missing)
- OG tags for `og:type`, `og:title`, `og:description` (no `og:image`)
- No `twitter:card` tags
- No `themeConfig.logo`
- Dynamic per-page OG in `transformPageData` (no image there either)

## Changes

### 1. Replace Favicon Head Tag

**Current** (line 17):

```typescript
['link', { rel: 'icon', href: '/favicon.ico' }],
```

**Replace with**:

```typescript
['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }],
['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' }],
['link', { rel: 'icon', href: '/favicon.ico' }],
['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }],
['link', { rel: 'manifest', href: '/site.webmanifest' }],
['meta', { name: 'theme-color', content: '#1a1a19' }],
```

### 2. Add Fallback OG Image to Static Head Tags

The static head tags provide a fallback OG image. Per-page images are injected dynamically in `transformHead` (see section 5).

**Add after existing OG tags** (after line 41):

```typescript
['meta', { property: 'og:image', content: 'https://claudelint.com/og-image.png' }],
['meta', { property: 'og:image:width', content: '1200' }],
['meta', { property: 'og:image:height', content: '630' }],
['meta', { property: 'og:image:alt', content: 'claudelint - The linter for Claude Code' }],
['meta', { property: 'og:site_name', content: 'claudelint' }],
```

### 3. Add Twitter Card Tags

**Add after OG tags**:

```typescript
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
```

Note: `twitter:image` is injected per-page in `transformHead` (section 5) to match the per-page OG image.

### 4. Add Logo to Theme Config

**Add to `themeConfig` object** (around line 44):

```typescript
themeConfig: {
  logo: {
    light: '/logo.svg',
    dark: '/logo-dark.svg',
  },
  // ... rest of themeConfig
}
```

This displays the logo inline next to the "claudelint" site title in the nav bar. VitePress handles swapping light/dark variants based on the active color scheme.

### 5. Add Per-Page OG Image to `transformHead`

Use `transformHead` (not `transformPageData`) to inject per-page OG image paths. The slug is derived from the page's relative path to match the filenames generated by `scripts/generate-og-images.ts`.

**Add new `transformHead` hook** (or merge into existing `transformPageData`):

```typescript
transformPageData(pageData) {
  const title = pageData.frontmatter.title || pageData.title;
  const description =
    pageData.frontmatter.description || pageData.description || 'claudelint documentation';
  const canonicalUrl = `https://claudelint.com/${pageData.relativePath}`
    .replace(/index\.md$/, '')
    .replace(/\.md$/, '');

  // Generate OG image slug from relative path
  // e.g., "guide/getting-started.md" -> "guide-getting-started"
  // e.g., "index.md" -> use fallback
  const slug = pageData.relativePath
    .replace(/\.md$/, '')
    .replace(/\/index$/, '')
    .replace(/\//g, '-');

  const ogImageUrl = slug
    ? `https://claudelint.com/og/${slug}.png`
    : 'https://claudelint.com/og-image.png';

  pageData.frontmatter.head ??= [];
  pageData.frontmatter.head.push(
    ['meta', { property: 'og:title', content: `${title} | claudelint` }],
    ['meta', { property: 'og:description', content: description }],
    ['meta', { property: 'og:url', content: canonicalUrl }],
    ['meta', { property: 'og:image', content: ogImageUrl }],
    ['meta', { property: 'og:image:alt', content: `${title} | claudelint` }],
    ['meta', { name: 'twitter:image', content: ogImageUrl }],
    ['link', { rel: 'canonical', href: canonicalUrl }]
  );
},
```

**Slug mapping examples**:

| Page path | Slug | OG image URL |
|-----------|------|-------------|
| `index.md` | (empty) | `/og-image.png` (static fallback) |
| `guide/getting-started.md` | `guide-getting-started` | `/og/guide-getting-started.png` |
| `rules/skills/skill-name.md` | `rules-skills-skill-name` | `/og/rules-skills-skill-name.png` |
| `api/overview.md` | `api-overview` | `/og/api-overview.png` |

### 6. Add `buildEnd` Hook for OG Image Generation

Wire the generation script into VitePress's `buildEnd` hook so images are generated after SSG:

```typescript
import { generateOgImages } from '../../scripts/generate-og-images';

export default defineConfig({
  // ... existing config ...

  async buildEnd(siteConfig) {
    await generateOgImages(siteConfig);
  },
});
```

Alternatively, run the generation as a separate build step in `package.json`:

```json
{
  "scripts": {
    "docs:build": "npm run docs:generate && vitepress build website && npm run generate:og",
    "generate:og": "tsx scripts/generate-og-images.ts"
  }
}
```

The second approach (separate script) is simpler to debug and test independently.

---

## Full Head Array (After All Changes)

For reference, the complete static `head` array. Per-page OG tags (`og:image`, `twitter:image`, `og:image:alt`) are injected dynamically in `transformPageData` and override these fallbacks.

```typescript
head: [
  // Favicons
  ['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }],
  ['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' }],
  ['link', { rel: 'icon', href: '/favicon.ico' }],
  ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }],
  ['link', { rel: 'manifest', href: '/site.webmanifest' }],
  ['meta', { name: 'theme-color', content: '#1a1a19' }],

  // Fonts
  ['link', { rel: 'preconnect', href: 'https://fonts.googleapis.com' }],
  ['link', { rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: '' }],
  [
    'link',
    {
      rel: 'stylesheet',
      href: 'https://fonts.googleapis.com/css2?family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,500;0,8..60,600;1,8..60,400;1,8..60,500&display=swap',
    },
  ],

  // Open Graph (fallback — per-page values injected in transformPageData)
  ['meta', { property: 'og:type', content: 'website' }],
  ['meta', { property: 'og:title', content: 'claudelint - Lint your Claude Code projects' }],
  ['meta', { property: 'og:description', content: 'Validate CLAUDE.md, skills, settings, hooks, MCP servers, and plugins' }],
  ['meta', { property: 'og:image', content: 'https://claudelint.com/og-image.png' }],
  ['meta', { property: 'og:image:width', content: '1200' }],
  ['meta', { property: 'og:image:height', content: '630' }],
  ['meta', { property: 'og:image:alt', content: 'claudelint - The linter for Claude Code' }],
  ['meta', { property: 'og:site_name', content: 'claudelint' }],

  // Twitter Card
  ['meta', { name: 'twitter:card', content: 'summary_large_image' }],
],
```

---

## Verification Checklist

After applying changes:

- [ ] `npm run docs:build` — no errors
- [ ] `npm run docs:dev` — favicon appears in browser tab
- [ ] `npm run docs:dev` — logo appears in nav bar (light mode)
- [ ] `npm run docs:dev` — logo appears in nav bar (dark mode)
- [ ] View page source on homepage — fallback `og:image` present
- [ ] View page source on subpage — per-page `og:image` with `/og/<slug>.png` path
- [ ] View page source on subpage — `twitter:image` matches `og:image`
- [ ] Verify `og/` directory in build output contains ~170 PNG files
- [ ] Spot-check a generated PNG opens correctly and looks right
