# Blog Template Development Guide

**Last Updated:** 2026-01-09

Complete guide to blog template structure, components, helpers, and data requirements for the WordPress to PHP migration.

## Overview

This guide documents the blog template system architecture, including all templates, components, helper functions, and data structures. Use this guide when developing, maintaining, or extending blog templates.

## Template Architecture

### Template Files

**Location**: `v2/pages/blog/`

**Templates**:

- `index.php` - Blog index page (all posts)
- `category.php` - Category archive page (reusable for all categories)
- `post.php` - Single post page
- `topic-hub.php` - Topic hub page

### Component Files

**Location**: `v2/components/blog/`

**Components**:

- `PostCard.php` - Post preview card
- `PostHeader.php` - Post header with featured image
- `PostContent.php` - Post content renderer
- `RelatedPosts.php` - Related posts section
- `Pagination.php` - Pagination navigation
- `CategoryNav.php` - Category navigation
- `Breadcrumbs.php` - Breadcrumb navigation
- `TopicHubHero.php` - Topic hub hero section

### Helper Functions

**Location**: `v2/config/blog-template-helpers.php`

**Functions**:

- `load_blog_post($category, $slug)` - Load single post
- `load_blog_posts_by_category($category, $limit, $offset)` - Load posts by category
- `load_all_blog_posts($limit, $offset)` - Load all posts
- `load_related_posts($post_slug, $category, $limit)` - Load related posts
- `load_blog_categories()` - Load categories
- `format_blog_date($date_string, $format)` - Format dates
- `calculate_reading_time($word_count)` - Calculate reading time
- `get_category_label($category_slug)` - Get category label
- `generate_excerpt($text, $max_length)` - Generate excerpt

### Meta & Schema Generators

**Location**: `v2/config/`

**Files**:

- `blog-meta-generator.php` - Meta tag generation
- `blog-schema-generator.php` - Schema markup generation

## Template Structure

### Blog Index (`index.php`)

**URL Pattern**: `/insights/` or `/insights/page/{page}/`

**Components Used**:

- CategoryNav
- PostCard
- Pagination

**Data Loading**:

```php
$posts = load_all_blog_posts($posts_per_page, $offset);
$categories = load_blog_categories();
```

**Meta Tags**: Generated via `render_blog_meta_tags('index', $page_data)`
**Schema**: Generated via `render_blog_schema('index', $page_data)`

### Category Archive (`category.php`)

**URL Pattern**: `/insights/{category}/` or `/insights/{category}/page/{page}/`

**Components Used**:

- CategoryNav
- PostCard
- Pagination

**Data Loading**:

```php
$posts = load_blog_posts_by_category($category, $posts_per_page, $offset);
$category_data = load_blog_categories()[$category];
```

**Meta Tags**: Generated via `render_blog_meta_tags('category', $page_data)`
**Schema**: Generated via `render_blog_schema('category', $page_data)`

### Single Post (`post.php`)

**URL Pattern**: `/insights/{category}/{slug}/`

**Components Used**:

- Breadcrumbs
- PostHeader
- PostContent
- RelatedPosts

**Data Loading**:

```php
$post = load_blog_post($category, $slug);
$related_posts = load_related_posts($slug, $category, 14);
```

**Meta Tags**: Generated via `render_blog_meta_tags('post', $post)`
**Schema**: Generated via `render_blog_schema('post', $post)`

### Topic Hub (`topic-hub.php`)

**URL Pattern**: `/insights/topics/{topic_id}/`

**Components Used**:

- Breadcrumbs
- TopicHubHero
- PostCard

**Data Loading**:

```php
$topic_data = load_topic_data($topic_id); // From topics.json
$related_posts = load_posts_by_topic($topic_id);
```

**Meta Tags**: Generated via `render_blog_meta_tags('topic_hub', $page_data)`
**Schema**: Generated via `render_blog_schema('topic_hub', $page_data)`

## Component API

### PostCard Component

