13 KiB
Implementation Plan
This implementation plan breaks down the blog system development into discrete, actionable coding tasks. Each task builds incrementally on previous work, with all code integrated and functional at each step.
Task List
-
1. Install and configure Nuxt Content module
- Install @nuxt/content package via pnpm
- Add @nuxt/content to modules array in nuxt.config.ts (before other modules)
- Configure content options: highlight themes (github-light/github-dark), markdown.mdc: true, toc depth
- Add content-specific route rules for caching (/blog, /fa/blog with swr: 3600)
- Verify installation by starting dev server and checking for content module initialization
- Requirements: 1.1, 1.2, 1.5, 1.6
-
2. Create content directory structure and sample posts
-
Create content/en/blog/ and content/fa/blog/ directories
-
Create TypeScript interface for BlogPost extending ParsedContent in app/types/blog.ts
-
Write 2 sample English blog posts with complete frontmatter (title, description, date, tags, image)
-
Write 2 sample Persian blog posts with RTL content
-
Include code blocks, headings, lists, and images in sample posts for testing
-
Create one draft post to test draft filtering
-
Requirements: 2.1, 2.3, 2.4, 3.1, 3.2, 3.5, 3.6
-
-
3. Implement useBlog composable with utility functions
-
Create app/composables/useBlog.ts file
-
Implement calculateReadingTime function (200 words/min from body.children)
-
Implement formatDate function using Intl.DateTimeFormat with locale support
-
Implement extractUniqueTags function to aggregate tags from posts array
-
Implement getBlogPath function returning locale-aware path
-
Implement filterPostsBySearch function for title/description/tags filtering
-
Implement filterPostsByTag function
-
Export all functions with proper TypeScript types
-
Requirements: 4.7, 11.6
-
-
4. Update i18n translation files with blog keys
- Add blog section to i18n/locales/en.json with all required keys (title, explore, empty, readMore, readingTime, publishedOn, backToBlog, previousPost, nextPost, tableOfContents, searchPlaceholder, filterByTag, allPosts, noResults, copyCode, codeCopied, subscribe)
- Add corresponding Persian translations to i18n/locales/fa.json
- Verify translations are loaded by checking in dev tools
- Requirements: 4.5, 8.1
-
5. Implement blog listing page
-
5.1 Update app/pages/blog/index.vue with content fetching
- Replace placeholder content with queryContent implementation
- Use useAsyncData to fetch posts for current locale with .where({ draft: { $ne: true } })
- Apply .sort({ date: -1 }) and .only() for required fields
- Implement computed property for extracting unique tags using useBlog composable
- Add reactive refs for searchQuery and selectedTag
- Implement computed filteredPosts using filterPostsBySearch and filterPostsByTag
- Requirements: 4.1, 4.2, 4.6, 4.8, 7.1
-
5.2 Create BlogCard component
- Create app/components/blog/BlogCard.vue
- Accept post prop with BlogPost type
- Use UCard as base component with hover effects
- Display NuxtImg for cover image with lazy loading and fallback
- Display title, description, formatted date, reading time, and tags
- Use UBadge for tags display
- Use localePath for navigation link
- Apply responsive styling
- Requirements: 4.3, 10.1, 10.2
-
5.3 Create BlogSearch component
- Create app/components/blog/BlogSearch.vue
- Accept modelValue prop and emit update:modelValue
- Use UInput with search icon and clear button
- Implement debounce using useDebounceFn from VueUse (300ms)
- Add i18n placeholder text
- Requirements: 13.2, 13.3
-
5.4 Create BlogTagFilter component
- Create app/components/blog/BlogTagFilter.vue
- Accept tags array and modelValue props
- Display tags as UButton or UBadge with click handlers
- Highlight active tag with primary color
- Add "All posts" option to clear filter
- Update URL query parameter using useRoute and navigateTo
- Read query parameter on mount to restore filter state
- Apply horizontal scroll on mobile
- Requirements: 7.2, 7.3, 7.4, 7.5, 7.6
-
5.5 Create BlogEmpty component and integrate all components
- Create app/components/blog/BlogEmpty.vue with empty state message
- Import and use BlogSearch, BlogTagFilter, BlogCard, BlogEmpty in index.vue
- Implement grid layout for blog cards (responsive: 1 col mobile, 2 col tablet, 3 col desktop)
- Add page header with title and description using i18n
- Test search, filter, and empty state scenarios
- Requirements: 4.4, 4.5, 13.4, 13.6
-
6. Implement blog detail page
-
6.1 Update app/pages/blog/[...slug].vue with content fetching
- Replace placeholder content with queryContent().findOne() implementation
- Use useAsyncData with slug-based key
- Fetch post using _path matching for current locale
- Throw createError({ statusCode: 404 }) if post not found
- Fetch adjacent posts for prev/next navigation using separate query
- Calculate current post index in sorted posts array
- Requirements: 5.1, 5.5, 16.4
-
6.2 Implement SEO meta tags and structured data
- Use useContentHead(post) for automatic meta generation
- Use useSeoMeta for custom title, og tags, twitter cards
- Set og:type to "article" with article:published_time and article:tag
- Use post.image or default cover image for og:image
- Add JSON-LD structured data using useHead with BlogPosting schema
- Include author, datePublished, headline in structured data
- Requirements: 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.8
-
6.3 Create BlogPost metadata component
-
Create app/components/blog/BlogPost.vue
-
Accept post prop with BlogPost type
-
Display post title as h1
-
Display formatted date using formatDate from useBlog
-
Display reading time using calculateReadingTime from useBlog
-
Display author if available
-
Display tags as UBadge components
-
Display cover image using NuxtImg if available
-
Use semantic HTML (article, header, time elements)
-
Requirements: 5.4
-
-
6.4 Implement ContentRenderer with Prose styling
-
Use ContentRenderer component to render post.body
-
Wrap in article element with proper semantic structure
-
Apply dir attribute based on locale (rtl for fa, ltr for en)
-
Add CSS to force LTR for code blocks in RTL context
-
Verify Shiki syntax highlighting is working
-
Test with sample posts containing various markdown elements
-
Requirements: 5.2, 5.3, 5.7, 9.1, 9.2, 9.3
-
-
6.5 Create BlogTableOfContents component
-
Create app/components/blog/BlogTableOfContents.vue
-
Accept toc prop from post.body.toc
-
Render nested heading structure from toc.links
-
Implement smooth scroll to heading on link click
-
Use IntersectionObserver to track active section
-
Highlight active section in TOC
-
Make sticky on desktop (position: sticky)
-
Make collapsible using UAccordion on mobile
-
Only show if post has 3+ headings
-
Requirements: 12.1, 12.2, 12.3, 12.4, 12.5, 12.6, 12.7
-
-
6.6 Create BlogNavigation component
-
Create app/components/blog/BlogNavigation.vue
-
Accept prev and next props (BlogPost | null)
-
Display previous post link with title and arrow icon
-
Display next post link with title and arrow icon
-
Use UButton with icon slots
-
Use localePath for navigation links
-
Implement keyboard navigation (@keydown for arrow keys)
-
Apply flexbox layout with space-between
-
Requirements: 16.4, 16.5, 16.6
-
-
6.7 Create breadcrumb navigation and integrate all components
- Use UBreadcrumb component with links array (Home > Blog > Post Title)
- Use localePath for breadcrumb links
- Import and use BlogPost, ContentRenderer, BlogTableOfContents, BlogNavigation
- Add "Back to Blog" link using localePath
- Implement responsive layout (TOC sidebar on desktop, inline on mobile)
- Test with both English and Persian posts
- Test prev/next navigation
- Requirements: 16.1, 16.2, 16.3, 16.5
-
7. Implement custom Prose components
-
7.1 Create ProseCode component with copy functionality
- Create app/components/content/ProseCode.vue
- Accept code, language, filename, highlights props
- Display language label if provided
- Display filename if provided
- Add copy button with icon
- Implement copy to clipboard using navigator.clipboard API
- Show success feedback (icon change or toast)
- Apply syntax highlighting theme based on color mode
- Support line highlighting from highlights prop
- Requirements: 15.1, 15.2, 15.3, 15.4, 15.5, 15.6
-
7.2 Create custom MDC components
- Create app/components/content/BlogCallout.vue for callout boxes
- Accept title and type props (info, warning, success)
- Use UCard with colored border based on type
- Create app/components/content/Alert.vue for inline alerts
- Test MDC syntax in sample blog posts (::blog-callout, ::alert)
- Requirements: 5.6
-
* 7.3 Customize Prose component styles
- Update app.config.ts with prose customization
- Define styles for ProseH1, ProseH2, ProseH3 (font sizes, spacing, colors)
- Define styles for ProseP (line height, spacing, colors)
- Define styles for ProseCode inline code (background, padding, border-radius)
- Define styles for ProseA links (color, hover effects)
- Define styles for ProseImg (responsive, rounded corners)
- Test with sample posts to verify styling
- Requirements: 5.7, 8.2
-
8. Implement RSS feed generation
- Create server/routes/blog/rss.xml.ts server route
- Use serverQueryContent to fetch published posts for current locale
- Detect locale from URL path (/blog/rss.xml vs /fa/blog/rss.xml)
- Generate RSS 2.0 XML with channel metadata
- Include item elements for each post (title, link, guid, pubDate, description)
- Implement escapeXml helper function for XML safety
- Set Content-Type header to application/rss+xml
- Add RSS link to blog listing page header
- Test RSS feed in browser and RSS reader
- Requirements: 14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7
-
9. Configure prerendering and route rules
- Update nitro.prerender.routes in nuxt.config.ts to include /blog and /fa/blog
- Add dynamic route generation for all blog posts using queryContent
- Verify route rules for caching are applied (/blog/, /fa/blog/ with swr: 3600)
- Test static generation by running pnpm generate
- Verify all blog routes are prerendered in .output/public
- Requirements: 10.5
-
10. Add blog link to navigation
- Update app/components/common/TopNav.vue to include blog link
- Use localePath('/blog') for navigation
- Highlight blog link when on blog routes using useRoute()
- Add blog icon if desired
- Test navigation in both locales
- Requirements: 16.3
-
11. Create default blog cover image
- Create or add a default cover image to public/img/blog/default-cover.jpg
- Ensure image is optimized (WebP format, appropriate dimensions)
- Use this image as fallback in BlogCard and SEO meta tags
- Requirements: 6.4
-
* 12. Performance optimization and testing
- Run Lighthouse audit on blog listing and detail pages
- Verify lazy loading of images below fold
- Check code splitting in network tab (separate chunks for blog components)
- Verify static generation of all routes
- Test hot-reload in development mode
- Measure and optimize Time to First Byte (TTFB)
- Verify Core Web Vitals (LCP, FID, CLS)
- Requirements: 10.3, 10.4, 10.6, 10.7, 11.1
-
* 13. Accessibility testing and improvements
- Test keyboard navigation (Tab, Enter, arrow keys)
- Test with screen reader (NVDA or VoiceOver)
- Verify color contrast ratios using browser dev tools
- Ensure all interactive elements have focus indicators
- Add ARIA labels where needed
- Verify semantic HTML structure
- Test with reduced motion preference
- Requirements: 8.3, 8.4, 8.5, 8.6
-
* 14. Cross-browser and responsive testing
- Test on Chrome, Firefox, Safari, Edge
- Test on mobile devices (iOS Safari, Chrome Android)
- Test on tablet viewports
- Verify RTL layout on Persian pages
- Test search and filter functionality on all devices
- Verify image optimization and lazy loading
- Test code block copy functionality
- Requirements: 8.1, 9.1, 9.2, 9.4
-
* 15. Documentation and sample content
- Create README.md in content/ directory with authoring guidelines
- Document frontmatter schema and required fields
- Provide markdown examples for common elements
- Document MDC component usage
- Add comments in code for complex logic
- Update main README.md with blog feature description
- Requirements: 11.3, 11.4