# Blog System Design Document
## Overview
This document outlines the technical design for implementing a fully-featured blog system in the Nuxt 4 portfolio application using Nuxt Content v3. The design integrates seamlessly with the existing architecture, leveraging Nuxt UI components, i18n for bilingual support, and modern performance optimization techniques.
### Design Goals
1. **Content-First Architecture**: Use file-based markdown content management for simplicity and version control
2. **Seamless Integration**: Maintain consistency with existing portfolio design system and navigation
3. **Bilingual Support**: Full English and Persian (RTL) support with locale-specific content
4. **Performance**: Optimize for fast page loads, SEO, and Core Web Vitals
5. **Developer Experience**: Hot-reload, TypeScript safety, and intuitive content authoring
6. **Extensibility**: Support for MDC components, custom frontmatter, and future enhancements
### Technology Stack
- **Content Management**: @nuxt/content v3.x
- **UI Components**: Nuxt UI v4 (existing)
- **Styling**: Tailwind CSS v4 (existing)
- **Internationalization**: @nuxtjs/i18n (existing)
- **Image Optimization**: @nuxt/image (existing)
- **Syntax Highlighting**: Shiki (built-in with Nuxt Content)
- **Type Safety**: TypeScript with custom interfaces
## Architecture
### High-Level Architecture Diagram
```
┌─────────────────────────────────────────────────────────────┐
│ Nuxt 4 Application │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Pages │ │ Components │ │
│ │ │ │ │ │
│ │ /blog/ │─────▶│ BlogList │ │
│ │ index.vue │ │ BlogCard │ │
│ │ │ │ BlogSearch │ │
│ │ /blog/ │ │ TagFilter │ │
│ │ [...slug] │─────▶│ BlogPost │ │
│ │ .vue │ │ TableOfContents │
│ └──────────────┘ │ BlogNav │ │
│ │ └──────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Nuxt Content Module │ │
│ │ - queryContent() API │ │
│ │ - ContentDoc component │ │
│ │ - ContentRenderer component │ │
│ │ - useContentHead() composable │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Content Directory │ │
│ │ content/ │ │
│ │ ├── en/blog/*.md │ │
│ │ └── fa/blog/*.md │ │
│ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
### Directory Structure
```
project-root/
├── content/ # Content directory (new)
│ ├── en/
│ │ └── blog/
│ │ ├── my-first-post.md
│ │ ├── nuxt-tips.md
│ │ └── tutorials/
│ │ └── getting-started.md
│ └── fa/
│ └── blog/
│ ├── first-post-fa.md
│ └── nuxt-tips-fa.md
│
├── app/
│ ├── pages/
│ │ └── blog/
│ │ ├── index.vue # Blog listing page (existing, to be updated)
│ │ └── [...slug].vue # Blog detail page (existing, to be updated)
│ │
│ ├── components/
│ │ └── blog/ # Blog components (new)
│ │ ├── BlogCard.vue
│ │ ├── BlogList.vue
│ │ ├── BlogSearch.vue
│ │ ├── BlogTagFilter.vue
│ │ ├── BlogPost.vue
│ │ ├── BlogTableOfContents.vue
│ │ ├── BlogNavigation.vue
│ │ └── BlogEmpty.vue
│ │
│ ├── composables/
│ │ └── useBlog.ts # Blog utilities composable (new)
│ │
│ ├── types/
│ │ └── blog.ts # Blog TypeScript types (new)
│ │
│ └── public/
│ └── img/
│ └── blog/ # Blog images (new)
│ └── default-cover.jpg
│
├── server/
│ └── routes/
│ └── blog/
│ └── rss.xml.ts # RSS feed generator (new)
│
└── nuxt.config.ts # Updated with @nuxt/content
```
### Content Flow
1. **Content Creation**: Developer writes markdown files in `content/{locale}/blog/`
2. **Content Parsing**: Nuxt Content parses markdown and frontmatter on server start
3. **Content Query**: Pages use `queryContent()` to fetch filtered/sorted content
4. **Content Rendering**: `ContentRenderer` transforms markdown to HTML with Vue components
5. **Content Display**: Nuxt UI components style the rendered content
## Components and Interfaces
### Page Components
#### 1. Blog Listing Page (`app/pages/blog/index.vue`)
**Purpose**: Display all published blog posts with search and filtering capabilities
**Key Features**:
- Fetch posts using `queryContent()`
- Search functionality with debounce
- Tag filtering
- Responsive grid layout
- Empty state handling
- Locale-aware content fetching
**Component Structure**:
```vue
```
**Data Fetching Strategy**:
```typescript
const { locale } = useI18n()
const route = useRoute()
// Fetch posts for current locale
const { data: posts } = await useAsyncData('blog-posts', () =>
queryContent(`${locale.value}/blog`)
.where({ draft: { $ne: true } })
.sort({ date: -1 })
.only(['title', 'description', 'date', 'tags', '_path', 'image'])
.find()
)
```
#### 2. Blog Detail Page (`app/pages/blog/[...slug].vue`)
**Purpose**: Render individual blog post with full content and metadata
**Key Features**:
- Fetch single post by slug
- Render markdown content with syntax highlighting
- Display metadata (title, date, tags, reading time)
- Table of contents for long posts
- Previous/next post navigation
- SEO meta tags
- Breadcrumb navigation
**Component Structure**:
```vue
```
**Data Fetching Strategy**:
```typescript
const { locale } = useI18n()
const route = useRoute()
const slug = route.params.slug as string[]
// Fetch current post
const { data: post } = await useAsyncData(`blog-post-${slug.join('/')}`, () =>
queryContent(`${locale.value}/blog`)
.where({ _path: `/${locale.value}/blog/${slug.join('/')}` })
.findOne()
)
if (!post.value) {
throw createError({ statusCode: 404, message: 'Post not found' })
}
// Fetch adjacent posts for navigation
const { data: adjacentPosts } = await useAsyncData('adjacent-posts', () =>
queryContent(`${locale.value}/blog`)
.where({ draft: { $ne: true } })
.sort({ date: -1 })
.only(['title', '_path', 'date'])
.find()
)
```
### UI Components
#### 1. BlogCard Component
**Purpose**: Display blog post preview in listing page
**Props**:
```typescript
interface BlogCardProps {
post: BlogPost
}
```
**Features**:
- Cover image with lazy loading
- Title and description
- Formatted date
- Reading time estimate
- Tags as badges
- Hover effects
- Click to navigate
**Implementation Notes**:
- Use `UCard` from Nuxt UI as base
- Use `NuxtImg` for optimized images
- Use `UBadge` for tags
- Use `localePath()` for navigation
#### 2. BlogSearch Component
**Purpose**: Search input for filtering posts
**Props**:
```typescript
interface BlogSearchProps {
modelValue: string
}
interface BlogSearchEmits {
'update:modelValue': [value: string]
}
```
**Features**:
- Debounced input (300ms)
- Clear button
- Search icon
- Placeholder text (i18n)
**Implementation Notes**:
- Use `UInput` with icon slots
- Use `useDebounceFn` from VueUse
- Emit updates to parent
#### 3. BlogTagFilter Component
**Purpose**: Display and filter by tags
**Props**:
```typescript
interface BlogTagFilterProps {
tags: string[]
modelValue: string | null
}
```
**Features**:
- Display all unique tags
- Highlight active tag
- Clear filter option
- Responsive layout
**Implementation Notes**:
- Use `UButton` or `UBadge` for tags
- Use query parameters for state persistence
- Horizontal scroll on mobile
#### 4. BlogTableOfContents Component
**Purpose**: Display navigable table of contents for long posts
**Props**:
```typescript
interface TocLink {
id: string
text: string
depth: number
children?: TocLink[]
}
interface BlogTableOfContentsProps {
toc: {
links: TocLink[]
}
}
```
**Features**:
- Nested heading structure
- Active section highlighting
- Smooth scroll to sections
- Sticky positioning on desktop
- Collapsible on mobile
**Implementation Notes**:
- Use `IntersectionObserver` for active tracking
- Use `scrollIntoView({ behavior: 'smooth' })` for navigation
- Use `UAccordion` for mobile collapsible version
#### 5. BlogNavigation Component
**Purpose**: Previous/next post navigation
**Props**:
```typescript
interface BlogNavigationProps {
prev: BlogPost | null
next: BlogPost | null
}
```
**Features**:
- Previous and next post links
- Post titles
- Directional arrows
- Keyboard navigation support
**Implementation Notes**:
- Use `UButton` with icon slots
- Use `@keydown` for arrow key navigation
- Use flexbox for layout
#### 6. BlogPost Component
**Purpose**: Display post metadata header
**Props**:
```typescript
interface BlogPostProps {
post: BlogPost
}
```
**Features**:
- Post title (h1)
- Formatted publish date
- Reading time estimate
- Author info (if available)
- Tags
- Cover image
**Implementation Notes**:
- Use semantic HTML (``, ``, `