# Blog Image Alt Text Guidelines

**Last Updated:** 2026-03-17

Accessibility and SEO best practices for blog image alt text (WCAG, NN/g, Moz, 2024–2025).

**Audit/fix alignment (2026-03-17):** `audit-blog-image-alt.php` and `fix-featured-image-alt.php` use identical deduplication logic. Both check if title ends with `\bOrdio\s*$`; if so, expected alt = title as-is; else append ` | Ordio`. Truncate to 150 chars. This ensures audit reports match what the fix script would apply.

## Standards

### Featured Images

**Format:** `{Post Title} | Ordio` (max 150 chars), unless the title already ends with Ordio

- Post title = H1/descriptive title from post JSON
- **Deduplication:** If the title already ends with Ordio (e.g. ` - Ordio`, ` x Ordio`, ` für Ordio`), use the title as-is for alt. Otherwise append ` | Ordio`. This avoids `Ordio | Ordio` or ` - Ordio | Ordio`.
- Aligns with `PostHeader.php` fallback: `$image_alt = $image_data['alt'] ?: $title`
- Never start with "image of", "photo of", "picture of", "bild von", "foto von"
- Always include `alt` attribute (even if empty for decorative images)

### General Best Practices

- **Length:** ~150 characters max; front-load important words
- **Informative images:** Describe unique value; avoid redundancy with surrounding text
- **Decorative images:** Use `alt=""` (empty)
- **Functional images (links):** Describe destination/action, not appearance
- **Context-dependent:** Same image can have different alt in different contexts

## Featured Image Workflow

1. **New posts:** `create-new-blog-post.php` sets `featured_image.alt = title + " | Ordio"` (or title as-is if it already ends with Ordio)
2. **Optimize:** `optimize-blog-featured-image.py` preserves existing alt; if empty, uses `{title} | Ordio` (truncates to 150 chars if needed)
3. **Batch fix:** `fix-featured-image-alt.php --all` or `--post=slug --category=lexikon` (applies deduplication rule)
4. **Audit:** `audit-blog-image-alt.php` reports mismatches, empty, too long, bad prefixes
5. **Validate:** `validate-new-post.php` checks alt non-empty, ≤ 150 chars, no bad prefix

## Inline Content Images

- Sourced from WordPress extraction; alt is embedded in `<img>` tags
- **Audit:** `python3 v2/scripts/blog/audit-content-image-alt.py`
- Same rules: present, non-empty (unless decorative), ≤ 150 chars, no bad prefixes

## Images Array (Legacy)

- Many have `alt: ""` (type: featured duplicate) or topic labels
- **Not rendered** in current PostContent (content HTML is pre-processed)
- If used for carousel: functional images → alt describes destination; decorative → `alt=""`

## Scripts Reference

| Script | Purpose |
|--------|---------|
| `audit-blog-image-alt.php` | Audit featured image alt |
| `audit-content-image-alt.py` | Audit inline content images |
| `audit-image-dimensions.py` | Audit content images for missing width/height |
| `fix-featured-image-alt.php` | Batch fix featured alt |
| `fix-image-attributes.py` | Add alt, width, height, loading to HTML `<img>` in content |
| `fix-content-image-dimensions.py` | Add dimensions to `images` array (filename, SVG defaults, PIL) |
| `validate-featured-image-alt.php` | Standalone alt validation |
| `validate-new-post.php` | Includes alt validation |

## Related

- [IMAGE_ALT_AUDIT_REPORT.md](IMAGE_ALT_AUDIT_REPORT.md)
- [BLOG_FEATURED_IMAGE_GENERATION.md](BLOG_FEATURED_IMAGE_GENERATION.md)
- [BLOG_CONTENT_EDIT_WORKFLOW.md](BLOG_CONTENT_EDIT_WORKFLOW.md)
- `.cursor/rules/blog-featured-image-style.mdc`