**File**: `v2/components/blog/PostCard.php`

**Props**:

- `$title` (string) - Post title
- `$url` (string) - Post URL
- `$category` (string) - Category slug
- `$category_label` (string) - Category display name
- `$excerpt` (string) - Post excerpt
- `$publication_date` (string) - ISO 8601 date
- `$featured_image` (array|null) - Image data
- `$show_excerpt` (bool) - Show excerpt (default: true)
- `$variant` (string) - 'default' | 'compact' (default: 'default')

**Usage**:

```php
$title = $post['title'];
$url = $post['url'];
$category = $post['category'];
$category_label = get_category_label($category);
$excerpt = $post['excerpt'];
$publication_date = $post['publication_date'];
$featured_image = $post['featured_image'];
include '../../components/blog/PostCard.php';
```

### PostHeader Component

**File**: `v2/components/blog/PostHeader.php`

**Props**:

- `$title` (string) - Post title (H1)
- `$category` (string) - Category slug
- `$category_label` (string) - Category display name
- `$publication_date` (string) - ISO 8601 date
- `$author` (array|null) - Author data (name, url)
- `$reading_time` (int|null) - Reading time in minutes
- `$featured_image` (array|null) - Featured image data

**Usage**:

```php
$title = $post['title'];
$category = $post['category'];
$category_label = get_category_label($category);
$publication_date = $post['publication_date'];
$author = $post['author'];
$reading_time = calculate_reading_time($post['content']['word_count']);
$featured_image = $post['featured_image'];
include '../../components/blog/PostHeader.php';
```

### PostContent Component

**File**: `v2/components/blog/PostContent.php`

**Props**:

- `$html_content` (string) - Post HTML content
- `$images` (array) - Image data for optimization
- `$internal_links` (array) - Internal links for processing

**Usage**:

```php
$html_content = $post['content']['html'];
$images = $post['images'];
$internal_links = $post['internal_links'];
include '../../components/blog/PostContent.php';
```

### RelatedPosts Component

**File**: `v2/components/blog/RelatedPosts.php`

**Props**:

- `$related_posts` (array) - Array of related post data
- `$limit` (int) - Maximum posts (default: 14)
- `$heading` (string) - Heading text (default: "Ähnliche Artikel")

**Usage**:

```php
$related_posts = load_related_posts($slug, $category, 14);
$heading = 'Ähnliche Artikel';
include '../../components/blog/RelatedPosts.php';
```

### Pagination Component

**File**: `v2/components/blog/Pagination.php`

**Props**:

- `$current_page` (int) - Current page number (1-based)
- `$total_pages` (int) - Total number of pages
- `$posts_per_page` (int) - Posts per page
- `$base_url` (string) - Base URL for pagination
- `$show_page_numbers` (bool) - Show page numbers (default: true)

**Usage**:

```php
$current_page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$total_pages = ceil($total_posts / $posts_per_page);
$base_url = '/insights/';
include '../../components/blog/Pagination.php';
```

### CategoryNav Component

**File**: `v2/components/blog/CategoryNav.php`

**Props**:

- `$current_category` (string) - Current category slug
- `$categories` (array) - Array of category data
- `$variant` (string) - 'tabs' | 'links' (default: 'tabs')

**Usage**:

```php
$current_category = $category;
$categories = load_blog_categories();
include '../../components/blog/CategoryNav.php';
```

### Breadcrumbs Component

**File**: `v2/components/blog/Breadcrumbs.php`

**Props**:

- `$items` (array) - Array of breadcrumb items (name, url)
- `$include_schema` (bool) - Include schema markup (default: true)

**Usage**:

```php
$items = [
    ['name' => 'Home', 'url' => '/'],
    ['name' => 'Blog', 'url' => '/insights/'],
    ['name' => $post['title'], 'url' => $post['url']]
];
include '../../components/blog/Breadcrumbs.php';
```

### TopicHubHero Component

**File**: `v2/components/blog/TopicHubHero.php`

