feat(blog+categories): Wave 3-5 — blog infrastructure, categories, content, navigation

- Install @astrojs/rss and setup content collections (blog + generators)
- Create BlogLayout with BlogPosting schema, article OG tags, prev/next nav
- Create blog post pages EN/RU ([slug].astro) with dynamic routing
- Create blog index pages EN/RU with post cards and filtering
- Create RSS feeds EN/RU with auto-discovery links
- Create generator category pages EN/RU (5 categories each)
- Add category navigation pills to homepage
- Create Breadcrumbs, RelatedGenerators, RelatedPosts components
- Add breadcrumbs to GeneratorLayout and BlogLayout
- Wire RelatedGenerators into GeneratorLayout
- Wire RelatedPosts into blog post pages
- Create 8 EN + 8 RU blog post templates with placeholder content
- Add blog link to LanguageSwitcher navigation
- Add category and blog translation keys
This commit is contained in:
emil
2026-05-14 03:19:06 +03:00
parent 23cf7058f0
commit 25fd24d236
56 changed files with 2078 additions and 28 deletions
@@ -0,0 +1,10 @@
Task 11: Install @astrojs/rss and set up content collections config
- Package installed: @astrojs/rss (added 9 packages, 511 total)
- File updated: src/content/config.ts
- Imported z from astro:content
- Added blog collection with type: "content"
- Blog schema fields: title (max 120), description (max 160), pubDate, modDate (optional), draft (default false), lang (en/ru), category (tutorial/guide/news/tips), tags (default []), ogImage (optional, image()), relatedGenerators (optional), relatedPosts (optional)
- Preserved existing generators collection (type: "data", schema: generatorSchema)
- Directories created: src/content/blog/en/ and src/content/blog/ru/
- Build verification: npm run build passed with 64 pages, 0 errors
+38
View File
@@ -0,0 +1,38 @@
Task 12: BlogLayout.astro with BlogPosting JSON-LD and Article OG Meta Tags
Verification: npm run build — PASSED (64 pages built)
File: src/layouts/BlogLayout.astro
Features implemented:
1. Wraps BaseLayout, passing title, description, ogImage.
2. Injects BlogPosting JSON-LD schema via slot="head" with:
- @type: BlogPosting
- headline (from title prop)
- author: Organization { name: "Randify" }
- publisher: Organization { name: "Randify", logo: ImageObject }
- datePublished (ISO 8601)
- dateModified (ISO 8601, falls back to datePublished)
- url (canonical)
- description
- inLanguage
3. OG type set to "article" via <meta property="og:type" content="article" /> in slot="head".
4. Article-specific meta tags added:
- article:published_time
- article:modified_time
5. Styled container: max-w-2xl mx-auto, matching GeneratorLayout spacing.
6. Header with title, accent dot, publication date, and conditional "Updated" label.
7. Prev/next post navigation with bilingual labels (EN/RU), arrow icons, and focus rings.
8. Named slot "related" for RelatedPosts component.
Props accepted:
- title: string
- description: string
- pubDate: Date | string
- modDate?: Date | string
- ogImage?: string
- lang?: Lang
- prevPost?: { slug: string; title: string }
- nextPost?: { slug: string; title: string }
Build output: 64 pages, 0 errors.
+7
View File
@@ -0,0 +1,7 @@
Task 13: EN blog post page ([slug].astro)
- Build: PASS (77 pages)
- Generated: dist/blog/test-post/index.html
- Verified: post title present in HTML
- Verified: BlogPosting JSON-LD schema present
- Verified: article:published_time meta tag present
- Slug derived from post.id with en/ prefix and .md extension stripped
+16
View File
@@ -0,0 +1,16 @@
Task 14: Create RU blog post page src/pages/ru/blog/[slug].astro
Build result: PASS
Pages built: 77
RU blog post page: compiled successfully (no RU blog posts yet, so no static pages generated)
EN blog post page: /blog/test-post/index.html generated
Files created/modified:
- src/pages/ru/blog/[slug].astro (created)
- src/layouts/BlogLayout.astro (fixed locale-aware prev/next links)
Verification:
- npm run build exited with code 0
- No TypeScript or Astro errors
- RU [slug].astro correctly filters getCollection("blog") by lang === "ru"
- BlogLayout prev/next navigation updated to use locale-aware paths (/ru/blog/ for RU, /blog/ for EN)
+36
View File
@@ -0,0 +1,36 @@
Task 15: Blog Index Pages — Evidence
======================================
Build Status: PASS (76 pages, exit code 0)
Files Created:
- src/pages/blog/index.astro
- src/pages/ru/blog/index.astro
Files Modified:
- src/i18n/translations.ts (added blogDesc, readMore, noPosts keys for EN + RU)
Build Output Verification:
- dist/blog/index.html (6893 bytes)
- dist/ru/blog/index.html (7342 bytes)
EN Page Features:
- Queries getCollection("blog"), filters lang === "en" and draft !== true
- Sorts by pubDate descending
- Links to /blog/[slug]/
- Date formatting: toLocaleDateString("en-US", ...)
- JSON-LD Blog schema with blogPost array
RU Page Features:
- Queries getCollection("blog"), filters lang === "ru" and draft !== true
- Sorts by pubDate descending
- Links to /ru/blog/[slug]/
- Date formatting: toLocaleDateString("ru-RU", ...)
- JSON-LD Blog schema with blogPost array
Both pages:
- Use BaseLayout with title and description props
- Include OG/Twitter Card meta tags (inherited from BaseLayout)
- Include canonical + hreflang alternates (inherited from BaseLayout)
- Show empty state "No posts yet" / "Пока нет записей." when collection is empty
- Display category badge + tag badges per post card
+20
View File
@@ -0,0 +1,20 @@
Task 16: RSS Feeds
Files created:
- src/pages/rss.xml.ts (EN feed)
- src/pages/ru/rss.xml.ts (RU feed)
File modified:
- src/layouts/BaseLayout.astro (added <link rel="alternate" type="application/rss+xml"> for both EN and RU feeds)
Build: PASS (npm run build exited 0)
Generated feeds:
- dist/rss.xml exists (287 bytes)
- dist/ru/rss.xml exists (404 bytes)
Feed content verified:
- EN: title="Randify Blog", language="en", description present, link=https://randify.pro/
- RU: title="Блог Randify", language="ru", description present, link=https://randify.pro/
Note: Feeds are currently empty because no blog posts exist in src/content/blog/ yet. When posts are added with lang="en"/"ru" and draft !== true, they will appear automatically.
+26
View File
@@ -0,0 +1,26 @@
Task 17: Category Pages for Generators
======================================
Build Result: PASS
Pages built: 76
EN Category Pages Created:
- dist/generators/category/gaming/index.html
- dist/generators/category/security/index.html
- dist/generators/category/decision-making/index.html
- dist/generators/category/creative/index.html
- dist/generators/category/utility/index.html
RU Category Pages Created:
- dist/ru/generators/category/gaming/index.html
- dist/ru/generators/category/security/index.html
- dist/ru/generators/category/decision-making/index.html
- dist/ru/generators/category/creative/index.html
- dist/ru/generators/category/utility/index.html
Verification:
- EN gaming page contains: "Gaming generators for tabletop RPGs" and generator titles (Lottery, Rock Paper Scissors, etc.)
- RU gaming page contains: "Игровые генераторы для настольных RPG" and generator titles (Колесо фортуны, Лотерея, etc.)
- All 5 categories render correctly with getStaticPaths
- Breadcrumb JSON-LD schema included on each page
- Generators filtered dynamically by category field from src/data/generators.ts
+16
View File
@@ -0,0 +1,16 @@
Task 18: Category Navigation Pills on Homepage
Files modified:
- src/pages/index.astro
- src/pages/ru/index.astro
- src/i18n/translations.ts
Build result: PASS (85 pages, 0 errors)
Verification:
- dist/index.html contains category navigation pills
- dist/ru/index.html contains localized category navigation pills
- All 5 categories present: Gaming (6), Security (3), Decision Making (4), Creative (5), Utility (10)
- Links point to /generators/category/[category]/ (EN) and /ru/generators/category/[category]/ (RU)
- Pills styled with Tailwind: rounded-full, bg-zinc-800/60, border-zinc-700/50, hover:bg-accent/20, hover:text-accent
- Responsive flex-wrap layout
@@ -0,0 +1,28 @@
Task 19: Create Breadcrumbs.astro Component — Evidence
=====================================================
1. File Created
---------------
Path: src/components/Breadcrumbs.astro
2. Component Specification
--------------------------
- Props interface: { items: Array<{ label: string, href?: string }> }
- Renders <nav aria-label="Breadcrumb"> containing <ol>
- Each item rendered as <li> with "/" separator between items
- Last item (no href or end of array): <span aria-current="page"> with text-zinc-400
- Items with href: <a> with text-zinc-500 hover:text-accent transition-colors and focus ring
- Tailwind styling: text-sm, muted zinc colors, accent hover state
3. Build Verification
---------------------
Command: npm run build
Result: PASS (64 pages built, 0 errors)
4. Example Usage
----------------
<Breadcrumbs items={[
{ label: "Home", href: "/" },
{ label: "Gaming", href: "/generators/category/gaming/" },
{ label: "Dice Roller" }
]} />
+19
View File
@@ -0,0 +1,19 @@
Task 20: RelatedGenerators.astro
Files changed:
- src/components/RelatedGenerators.astro (created)
- src/layouts/GeneratorLayout.astro (updated — integrated RelatedGenerators)
- src/i18n/translations.ts (updated — added relatedGenerators key EN+RU)
Build result:
- npm run build: PASSED
- 76 pages built, 0 errors
Component behavior:
- Accepts props: currentSlug, category, optional lang
- Filters generators by category, excludes current slug, limits to 4
- Returns null (renders nothing) if no related generators exist
- Locale-aware titles and links (EN / RU)
- Compact card layout: icon + title in a responsive 2-column grid
- Uses Tailwind with existing design tokens (accent color, zinc palette)
- Includes focus-visible ring for accessibility
@@ -0,0 +1,24 @@
Task 21: RelatedPosts Component
================================
Files created:
- src/components/RelatedPosts.astro
Files modified:
- src/i18n/translations.ts (added relatedPosts key: en="Related posts", ru="Похожие статьи")
- src/pages/blog/[slug].astro (imported RelatedPosts, wired into "related" slot)
- src/pages/ru/blog/[slug].astro (imported RelatedPosts, wired into "related" slot)
Build result: PASS
- 83 pages built successfully
- No errors or warnings
Implementation details:
- Accepts post: CollectionEntry<"blog">
- Uses getCollection to fetch non-draft posts in same language
- Scores related posts by tag overlap count
- Shows top 3 posts with score > 0
- Hides section entirely if no related posts found
- Renders title, date, and description snippet for each related post
- Links use correct /blog/ or /ru/blog/ base path
- Styled with Tailwind matching existing dark theme (zinc palette, accent color)
+25
View File
@@ -0,0 +1,25 @@
Task 22: Create 8 English blog post templates
Date: 2026-05-14
Build result: PASS
Pages built: 85
Files created:
- src/content/blog/en/what-is-cryptographically-secure-randomness.md
- src/content/blog/en/how-to-create-strong-passwords.md
- src/content/blog/en/dice-notation-explained.md
- src/content/blog/en/psychology-of-randomness.md
- src/content/blog/en/rng-in-gaming.md
- src/content/blog/en/random-team-picker.md
- src/content/blog/en/color-theory-palettes.md
- src/content/blog/en/uuid-vs-sequential-ids.md
Build output confirms all 8 posts were generated alongside the existing test-post:
/blog/what-is-cryptographically-secure-randomness/index.html
/blog/how-to-create-strong-passwords/index.html
/blog/dice-notation-explained/index.html
/blog/psychology-of-randomness/index.html
/blog/rng-in-gaming/index.html
/blog/random-team-picker/index.html
/blog/color-theory-palettes/index.html
/blog/uuid-vs-sequential-ids/index.html
/blog/test-post/index.html
+20
View File
@@ -0,0 +1,20 @@
Build completed: 93 pages
RU blog posts generated (8):
- /ru/blog/what-is-cryptographically-secure-randomness/index.html
- /ru/blog/how-to-create-strong-passwords/index.html
- /ru/blog/dice-notation-guide/index.html
- /ru/blog/psychology-of-randomness/index.html
- /ru/blog/rng-in-gaming/index.html
- /ru/blog/random-team-selection/index.html
- /ru/blog/color-theory-guide/index.html
- /ru/blog/uuid-vs-sequential-ids/index.html
Files created:
- src/content/blog/ru/what-is-cryptographically-secure-randomness.md
- src/content/blog/ru/how-to-create-strong-passwords.md
- src/content/blog/ru/dice-notation-guide.md
- src/content/blog/ru/psychology-of-randomness.md
- src/content/blog/ru/rng-in-gaming.md
- src/content/blog/ru/random-team-selection.md
- src/content/blog/ru/color-theory-guide.md
- src/content/blog/ru/uuid-vs-sequential-ids.md
Build timestamp: 2026-05-14T03:08:00+03:00
+16
View File
@@ -0,0 +1,16 @@
Task 24 Evidence: Blog Navigation Link
=======================================
Build: PASS
- npm run build completed successfully (84 pages built)
Verification:
- EN blog link (/blog/) found in dist/index.html
- RU blog link (/ru/blog/) found in 38 RU pages including dist/ru/index.html
Changes made:
- Modified src/components/LanguageSwitcher.astro
- Added Blog / Блог link between language toggle and About link
- Link points to /blog/ on EN pages and /ru/blog/ on RU pages
- Used existing translation keys (blog: "Blog", blog: "Блог") from src/i18n/translations.ts
- Styled consistently with existing About/Privacy links
@@ -0,0 +1,17 @@
Task: Add Breadcrumbs component to generator and blog layouts
Date: 2026-05-14
1. npm run build
Result: PASS (93 pages built, exit code 0)
2. Grep dist/generators/dice/index.html for breadcrumb items:
- Found: Home, Gaming, Dice
- Command: grep -o 'Home\|Gaming\|Dice' dist/generators/dice/index.html
3. Grep dist/blog/test-post/index.html for breadcrumb items:
- Found: Home, Blog, Test Post
- Command: grep -o 'Home\|Blog\|Test Post' dist/blog/test-post/index.html
Files modified:
- src/layouts/GeneratorLayout.astro
- src/layouts/BlogLayout.astro