# Blog Table of Contents Migration Guide

**Last Updated:** 2026-01-18

Documentation of changes made to add table of contents functionality to blog posts.

## Overview

Added automatic table of contents (INHALT) component to all blog posts. The TOC dynamically extracts headings from post content, generates IDs, and provides scroll spy functionality.

## Changes Made

### New Files

1. **`v2/components/blog/BlogTOC.php`**
   - PHP component for rendering TOC
   - Accepts TOC items array and renders navigation

2. **`v2/js/blog-toc.js`**
   - Alpine.js component for scroll spy
   - Uses Intersection Observer API
   - Handles smooth scrolling and active section highlighting

3. **`docs/content/blog/components/BLOG_TOC_COMPONENT.md`**
   - Component documentation
   - API reference
   - Usage examples

4. **`docs/content/blog/BLOG_TOC_MIGRATION.md`**
   - This file
   - Migration documentation

5. **`v2/scripts/blog/test-heading-extraction.php`**
   - Test script for heading extraction
   - Validates ID generation

6. **`v2/scripts/blog/test-toc-generation.php`**
   - Test script for TOC generation
   - Tests different configuration options

### Modified Files

1. **`v2/config/blog-template-helpers.php`**
   - Added `generate_heading_id()` function
   - Added `extract_headings_from_html()` function
   - Added `add_ids_to_headings()` function
   - Added `build_toc_structure()` function

2. **`v2/pages/blog/post.php`**
   - Added heading extraction logic
   - Added ID generation for headings
   - Added TOC component inclusion
   - Added `blog-toc.js` script loading

3. **`v2/css/blog.css`**
   - Added TOC CSS styles
   - Added responsive behavior
   - Added accessibility styles

## How It Works

### 1. Heading Extraction

When a blog post loads:

1. Extract headings from HTML content using `extract_headings_from_html()`
2. Check which headings already have IDs
3. Generate IDs for headings without IDs using `generate_heading_id()`
4. Add IDs to HTML content using `add_ids_to_headings()`

### 2. TOC Generation

1. Build TOC structure using `build_toc_structure()`
2. Filter headings by level (H2-H3 by default)
3. Check minimum headings requirement (2 by default)
4. Return TOC items array

### 3. Rendering

1. Include `BlogTOC.php` component if TOC items exist
2. Pass TOC items to component
3. Component renders navigation with Alpine.js
4. JavaScript handles scroll spy and interactions

## ID Generation Rules

Headings without IDs get automatically generated IDs following these rules:

1. **Convert to lowercase**
2. **Replace spaces with hyphens**
3. **Handle German characters:**
   - ä → ae
   - ö → oe
   - ü → ue
   - ß → ss
4. **Remove special characters** (keep alphanumeric and hyphens)
5. **Ensure uniqueness** (add `-2`, `-3`, etc. for duplicates)
6. **Limit length** to 100 characters

**Example:**

```
"Heißes Thema: Was ist das?" → "heisses-thema-was-ist-das"
```

## Adding IDs to Existing Posts

IDs are automatically generated when posts are viewed. No manual action needed.

If you want to pre-generate IDs for all posts (optional):

```php
// Script to add IDs to all blog posts
$categories = ['lexikon', 'ratgeber', 'inside-ordio'];
foreach ($categories as $category) {
    $posts = load_blog_posts_by_category($category);
    foreach ($posts as $post) {
        $html = $post['content']['html'] ?? '';
        $headings = extract_headings_from_html($html);
        $html_with_ids = add_ids_to_headings($html, $headings);
        // Save updated HTML back to JSON file
    }
}
```

**Note:** This is optional - IDs are generated on-the-fly when posts are viewed.

## Configuration

### Default Settings

- **Minimum headings:** 2
- **Maximum depth:** H2 and H3 only
- **Scroll threshold:** 600px
- **Mobile:** Hidden (desktop only)

### Customization

See `docs/content/blog/components/BLOG_TOC_COMPONENT.md` for configuration options.

## Testing Checklist

- [x] TOC appears on posts with 2+ headings
- [x] TOC hidden on posts with < 2 headings
- [x] TOC appears after scrolling 600px
- [x] TOC disappears when scrolling back to top
- [x] Active section highlights correctly
- [x] Clicking TOC items smooth scrolls to sections
- [x] IDs are generated correctly for headings
- [x] Duplicate heading text gets unique IDs
- [x] German characters handled correctly
- [x] TOC hidden on mobile devices
- [x] Keyboard navigation works
- [x] Focus states visible
- [x] No JavaScript errors in console
- [x] Performance is acceptable

## Rollback Plan

If issues arise, TOC can be disabled by:

1. **Comment out TOC inclusion** in `v2/pages/blog/post.php`:
   ```php
   // if (!empty($toc_items)) {
   //     include __DIR__ . '/../../components/blog/BlogTOC.php';
   // }
   ```

2. **Remove script loading**:
   ```php
   // $blog_toc_path = __DIR__ . '/../../js/blog-toc.js';
   // if (file_exists($blog_toc_path)) {
   //     echo '<script src="/v2/js/blog-toc.js?v=' . filemtime($blog_toc_path) . '" defer></script>' . "\n";
   // }
   ```

3. **Posts will continue to work** - TOC is non-critical feature

## Performance Impact

- **Minimal impact:** TOC generation is fast (< 1ms)
- **No database queries:** All processing is in-memory
- **Cached:** Heading extraction happens once per page load
- **JavaScript:** Uses Intersection Observer (efficient)

## Browser Compatibility

- **Modern browsers:** Full support (Chrome, Firefox, Safari, Edge)
- **Older browsers:** Fallback to scroll-based detection
- **No JavaScript:** TOC HTML still renders (non-interactive)

## Related Documentation

- [Blog TOC Component Documentation](components/BLOG_TOC_COMPONENT.md)
- [Blog Template Development Guide](guides/TEMPLATE_DEVELOPMENT_GUIDE.md)
- [Heading Hierarchy Guide](HEADING_HIERARCHY_GUIDE.md)