**Props**:

- `$topic_id` (string) - Topic identifier
- `$topic_name` (string) - Topic display name
- `$description` (string) - Topic description
- `$statistics` (array) - Statistics (post_count, last_updated, subtopics_count)

**Usage**:

```php
$topic_name = $topic_data['name'];
$description = $topic_data['description'];
$statistics = [
    'post_count' => count($related_posts),
    'last_updated' => null,
    'subtopics_count' => 0
];
include '../../components/blog/TopicHubHero.php';
```

## Data Structure

### Post JSON Format

**Location**: `v2/data/blog/posts/{category}/{slug}.json`

**Schema**:

```json
{
  "slug": "leitfaden-zur-finanzbuchhaltung",
  "title": "Leitfaden zur Finanzbuchhaltung",
  "category": "lexikon",
  "category_label": "Lexikon",
  "url": "/insights/lexikon/leitfaden-zur-finanzbuchhaltung/",
  "publication_date": "2023-09-01T10:40:44+00:00",
  "modified_date": "2025-03-31T18:58:44+00:00",
  "author": {
    "name": "Emma",
    "url": "/author/emma/"
  },
  "featured_image": {
    "src": "/insights/bilder/image.webp",
    "alt": "Featured image",
    "width": 1024,
    "height": 576,
    "srcset": [
      { "src": "/insights/bilder/image-640w.webp", "width": 640 },
      { "src": "/insights/bilder/image-1024w.webp", "width": 1024 }
    ]
  },
  "excerpt": "Short excerpt...",
  "content": {
    "html": "<p>Full HTML content...</p>",
    "text": "Plain text content...",
    "word_count": 1214
  },
  "images": [],
  "meta": {
    "title": "Leitfaden zur Finanzbuchhaltung | Lexikon | Ordio",
    "description": "Meta description...",
    "keywords": ["keyword1", "keyword2"]
  },
  "topics": ["personalverwaltung"],
  "clusters": {
    "primary": "personalverwaltung",
    "secondary": ["compliance"]
  },
  "related_posts": [
    {
      "slug": "related-post-slug",
      "title": "Related Post Title",
      "url": "/insights/category/related-post-slug/",
      "category": "lexikon",
      "similarity_score": 0.686,
      "relationship_type": "related"
    }
  ],
  "reading_time": 6,
  "internal_links": []
}
```

### Categories JSON Format

**Location**: `v2/data/blog/categories.json`

**Schema**:

```json
{
  "categories": [
    {
      "slug": "lexikon",
      "name": "Lexikon",
      "description": "Fachbegriffe...",
      "url": "/insights/lexikon/",
      "post_count": 22,
      "meta": {
        "title": "Lexikon - Ordio",
        "description": "...",
        "robots": "noindex, follow"
      }
    }
  ]
}
```

### Topics JSON Format

**Location**: `v2/data/blog/topics.json`

**Schema**:

```json
{
  "topics": [
    {
      "id": "zeiterfassung",
      "name": "Zeiterfassung",
      "description": "Topic description...",
      "post_count": 69,
      "hub_url": "/insights/topics/zeiterfassung/"
    }
  ]
}
```

## URL Routing

### URL Patterns

**Blog Index**: `/insights/` or `/insights/page/{page}/`
**Category Archive**: `/insights/{category}/` or `/insights/{category}/page/{page}/`
**Single Post**: `/insights/{category}/{slug}/`
**Topic Hub**: `/insights/topics/{topic_id}/`

### URL Extraction

**From Request URI**:

```php
$request_uri = $_SERVER['REQUEST_URI'] ?? '';
$path = parse_url($request_uri, PHP_URL_PATH);
$segments = explode('/', trim($path, '/'));
```

**Extract Category & Slug**:

```php
if (count($segments) >= 3 && $segments[0] === 'insights') {
    $category = $segments[1] ?? '';
    $slug = $segments[2] ?? '';
}
```

## SEO Implementation

