# 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 (`
`, `
`, `