- Introduced a new document outlining the configuration and component patterns for integrating @nuxt/content. - Included mappings for `nuxt.config.ts`, `content.config.ts`, and new components `ProseImg.vue` and `Alert.vue`. - Added example markdown content for testing syntax highlighting and layout. - Documented critical patterns and anti-patterns to follow during implementation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.9 KiB
Phase 5: @nuxt/content Setup & Renderer — Pattern Map
Mapped: 2026-04-21 Files analyzed: 7 (2 modifications + 5 créations) Analogs found: 5 / 7
File Classification
| New/Modified File | Role | Data Flow | Closest Analog | Match Quality |
|---|---|---|---|---|
nuxt.config.ts |
config | — | nuxt.config.ts (lui-même, existant) |
exact — extension |
content.config.ts |
config | CRUD | nuxt.config.ts (structure defineNuxtConfig) |
role-match |
app/assets/css/main.css |
config | — | app/assets/css/main.css (lui-même, existant) |
exact — extension |
app/components/content/ProseImg.vue |
component | request-response | app/components/ProjectCard.vue (NuxtImg + Props interface) |
role-match |
app/components/content/Alert.vue |
component | request-response | app/components/TechBadge.vue (withDefaults + UBadge + computed map) |
role-match |
content/fr/blog/test-kotlin-syntax.md |
content | — | aucun | no-analog |
content/en/blog/test-kotlin-syntax.md |
content | — | aucun | no-analog |
Pattern Assignments
nuxt.config.ts (config — extension)
Analog: nuxt.config.ts lui-même (ligne 1–65)
État actuel (lignes 7–14) :
modules: [
'@nuxt/ui',
'@nuxtjs/i18n',
'@nuxt/eslint',
'@nuxtjs/sitemap',
'nuxt-gtag',
'@nuxt/image'
],
Pattern à ajouter — ajout dans modules :
modules: [
'@nuxt/ui',
'@nuxtjs/i18n',
'@nuxt/eslint',
'@nuxtjs/sitemap',
'nuxt-gtag',
'@nuxt/image',
'@nuxt/content' // ← ajouter ici
],
Pattern à ajouter — bloc content après les modules existants :
content: {
build: {
markdown: {
highlight: {
theme: {
default: 'github-light',
dark: 'github-dark'
},
langs: ['kotlin', 'java', 'typescript', 'shell', 'bash', 'json', 'vue', 'html', 'css']
}
}
},
experimental: {
sqliteConnector: 'native' // Node 22+ — pas de better-sqlite3
}
},
Contexte critique : colorMode.classSuffix: '' est déjà configuré ligne 29 — Shiki dual-theme fonctionne via html.dark, donc compatible sans modification.
content.config.ts (config — création, racine du projet)
Analog: Structure nuxt.config.ts (pattern defineNuxtConfig → defineContentConfig)
Pattern complet (source: RESEARCH.md Pattern 2) :
import { defineContentConfig, defineCollection, z } from '@nuxt/content'
const blogSchema = z.object({
title: z.string(),
description: z.string(),
date: z.string(),
tags: z.array(z.string()).optional(),
image: z.string().optional(),
})
export default defineContentConfig({
collections: {
blog_fr: defineCollection({
type: 'page',
source: { include: 'fr/blog/**/*.md', prefix: '/blog' },
schema: blogSchema,
}),
blog_en: defineCollection({
type: 'page',
source: { include: 'en/blog/**/*.md', prefix: '/en/blog' },
schema: blogSchema,
}),
},
})
Note sur le prefix : i18n.strategy: 'prefix_except_default' avec defaultLocale: 'fr' → les articles FR sont sous /blog/slug, les EN sous /en/blog/slug. (Assumption A3 de RESEARCH.md — valider avec l'article de test.)
app/assets/css/main.css (config — extension)
Analog: app/assets/css/main.css lui-même (lignes 1–3, existant)
État actuel (lignes 1–3) :
@import "tailwindcss";
@import "@nuxt/ui";
Pattern à ajouter — une ligne après @import "@nuxt/ui" :
@import "tailwindcss";
@import "@nuxt/ui";
@plugin "@tailwindcss/typography"; /* ← ajouter ici — syntaxe Tailwind v4 obligatoire */
Anti-pattern à éviter : Ne pas utiliser plugins: [require('@tailwindcss/typography')] dans tailwind.config.js — ignoré en Tailwind v4.
app/components/content/ProseImg.vue (component, request-response)
Analog: app/components/ProjectCard.vue — utilisation de NuxtImg avec Props interface (lignes 1–16, 25–35)
Imports pattern (depuis ProjectCard.vue, lignes 1–3) :
<script setup lang="ts">
// Pas d'import externe — NuxtImg est auto-importé par @nuxt/image
Props pattern (depuis ProjectCard.vue, lignes 4–8) :
interface Props {
project: Project
}
const props = defineProps<Props>()
NuxtImg pattern (depuis ProjectCard.vue, lignes 26–35) :
<NuxtImg
:src="project.image"
:alt="`...`"
loading="lazy"
format="webp"
width="400"
height="300"
class="w-full h-52 object-cover"
/>
Pattern cible pour ProseImg.vue (adapté avec withDefaults — depuis TechBadge.vue ligne 11) :
<script setup lang="ts">
interface Props {
src: string
alt?: string
title?: string
width?: string | number
height?: string | number
}
const props = withDefaults(defineProps<Props>(), {
alt: '',
})
</script>
<template>
<NuxtImg
:src="props.src"
:alt="props.alt"
:title="props.title"
:width="props.width"
:height="props.height"
class="rounded-lg w-full"
sizes="sm:600px md:800px lg:1000px"
/>
</template>
app/components/content/Alert.vue (component, request-response)
Analog: app/components/TechBadge.vue — withDefaults + computed map de valeurs + composant Nuxt UI (UBadge) (lignes 1–57)
withDefaults pattern (depuis TechBadge.vue, lignes 11–13) :
const props = withDefaults(defineProps<Props>(), {
showLevel: true,
showImage: true,
})
Computed map pattern (depuis TechBadge.vue, lignes 44–56) :
const levelColor = computed(() => {
switch (techData.value.level) {
case 'Advanced': return 'success' as const
case 'Intermediate': return 'primary' as const
default: return 'neutral' as const
}
})
Pattern cible pour Alert.vue (adapté avec UAlert Nuxt UI + ContentSlot MDC) :
<script setup lang="ts">
interface Props {
type?: 'info' | 'warning' | 'tip' | 'danger'
}
const props = withDefaults(defineProps<Props>(), {
type: 'info',
})
const iconMap = {
info: 'i-heroicons-information-circle',
warning: 'i-heroicons-exclamation-triangle',
tip: 'i-heroicons-light-bulb',
danger: 'i-heroicons-x-circle',
}
const colorMap = {
info: 'info',
warning: 'warning',
tip: 'success',
danger: 'error',
}
</script>
<template>
<UAlert
:icon="iconMap[props.type]"
:color="colorMap[props.type] as any"
variant="soft"
class="my-4"
>
<template #description>
<ContentSlot :use="$slots.default" unwrap="p" />
</template>
</UAlert>
</template>
Point critique : <ContentSlot :use="$slots.default" unwrap="p" /> est obligatoire — sans lui le contenu entre ::alert et :: n'est pas rendu (Pitfall 4 RESEARCH.md).
content/fr/blog/test-kotlin-syntax.md (content — création)
Analog: aucun (pas de fichiers markdown dans le projet actuellement)
Pattern depuis RESEARCH.md (Code Examples) :
---
title: "Test Kotlin Syntax Highlighting"
description: "Article de test pour valider le renderer"
date: "2026-04-21"
tags: ["kotlin", "hytale", "test"]
---
## Bloc de code Kotlin
```kotlin
fun main() {
println("Hello, Hytale!")
}
Image optimisée
Tableau
| Colonne A | Colonne B |
|---|---|
| Valeur 1 | Valeur 2 |
Callout
::alert{type="info"} Ceci est un callout d'information. ::
**Critère de validation :** Ce fichier doit couvrir les 4 success criteria : bloc Kotlin coloré (BLOG-04), image via ProseImg (BLOG-05), tableau, callout (BLOG-01).
---
### `content/en/blog/test-kotlin-syntax.md` (content — création)
**Analog:** même structure que la version FR, même slug, contenu traduit en anglais.
---
## Shared Patterns
### Props avec valeurs par défaut (withDefaults)
**Source:** `app/components/TechBadge.vue` lignes 11–14
**Apply to:** `ProseImg.vue`, `Alert.vue`
```typescript
const props = withDefaults(defineProps<Props>(), {
// valeurs par défaut pour props optionnelles
})
NuxtImg usage
Source: app/components/ProjectCard.vue lignes 26–35, app/components/TechBadge.vue lignes 65–74
Apply to: ProseImg.vue
- Toujours utiliser
:src,:alt,loading="lazy"au minimum format="webp"si format fixe, sinon laisser @nuxt/image décidersizespour responsive
Composants Nuxt UI (UAlert, UBadge)
Source: app/components/TechBadge.vue ligne 76, app/components/FAQSection.vue ligne 33
Apply to: Alert.vue
- Nuxt UI est auto-importé — pas d'import explicite nécessaire
- Utiliser
color+variantpour le style as anyacceptable pour les types union non-exhaustifs de Nuxt UI
Convention import types
Source: app/components/ProjectCard.vue ligne 2
Apply to: content.config.ts
import type { ... } from '~~/shared/types' // types partagés
import { ... } from '@nuxt/content' // imports de librairie
Auto-import composants
Source: nuxt.config.ts lignes 15–19
Apply to: ProseImg.vue, Alert.vue
components: [
{
path: '~/components',
pathPrefix: false, // → components/content/Alert.vue est auto-importé
},
],
Les composants dans components/content/ sont auto-importés par Nuxt ET reconnus par @nuxt/content comme Prose overrides / composants MDC.
No Analog Found
| File | Role | Data Flow | Reason |
|---|---|---|---|
content/fr/blog/test-kotlin-syntax.md |
content | — | Pas de fichiers markdown dans le projet — nouveau format |
content/en/blog/test-kotlin-syntax.md |
content | — | Pas de fichiers markdown dans le projet — nouveau format |
Le planner doit utiliser le pattern RESEARCH.md "Code Examples" pour ces deux fichiers.
Metadata
Analog search scope: app/components/, app/assets/css/, nuxt.config.ts
Files scanned: 6 (nuxt.config.ts, main.css, ProjectCard.vue, TechBadge.vue, FAQSection.vue, app.vue partiel)
Pattern extraction date: 2026-04-21