### Meta Tags

**Generated via**: `render_blog_meta_tags($page_type, $data, $overrides)`

**Page Types**:

- `'index'` - Blog index
- `'category'` - Category archive
- `'post'` - Single post
- `'topic_hub'` - Topic hub

**Includes**:

- Title tag
- Meta description
- Robots meta
- Canonical URL
- Open Graph tags
- Twitter Card tags
- Article-specific tags (for posts)

### Schema Markup

**Generated via**: `render_blog_schema($page_type, $data, $overrides)`

**Schema Types**:

- **WebPage** - All pages
- **CollectionPage** - Category archives
- **Article** - Single posts
- **BreadcrumbList** - All pages
- **Person** - Authors (for posts)
- **ImageObject** - Featured images

## Performance Optimization

### Image Optimization

**Features**:

- Responsive srcset
- WebP format
- Lazy loading (below fold)
- fetchpriority="high" (hero images)
- Explicit width/height attributes

**Implementation**:

```php
<img
    src="<?php echo $image['src']; ?>"
    srcset="<?php echo generate_srcset($image['srcset']); ?>"
    sizes="(max-width: 640px) 640px, 1024px"
    alt="<?php echo $image['alt']; ?>"
    width="<?php echo $image['width']; ?>"
    height="<?php echo $image['height']; ?>"
    loading="lazy"
    fetchpriority="high"
/>
```

### Caching Strategy

**Post Data Caching**:

- Cache loaded post data in memory
- Invalidate on file modification
- Use `filemtime()` for cache invalidation

**Template Caching**:

- Cache rendered templates
- Invalidate on data changes
- Use cache headers for static content

## Accessibility

### Semantic HTML

**Elements Used**:

- `<article>` - Post cards and content
- `<header>` - Post headers
- `<nav>` - Navigation and pagination
- `<main>` - Main content area
- `<time>` - Publication dates
- `<section>` - Content sections

### ARIA Attributes

**Labels**:

- `aria-label` - Navigation labels
- `aria-current` - Active states
- `aria-disabled` - Disabled pagination links

**Landmarks**:

- `role="navigation"` - Navigation elements
- `role="main"` - Main content
- `role="article"` - Blog posts

### Keyboard Navigation

**Features**:

- Visible focus states
- Tab order logical
- Skip links (if needed)
- Keyboard-accessible pagination

## Testing Checklist

### Functional Testing

- [ ] All templates render correctly
- [ ] Pagination works (previous/next, page numbers)
- [ ] Category navigation works
- [ ] Related posts display correctly
- [ ] Links work (internal and external)
- [ ] Images load correctly
- [ ] Responsive design works

### SEO Testing

- [ ] Meta tags present and correct
- [ ] Schema markup valid (Google Rich Results Test)
- [ ] Canonical URLs correct
- [ ] Robots meta correct
- [ ] Open Graph tags correct
- [ ] Twitter Card tags correct

### Performance Testing

- [ ] LCP < 2.5s
- [ ] CLS < 0.1
- [ ] FID < 100ms
- [ ] Images optimized (WebP, srcset)
- [ ] CSS/JS minified
- [ ] Caching works

### Accessibility Testing

- [ ] Semantic HTML correct
- [ ] ARIA attributes present
- [ ] Keyboard navigation works
- [ ] Screen reader compatible
- [ ] Color contrast WCAG AA compliant
- [ ] Focus states visible

## Related Documentation

- [Component API](COMPONENT_API.md) - Detailed component documentation
- [Data Structure Mapping](../DATA_STRUCTURE_MAPPING.md) - WordPress to PHP data mapping
- [Blog Template Best Practices](BLOG_TEMPLATE_BEST_PRACTICES.md) - Best practices guide
- [Migration Architecture](MIGRATION_ARCHITECTURE.md) - Technical architecture
- [Frontend Patterns Extraction](FRONTEND_PATTERNS_EXTRACTION.md) - Extracted patterns
