Files
igny8/docs/wp/WP-CONTENT-TEMPLATE-IMPLEMENTATION-PLAN.md
2025-12-03 18:12:01 +05:00

1145 lines
30 KiB
Markdown

# WordPress Content Template Implementation Plan
**Date:** December 1, 2025
**Purpose:** Create a custom WordPress post template for IGNY8-generated content that mirrors the igny8 app ContentViewTemplate
---
## 📋 Overview
We need to create a **custom single post template** in WordPress that:
1. Only applies to posts where `_igny8_content_id` meta exists (IGNY8-generated content)
2. Mirrors the design and structure of `/writer/content/[id]` from the igny8 app
3. Uses WordPress theme colors and fonts (fully theme-compatible)
4. Works with any WordPress theme without breaking appearance
---
## 🔍 Analysis: Current IGNY8 App Template
### Key Components in ContentViewTemplate.tsx
#### 1. **Header Section**
- Back button
- Post title (large, prominent)
- Status badge (draft/review/publish)
- Metadata row: Created date, word count, tags, categories
- SEO metadata (meta title, meta description)
#### 2. **Content Organization**
- **Featured Image Block**
- Large hero image with rounded corners
- Image status pill (generated/pending/failed)
- Prompt display (if available)
- **Introduction Section**
- "Opening Narrative" label
- First paragraph(s) before first H2
- **Content Sections** (Dynamic, one per H2)
- Section number badge
- Section heading (H2)
- Section content (parsed HTML)
- In-article image (side-by-side on large screens)
- Image prompts displayed
- **Metadata Footer**
- Collapsible JSON metadata viewer
#### 3. **Design Characteristics**
- Max width: 1200px, centered
- Rounded corners (rounded-3xl = 24px)
- Soft shadows and borders
- Gradient backgrounds
- Prose typography (1.85 line-height, 1.05rem base)
- Responsive grid layouts
---
## 🎨 WordPress Implementation Strategy
### File Structure
```
igny8-wp-plugin/
templates/
single-igny8-content.php # Main template file
parts/
igny8-header.php # Header with metadata
igny8-featured-image.php # Featured image block
igny8-content-sections.php # Parsed content sections
igny8-metadata.php # SEO and metadata footer
assets/
css/
igny8-content-template.css # Template styles
js/
igny8-content-template.js # JavaScript (if needed)
```
---
## 📊 Data Mapping: IGNY8 → WordPress
### Meta Fields Available in WordPress
(All stored via `update_post_meta()` during sync)
| IGNY8 Field | WordPress Meta Key | Usage in Template |
|-------------|-------------------|-------------------|
| `content_id` | `_igny8_content_id` | ✅ Identifier (required to activate template) |
| `task_id` | `_igny8_task_id` | Link back to IGNY8 |
| `content_type` | `_igny8_content_type` | Display content type badge |
| `content_structure` | `_igny8_content_structure` | Article/Guide/Hub indicator |
| `source` | `_igny8_source` | Show content source |
| `primary_keyword` | `_igny8_primary_keyword` | Display in metadata |
| `secondary_keywords` | `_igny8_secondary_keywords` | Display as tags/badges |
| `cluster_name` | `_igny8_cluster_name` | Show cluster info |
| `cluster_id` | `_igny8_cluster_id` | - |
| `sector_id` | `_igny8_sector_id` | - |
| `meta_title` | `_igny8_meta_title` | SEO title display |
| `meta_description` | `_igny8_meta_description` | SEO description display |
| `last_sync` | `_igny8_last_synced` | Show last sync time |
| Featured image | `_thumbnail_id` | Standard WP featured image |
| Gallery images | `_igny8_gallery_images` | Array of image IDs |
| In-article images | `_igny8_imported_images` | Array of image data with prompts |
### WordPress Native Fields
| Field | How to Access | Usage |
|-------|--------------|-------|
| Title | `get_the_title()` | Main heading |
| Content | `get_the_content()` | Post content HTML |
| Excerpt | `get_the_excerpt()` | - |
| Author | `get_the_author()` | Display author |
| Date | `get_the_date()` | Created date |
| Modified | `get_the_modified_date()` | Last updated |
| Categories | `get_the_category()` | Category badges |
| Tags | `get_the_tags()` | Tag badges |
| Word Count | `str_word_count(strip_tags())` | Calculate |
| Status | `get_post_status()` | draft/publish |
---
## 🎯 Implementation Plan
### Phase 1: Template Detection & Loading
**File:** `igny8-wp-plugin/includes/class-template-loader.php`
```php
class Igny8_Template_Loader {
public function __construct() {
add_filter('single_template', [$this, 'load_igny8_template'], 99);
add_action('wp_enqueue_scripts', [$this, 'enqueue_template_styles']);
}
public function load_igny8_template($template) {
global $post;
// Only for single posts
if (!is_singular('post')) {
return $template;
}
// Check if post has IGNY8 content ID
$content_id = get_post_meta($post->ID, '_igny8_content_id', true);
if (empty($content_id)) {
return $template; // Use default theme template
}
// Load our custom template
$custom_template = plugin_dir_path(dirname(__FILE__)) . 'templates/single-igny8-content.php';
if (file_exists($custom_template)) {
return $custom_template;
}
return $template;
}
public function enqueue_template_styles() {
global $post;
if (!is_singular('post')) {
return;
}
$content_id = get_post_meta($post->ID, '_igny8_content_id', true);
if (empty($content_id)) {
return;
}
// Enqueue custom styles
wp_enqueue_style(
'igny8-content-template',
plugin_dir_url(dirname(__FILE__)) . 'templates/assets/css/igny8-content-template.css',
array(),
'1.0.0'
);
// Enqueue custom JS if needed
wp_enqueue_script(
'igny8-content-template',
plugin_dir_url(dirname(__FILE__)) . 'templates/assets/js/igny8-content-template.js',
array('jquery'),
'1.0.0',
true
);
}
}
new Igny8_Template_Loader();
```
---
### Phase 2: Main Template Structure
**File:** `templates/single-igny8-content.php`
```php
<?php
/**
* Single Post Template for IGNY8-Generated Content
*
* This template is automatically loaded for posts that have _igny8_content_id meta.
* It mirrors the design of the IGNY8 app content view template.
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
get_header();
// Get IGNY8 metadata
$content_id = get_post_meta(get_the_ID(), '_igny8_content_id', true);
$task_id = get_post_meta(get_the_ID(), '_igny8_task_id', true);
$content_type = get_post_meta(get_the_ID(), '_igny8_content_type', true);
$structure = get_post_meta(get_the_ID(), '_igny8_content_structure', true);
$primary_keyword = get_post_meta(get_the_ID(), '_igny8_primary_keyword', true);
$secondary_keywords = get_post_meta(get_the_ID(), '_igny8_secondary_keywords', true);
$cluster_name = get_post_meta(get_the_ID(), '_igny8_cluster_name', true);
$meta_title = get_post_meta(get_the_ID(), '_igny8_meta_title', true);
$meta_description = get_post_meta(get_the_ID(), '_igny8_meta_description', true);
$gallery_images = get_post_meta(get_the_ID(), '_igny8_gallery_images', true);
$imported_images = get_post_meta(get_the_ID(), '_igny8_imported_images', true);
// Parse secondary keywords
$keywords_array = !empty($secondary_keywords) ? explode(',', $secondary_keywords) : [];
// Get WordPress data
$categories = get_the_category();
$tags = get_the_tags();
$word_count = str_word_count(strip_tags(get_the_content()));
?>
<div class="igny8-content-wrapper">
<div class="igny8-content-container">
<?php
// Include template parts
include plugin_dir_path(__FILE__) . 'parts/igny8-header.php';
include plugin_dir_path(__FILE__) . 'parts/igny8-featured-image.php';
include plugin_dir_path(__FILE__) . 'parts/igny8-content-sections.php';
include plugin_dir_path(__FILE__) . 'parts/igny8-metadata.php';
?>
</div>
</div>
<?php
get_footer();
```
---
### Phase 3: Template Parts
#### 3.1 Header Part
**File:** `templates/parts/igny8-header.php`
```php
<?php
/**
* IGNY8 Content Header
* Displays title, status, and metadata
*/
$status = get_post_status();
$status_labels = [
'draft' => 'Draft',
'pending' => 'Pending Review',
'publish' => 'Published',
'private' => 'Private'
];
$status_label = $status_labels[$status] ?? ucfirst($status);
?>
<div class="igny8-header">
<!-- Back Button -->
<div class="igny8-header-back">
<a href="<?php echo esc_url(get_post_type_archive_link('post')); ?>" class="igny8-back-button">
<span class="igny8-back-icon">←</span>
<span>Back to Posts</span>
</a>
</div>
<!-- Title & Status -->
<div class="igny8-header-title-row">
<h1 class="igny8-title"><?php the_title(); ?></h1>
<span class="igny8-status-badge igny8-status-<?php echo esc_attr($status); ?>">
<?php echo esc_html($status_label); ?>
</span>
</div>
<!-- Metadata Row -->
<div class="igny8-metadata-row">
<!-- Created Date -->
<div class="igny8-meta-item">
<span class="igny8-meta-icon">📅</span>
<span class="igny8-meta-value"><?php echo get_the_date(); ?></span>
</div>
<!-- Word Count -->
<div class="igny8-meta-item">
<span class="igny8-meta-icon">📝</span>
<span class="igny8-meta-value"><?php echo number_format($word_count); ?> words</span>
</div>
<!-- Categories -->
<?php if ($categories): ?>
<div class="igny8-meta-item">
<span class="igny8-meta-icon">📁</span>
<div class="igny8-meta-badges">
<?php foreach ($categories as $cat): ?>
<span class="igny8-category-badge"><?php echo esc_html($cat->name); ?></span>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- Tags -->
<?php if ($tags): ?>
<div class="igny8-meta-item">
<span class="igny8-meta-icon">🏷️</span>
<div class="igny8-meta-badges">
<?php foreach ($tags as $tag): ?>
<span class="igny8-tag-badge"><?php echo esc_html($tag->name); ?></span>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<!-- SEO Metadata (if different from post title) -->
<?php if ($meta_title && $meta_title !== get_the_title()): ?>
<div class="igny8-seo-section">
<div class="igny8-seo-item">
<label class="igny8-seo-label">SEO Title:</label>
<div class="igny8-seo-value"><?php echo esc_html($meta_title); ?></div>
</div>
</div>
<?php endif; ?>
<?php if ($meta_description): ?>
<div class="igny8-seo-section">
<div class="igny8-seo-item">
<label class="igny8-seo-label">Meta Description:</label>
<div class="igny8-seo-value"><?php echo esc_html($meta_description); ?></div>
</div>
</div>
<?php endif; ?>
<!-- IGNY8 Specific Info -->
<?php if ($cluster_name || $primary_keyword): ?>
<div class="igny8-info-section">
<?php if ($cluster_name): ?>
<div class="igny8-info-item">
<label>Cluster:</label>
<span><?php echo esc_html($cluster_name); ?></span>
</div>
<?php endif; ?>
<?php if ($primary_keyword): ?>
<div class="igny8-info-item">
<label>Primary Keyword:</label>
<span><?php echo esc_html($primary_keyword); ?></span>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
```
#### 3.2 Featured Image Part
**File:** `templates/parts/igny8-featured-image.php`
```php
<?php
/**
* IGNY8 Featured Image Block
*/
$featured_image_id = get_post_thumbnail_id();
if (!$featured_image_id) {
return;
}
$image_url = wp_get_attachment_image_url($featured_image_id, 'full');
$image_alt = get_post_meta($featured_image_id, '_wp_attachment_image_alt', true);
// Get image prompt if available from imported images data
$image_prompt = '';
if (!empty($imported_images) && is_array($imported_images)) {
foreach ($imported_images as $img) {
if (isset($img['is_featured']) && $img['is_featured']) {
$image_prompt = $img['prompt'] ?? '';
break;
}
}
}
?>
<div class="igny8-featured-image-block">
<div class="igny8-featured-header">
<span class="igny8-featured-label">Featured Visual</span>
</div>
<div class="igny8-featured-image-wrapper">
<img src="<?php echo esc_url($image_url); ?>"
alt="<?php echo esc_attr($image_alt ?: get_the_title()); ?>"
class="igny8-featured-image"
loading="lazy">
</div>
<?php if ($image_prompt): ?>
<div class="igny8-image-prompt">
<p class="igny8-prompt-label">AI Image Prompt</p>
<p class="igny8-prompt-text"><?php echo esc_html($image_prompt); ?></p>
</div>
<?php endif; ?>
</div>
```
#### 3.3 Content Sections Part
**File:** `templates/parts/igny8-content-sections.php`
```php
<?php
/**
* IGNY8 Content Sections
* Parses content HTML and displays sections with in-article images
*/
$content = get_the_content();
$content = apply_filters('the_content', $content);
// Parse content into intro and H2 sections
$sections = igny8_parse_content_sections($content);
// Get in-article images
$in_article_images = [];
if (!empty($imported_images) && is_array($imported_images)) {
foreach ($imported_images as $img) {
if (!isset($img['is_featured']) || !$img['is_featured']) {
$position = $img['position'] ?? count($in_article_images) + 1;
$in_article_images[$position] = $img;
}
}
}
?>
<div class="igny8-content-body">
<!-- Introduction (content before first H2) -->
<?php if (!empty($sections['intro'])): ?>
<section class="igny8-intro-section">
<div class="igny8-section-label">Opening Narrative</div>
<div class="igny8-prose">
<?php echo $sections['intro']; ?>
</div>
</section>
<?php endif; ?>
<!-- H2 Sections with Images -->
<?php if (!empty($sections['sections'])): ?>
<?php foreach ($sections['sections'] as $index => $section): ?>
<section class="igny8-content-section" id="section-<?php echo $index + 1; ?>">
<div class="igny8-section-container">
<div class="igny8-section-header">
<span class="igny8-section-number"><?php echo $index + 1; ?></span>
<div class="igny8-section-heading-wrapper">
<span class="igny8-section-label">Section Spotlight</span>
<h2 class="igny8-section-heading"><?php echo esc_html($section['heading']); ?></h2>
</div>
</div>
<div class="igny8-section-content<?php echo isset($in_article_images[$index + 1]) ? ' igny8-has-image' : ''; ?>">
<div class="igny8-section-text">
<div class="igny8-prose">
<?php echo $section['content']; ?>
</div>
</div>
<?php if (isset($in_article_images[$index + 1])):
$img_data = $in_article_images[$index + 1];
$img_id = $img_data['attachment_id'] ?? null;
if ($img_id):
$img_url = wp_get_attachment_image_url($img_id, 'large');
$img_alt = get_post_meta($img_id, '_wp_attachment_image_alt', true);
$img_prompt = $img_data['prompt'] ?? '';
?>
<div class="igny8-section-image">
<figure class="igny8-image-figure">
<img src="<?php echo esc_url($img_url); ?>"
alt="<?php echo esc_attr($img_alt ?: $section['heading']); ?>"
class="igny8-in-article-image"
loading="lazy">
<?php if ($img_prompt): ?>
<figcaption class="igny8-image-caption">
<p class="igny8-caption-label">Visual Direction</p>
<p class="igny8-caption-text"><?php echo esc_html($img_prompt); ?></p>
</figcaption>
<?php endif; ?>
</figure>
</div>
<?php endif; endif; ?>
</div>
</div>
</section>
<?php endforeach; ?>
<?php endif; ?>
</div>
```
#### 3.4 Metadata Footer Part
**File:** `templates/parts/igny8-metadata.php`
```php
<?php
/**
* IGNY8 Metadata Footer
* Shows IGNY8-specific metadata in collapsible format
*/
// Only show if we have IGNY8 content ID
if (!$content_id) {
return;
}
?>
<div class="igny8-metadata-footer">
<details class="igny8-metadata-details">
<summary class="igny8-metadata-summary">
View IGNY8 Metadata
</summary>
<div class="igny8-metadata-content">
<table class="igny8-metadata-table">
<tr>
<th>Content ID:</th>
<td><?php echo esc_html($content_id); ?></td>
</tr>
<?php if ($task_id): ?>
<tr>
<th>Task ID:</th>
<td><?php echo esc_html($task_id); ?></td>
</tr>
<?php endif; ?>
<?php if ($content_type): ?>
<tr>
<th>Content Type:</th>
<td><?php echo esc_html($content_type); ?></td>
</tr>
<?php endif; ?>
<?php if ($structure): ?>
<tr>
<th>Structure:</th>
<td><?php echo esc_html($structure); ?></td>
</tr>
<?php endif; ?>
<?php if ($keywords_array): ?>
<tr>
<th>Secondary Keywords:</th>
<td>
<?php foreach ($keywords_array as $kw): ?>
<span class="igny8-keyword-tag"><?php echo esc_html(trim($kw)); ?></span>
<?php endforeach; ?>
</td>
</tr>
<?php endif; ?>
<tr>
<th>Last Synced:</th>
<td><?php echo get_post_meta(get_the_ID(), '_igny8_last_synced', true) ?: 'Never'; ?></td>
</tr>
</table>
</div>
</details>
</div>
```
---
### Phase 4: Helper Functions
**File:** `includes/template-functions.php`
```php
<?php
/**
* Helper functions for IGNY8 content template
*/
/**
* Parse content HTML into intro and H2 sections
*
* @param string $content HTML content
* @return array ['intro' => string, 'sections' => array]
*/
function igny8_parse_content_sections($content) {
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML('<?xml encoding="UTF-8">' . $content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_clear_errors();
$intro_html = '';
$sections = [];
$current_section = null;
foreach ($dom->getElementsByTagName('body')->item(0)->childNodes as $node) {
if ($node->nodeName === 'h2') {
// Save previous section
if ($current_section !== null) {
$sections[] = $current_section;
}
// Start new section
$current_section = [
'heading' => $node->textContent,
'content' => ''
];
} elseif ($current_section !== null) {
// Add to current section
$current_section['content'] .= $dom->saveHTML($node);
} else {
// Add to intro
$intro_html .= $dom->saveHTML($node);
}
}
// Save last section
if ($current_section !== null) {
$sections[] = $current_section;
}
return [
'intro' => $intro_html,
'sections' => $sections
];
}
```
---
### Phase 5: CSS Styling
**File:** `templates/assets/css/igny8-content-template.css`
```css
/**
* IGNY8 Content Template Styles
* Theme-compatible styles using CSS custom properties
*/
:root {
--igny8-max-width: 1200px;
--igny8-spacing: 2rem;
--igny8-border-radius: 24px;
--igny8-border-radius-sm: 16px;
--igny8-border-radius-xs: 8px;
}
/* Use theme's colors as fallback */
.igny8-content-wrapper {
background: var(--wp--preset--color--background, #f9fafb);
color: var(--wp--preset--color--foreground, #1f2937);
padding: var(--igny8-spacing) 0;
font-family: inherit; /* Inherit theme font */
}
.igny8-content-container {
max-width: var(--igny8-max-width);
margin: 0 auto;
padding: 0 1rem;
}
/* Header Styles */
.igny8-header {
background: var(--wp--preset--color--base, #ffffff);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: var(--igny8-border-radius);
padding: var(--igny8-spacing);
margin-bottom: var(--igny8-spacing);
box-shadow: 0 10px 25px -10px rgba(0, 0, 0, 0.1);
}
.igny8-back-button {
display: inline-flex;
align-items: center;
gap: 0.5rem;
color: inherit;
text-decoration: none;
font-size: 0.875rem;
opacity: 0.7;
transition: opacity 0.2s;
}
.igny8-back-button:hover {
opacity: 1;
}
.igny8-header-title-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
margin: 1.5rem 0;
}
.igny8-title {
font-size: 2.5rem;
font-weight: 700;
line-height: 1.2;
margin: 0;
color: inherit;
}
.igny8-status-badge {
display: inline-flex;
align-items: center;
padding: 0.5rem 1rem;
border-radius: var(--igny8-border-radius-xs);
font-size: 0.875rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.igny8-status-draft {
background: rgba(251, 191, 36, 0.15);
color: rgba(180, 83, 9, 1);
}
.igny8-status-publish {
background: rgba(16, 185, 129, 0.15);
color: rgba(5, 150, 105, 1);
}
.igny8-metadata-row {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
padding-top: 1rem;
border-top: 1px solid rgba(0, 0, 0, 0.08);
}
.igny8-meta-item {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
}
.igny8-meta-badges {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.igny8-category-badge,
.igny8-tag-badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: var(--igny8-border-radius-xs);
font-size: 0.75rem;
background: rgba(0, 0, 0, 0.05);
color: inherit;
}
/* Featured Image */
.igny8-featured-image-block {
background: var(--wp--preset--color--base, #ffffff);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: var(--igny8-border-radius);
overflow: hidden;
margin-bottom: var(--igny8-spacing);
box-shadow: 0 10px 25px -10px rgba(0, 0, 0, 0.1);
}
.igny8-featured-header {
padding: 2rem 2rem 1rem;
}
.igny8-featured-label {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.2em;
opacity: 0.6;
}
.igny8-featured-image {
width: 100%;
height: auto;
display: block;
}
.igny8-image-prompt {
padding: 1.5rem 2rem;
border-top: 1px solid rgba(0, 0, 0, 0.08);
background: rgba(0, 0, 0, 0.02);
}
.igny8-prompt-label {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.15em;
opacity: 0.5;
margin-bottom: 0.75rem;
}
.igny8-prompt-text {
font-size: 0.875rem;
line-height: 1.6;
margin: 0;
}
/* Content Sections */
.igny8-content-body {
display: flex;
flex-direction: column;
gap: 3rem;
}
.igny8-intro-section,
.igny8-content-section {
background: var(--wp--preset--color--base, #ffffff);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: var(--igny8-border-radius);
overflow: hidden;
box-shadow: 0 10px 25px -10px rgba(0, 0, 0, 0.1);
}
.igny8-intro-section {
padding: 2rem;
}
.igny8-section-label {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.2em;
opacity: 0.6;
margin-bottom: 1.5rem;
}
.igny8-section-container {
padding: 2.5rem;
}
.igny8-section-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 2rem;
}
.igny8-section-number {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
background: rgba(59, 130, 246, 0.1);
color: rgba(37, 99, 235, 1);
font-weight: 600;
font-size: 0.875rem;
flex-shrink: 0;
}
.igny8-section-heading {
font-size: 2rem;
font-weight: 700;
margin: 0;
color: inherit;
}
.igny8-section-content {
display: grid;
gap: 2.5rem;
}
.igny8-section-content.igny8-has-image {
grid-template-columns: 1fr;
}
@media (min-width: 1024px) {
.igny8-section-content.igny8-has-image {
grid-template-columns: 3fr 2fr;
}
}
/* Prose Styles */
.igny8-prose {
font-size: 1.0625rem;
line-height: 1.85;
color: inherit;
}
.igny8-prose h2,
.igny8-prose h3,
.igny8-prose h4 {
margin-top: 2.5rem;
margin-bottom: 1.25rem;
font-weight: 600;
color: inherit;
}
.igny8-prose h3 {
font-size: 1.6rem;
}
.igny8-prose h4 {
font-size: 1.35rem;
}
.igny8-prose p {
margin-bottom: 1.3rem;
}
.igny8-prose ul,
.igny8-prose ol {
margin-bottom: 1.5rem;
padding-left: 1.75rem;
}
.igny8-prose li {
margin-bottom: 0.6rem;
}
.igny8-prose a {
color: inherit;
text-decoration: underline;
text-decoration-color: rgba(0, 0, 0, 0.3);
transition: text-decoration-color 0.2s;
}
.igny8-prose a:hover {
text-decoration-color: rgba(0, 0, 0, 0.6);
}
.igny8-prose img {
max-width: 100%;
height: auto;
border-radius: var(--igny8-border-radius-sm);
margin: 1.75rem auto;
}
/* In-Article Images */
.igny8-image-figure {
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: var(--igny8-border-radius-sm);
overflow: hidden;
background: rgba(0, 0, 0, 0.02);
}
.igny8-in-article-image {
width: 100%;
height: auto;
display: block;
}
.igny8-image-caption {
padding: 1.25rem;
border-top: 1px solid rgba(0, 0, 0, 0.08);
}
.igny8-caption-label {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.15em;
opacity: 0.5;
margin-bottom: 0.75rem;
}
.igny8-caption-text {
font-size: 0.875rem;
line-height: 1.6;
margin: 0;
}
/* Metadata Footer */
.igny8-metadata-footer {
margin-top: 3rem;
padding: 2rem;
background: rgba(0, 0, 0, 0.02);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: var(--igny8-border-radius);
}
.igny8-metadata-summary {
cursor: pointer;
font-weight: 600;
font-size: 0.875rem;
user-select: none;
}
.igny8-metadata-summary:hover {
opacity: 0.7;
}
.igny8-metadata-content {
margin-top: 1.5rem;
padding: 1.5rem;
background: var(--wp--preset--color--base, #ffffff);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: var(--igny8-border-radius-xs);
}
.igny8-metadata-table {
width: 100%;
border-collapse: collapse;
}
.igny8-metadata-table th,
.igny8-metadata-table td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.igny8-metadata-table th {
font-weight: 600;
width: 30%;
}
.igny8-keyword-tag {
display: inline-block;
padding: 0.25rem 0.75rem;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
border-radius: var(--igny8-border-radius-xs);
background: rgba(0, 0, 0, 0.05);
font-size: 0.875rem;
}
/* Responsive */
@media (max-width: 768px) {
.igny8-title {
font-size: 1.875rem;
}
.igny8-section-heading {
font-size: 1.5rem;
}
.igny8-prose {
font-size: 1rem;
}
}
```
---
## 🎨 Theme Compatibility Strategy
### 1. **Use Theme Colors**
- Rely on `inherit` for text colors
- Use CSS custom properties with fallbacks
- Detect theme colors: `var(--wp--preset--color--foreground, fallback)`
### 2. **Use Theme Fonts**
- Always use `font-family: inherit`
- Let theme control all typography
### 3. **Minimal Override**
- Only style structure and spacing
- Don't set absolute colors unless necessary
- Use `rgba()` with opacity for neutrals
### 4. **Dark Mode Support**
```css
@media (prefers-color-scheme: dark) {
.igny8-content-wrapper {
background: var(--wp--preset--color--background, #111827);
}
/* Adjust opacity for better contrast */
}
```
---
## ✅ Implementation Checklist
### Backend
- [ ] Create `class-template-loader.php`
- [ ] Add `template-functions.php` with parser
- [ ] Register template loader in main plugin file
- [ ] Test template detection logic
### Templates
- [ ] Create `single-igny8-content.php`
- [ ] Create `parts/igny8-header.php`
- [ ] Create `parts/igny8-featured-image.php`
- [ ] Create `parts/igny8-content-sections.php`
- [ ] Create `parts/igny8-metadata.php`
### Styles & Assets
- [ ] Create `igny8-content-template.css`
- [ ] Add responsive breakpoints
- [ ] Test with multiple themes (Twenty Twenty-Four, Astra, GeneratePress)
- [ ] Add dark mode support
### Testing
- [ ] Test with posts that have `_igny8_content_id`
- [ ] Test with posts without `_igny8_content_id` (should use default theme)
- [ ] Test image display (featured + in-article)
- [ ] Test section parsing with various HTML structures
- [ ] Test metadata display
- [ ] Test on mobile devices
### Documentation
- [ ] Add usage docs to plugin README
- [ ] Document theme compatibility
- [ ] Create troubleshooting guide
---
## 🚀 Next Steps
1. **Review this plan** - Confirm approach and structure
2. **Create base template files** - Start with template loader
3. **Implement template parts** - One by one
4. **Style incrementally** - Test as we go
5. **Iterate based on testing** - Refine for different themes
---
## ⚠️ Important Notes
1. **Non-destructive**: Only affects IGNY8-generated posts
2. **Theme-first**: Always respect theme's design decisions
3. **Graceful fallback**: If something fails, show basic content
4. **Performance**: Minimal CSS, no heavy JavaScript
5. **Accessibility**: Semantic HTML, proper ARIA labels