30 KiB
📐 DESIGN PLAN
1. CSS Container Width Update
.igny8-content-container {
max-width: 1280px; /* Default for screens <= 1600px */
}
@media (min-width: 1600px) {
.igny8-content-container {
max-width: 1530px; /* For screens > 1600px */
}
}
2. WordPress Header Redesign
USER-FACING FIELDS (Keep in Header):
| Field | Display | Icon | Notes |
|---|---|---|---|
| Title | H1 | - | Post title |
| Status Badge | Published/Draft/etc | - | Post status |
| Posted Date | Formatted date | Calendar SVG | Publication date |
| Word Count | Formatted number | Document SVG | Content word count |
| Author | Author name | User SVG | Post author |
| Topic | Cluster name (clickable) | Compass SVG | Display cluster_name as "Topic" |
| Categories | Badge list (Parent > Child clicakble) | Folder SVG | WP Categories |
| Tags | Badge list (clickable) | Tag SVG | WP Tags |
NON-USER FIELDS (Move to Metadata Section - Editor+ only):
- Content ID, Task ID
- Content Type, Structure
- Cluster ID (keep cluster_name as Topic in header)
- Sector ID, Sector Name
- Primary Keyword, Secondary Keywords
- Meta Title, Meta Description
- Source, Last Synced
3. Section Label Redesign
Current: "Section Spotlight" (generic text)
New Approach - Keyword/Tag Matching Algorithm:
-
Source Data:
- Get all WordPress tags assigned to the post
- Get all WordPress categories assigned to the post
- Get primary keyword from post meta
- Get secondary keywords from post meta (if available)
-
Matching Logic:
- For each section heading (H2), perform case-insensitive partial matching
- Check if any tag name appears in the heading text
- Check if any category name appears in the heading text
- Check if primary/secondary keywords appear in the heading text
- Prioritize: Primary Keyword > Tags > Categories > Secondary Keywords
-
Display Rules:
- If matches found: Display up to 2 matched keywords/tags as badges
- If no matches: Display topic (cluster_name) or leave section without label badges
- Never display generic "Section Spotlight" text
-
Badge Styling:
[Primary Match] [Secondary Match] ← styled badges replacing "Section Spotlight"
Colors:
- Primary badge:
theme-color @ 15% opacitybackground,theme-colortext - Secondary badge:
theme-color @ 8% opacitybackground,theme-color @ 80%text
Implementation Function (Pseudo-code):
function igny8_get_section_badges($heading, $post_id) {
$badges = [];
$heading_lower = strtolower($heading);
// Get post taxonomies and keywords
$tags = get_the_tags($post_id);
$categories = get_the_category($post_id);
$primary_kw = get_post_meta($post_id, '_igny8_primary_keyword', true);
$secondary_kws = get_post_meta($post_id, '_igny8_secondary_keywords', true);
// Priority 1: Primary keyword
if ($primary_kw && stripos($heading_lower, strtolower($primary_kw)) !== false) {
$badges[] = ['text' => $primary_kw, 'type' => 'primary'];
}
// Priority 2: Tags
if ($tags && count($badges) < 2) {
foreach ($tags as $tag) {
if (stripos($heading_lower, strtolower($tag->name)) !== false) {
$badges[] = ['text' => $tag->name, 'type' => 'tag'];
if (count($badges) >= 2) break;
}
}
}
// Priority 3: Categories
if ($categories && count($badges) < 2) {
foreach ($categories as $cat) {
if (stripos($heading_lower, strtolower($cat->name)) !== false) {
$badges[] = ['text' => $cat->name, 'type' => 'category'];
if (count($badges) >= 2) break;
}
}
}
// Priority 4: Secondary keywords
if ($secondary_kws && count($badges) < 2) {
$kw_array = is_array($secondary_kws) ? $secondary_kws : explode(',', $secondary_kws);
foreach ($kw_array as $kw) {
$kw = trim($kw);
if (stripos($heading_lower, strtolower($kw)) !== false) {
$badges[] = ['text' => $kw, 'type' => 'keyword'];
if (count($badges) >= 2) break;
}
}
}
return $badges;
}
4. Image Distribution Strategy
Available Images (4 total):
- Position 0: Square (1024×1024)
- Position 1: Landscape (1536×1024 or 1920×1080)
- Position 2: Square (1024×1024)
- Position 3: Landscape (1536×1024 or 1920×1080)
Distribution Plan - First 4 Sections (with descriptions):
| Section | Image Position | Type | Width | Alignment | Description |
|---|---|---|---|---|---|
| Featured | Position 1 | Landscape | 100% max 1024px | Center | Show prompt on first use |
| Section 1 | Position 0 | Square | 50% | Right | With description + widget placeholder below |
| Section 2 | Position 3 | Landscape | 100% max 1024px | Full width | With description |
| Section 3 | Position 2 | Square | 50% | Left | With description + widget placeholder below |
| Section 4 | Position 1 (reuse) | Landscape | 100% max 1024px | Full width | With description |
Distribution Plan - Sections 5-7+ (reuse without descriptions):
| Section | Reuse Image | Type | Width | Alignment | Description |
|---|---|---|---|---|---|
| Section 5 | Featured (pos 1) | Landscape | 100% max 1024px | Full width | NO description |
| Section 6 | Position 0 | Square | 50% | Right | NO description + widget placeholder |
| Section 7 | Position 3 | Landscape | 100% max 1024px | Full width | NO description |
| Section 8+ | Cycle through all 4 | Based on type | Based on type | Based on type | NO description |
Special Case - Tables:
- When section contains
<table>element, always place full-width landscape image BEFORE table - Use next available landscape image (Position 1 or 3)
- Max width: 1024px, centered
- Spacing:
margin-bottom: 2rembefore table - Override normal section pattern when table detected
Image Reuse Rules:
- Images 1-4 used in first 4 sections WITH descriptions/prompts
- Sections 5+ reuse same images WITHOUT descriptions/prompts
- Use CSS classes:
.igny8-image-first-usevs.igny8-image-reuse - Maintain same layout pattern (square = 50%, landscape = 100%)
Widget Placeholders:
- Show only below square images (left/right aligned)
- Empty div with class
.igny8-widget-placeholder - Space reserved for future widget insertion
- Controlled via plugin settings (future implementation)
Implementation Notes:
// Check for table in section content
function igny8_section_has_table($section_html) {
return (stripos($section_html, '<table') !== false);
}
// Get image aspect ratio from position
function igny8_get_image_aspect($position) {
// Even positions (0, 2) = square
// Odd positions (1, 3) = landscape
return ($position % 2 === 0) ? 'square' : 'landscape';
}
// Determine if image should show description
function igny8_show_image_description($section_index) {
// First 4 sections (0-3) show descriptions
return ($section_index < 4);
}
5. Image Alignment with Tables
When section contains a <table>:
- Place landscape image ABOVE the table
- Full width (max 800px)
- Proper spacing:
margin-bottom: 2rem - Table should not wrap around image
6. Responsive Image Width Rules
/* Landscape images */
.igny8-image-landscape {
max-width: 1024px; /* Updated from 800px */
width: 100%;
margin: 0 auto;
display: block;
}
.igny8-image-landscape.igny8-image-reuse {
/* No description shown on reuse */
}
/* Single square image - Right aligned */
.igny8-image-square-right {
max-width: 50%;
margin-left: auto;
float: right;
margin-left: 2rem;
margin-bottom: 2rem;
}
/* Single square image - Left aligned */
.igny8-image-square-left {
max-width: 50%;
margin-right: auto;
float: left;
margin-right: 2rem;
margin-bottom: 2rem;
}
/* Widget placeholder below square images */
.igny8-widget-placeholder {
clear: both;
min-height: 200px;
padding: 1.5rem;
margin-top: 1rem;
background: rgba(0, 0, 0, 0.02);
border: 1px dashed rgba(0, 0, 0, 0.1);
border-radius: 12px;
display: none; /* Hidden by default, shown when widgets enabled */
}
.igny8-widget-placeholder.igny8-widgets-enabled {
display: block;
}
/* Table-specific image positioning */
.igny8-image-before-table {
max-width: 1024px;
width: 100%;
margin: 0 auto 2rem;
display: block;
}
7. Role-Based Visibility
Metadata Section (Bottom):
<?php if (current_user_can('edit_posts')): ?>
<div class="igny8-metadata-footer igny8-editor-only">
<!-- All internal metadata here -->
</div>
<?php endif; ?>
Visible only to:
- Editor
- Administrator
- Author (for their own posts)
8. Header Icon Set (Replace Emojis)
Create inline SVG icons matching theme color:
// Calendar Icon
<svg class="igny8-icon" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"/>
</svg>
// Document Icon (Word Count)
<svg class="igny8-icon" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z"/>
</svg>
// User Icon (Author)
<svg class="igny8-icon" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"/>
</svg>
// Compass Icon (Topic/Cluster)
<svg class="igny8-icon" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z"/>
</svg>
// Folder Icon (Categories)
<svg class="igny8-icon" viewBox="0 0 20 20" fill="currentColor">
<path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"/>
</svg>
// Tag Icon (Tags)
<svg class="igny8-icon" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M17.707 9.293a1 1 0 010 1.414l-7 7a1 1 0 01-1.414 0l-7-7A.997.997 0 012 10V5a3 3 0 013-3h5c.256 0 .512.098.707.293l7 7zM5 6a1 1 0 100-2 1 1 0 000 2z"/>
</svg>
Icon styling:
.igny8-icon {
width: 1rem;
height: 1rem;
color: var(--igny8-theme-color, currentColor);
opacity: 0.8;
display: inline-block;
vertical-align: middle;
}
9. Table of Contents
Position: Below featured image, before intro section
Content: List all H2 headings from content
Features:
- Clickable links with smooth scroll to sections
- Collapsible/expandable (optional)
- Numbered list matching section numbers
- Sticky positioning option (future setting)
Implementation:
function igny8_generate_table_of_contents($content) {
$toc_items = [];
// Parse content for H2 headings
preg_match_all('/<h2[^>]*>(.*?)<\/h2>/i', $content, $matches);
if (!empty($matches[1])) {
foreach ($matches[1] as $index => $heading) {
$heading_text = strip_tags($heading);
$slug = sanitize_title($heading_text);
$toc_items[] = [
'number' => $index + 1,
'text' => $heading_text,
'id' => $slug
];
}
}
return $toc_items;
}
HTML Structure:
<nav class="igny8-table-of-contents">
<div class="igny8-toc-header">
<span class="igny8-toc-icon">📑</span>
<h3>Table of Contents</h3>
</div>
<ol class="igny8-toc-list">
<?php foreach ($toc_items as $item): ?>
<li class="igny8-toc-item">
<a href="#<?php echo esc_attr($item['id']); ?>" class="igny8-toc-link">
<span class="igny8-toc-number"><?php echo $item['number']; ?>.</span>
<span class="igny8-toc-text"><?php echo esc_html($item['text']); ?></span>
</a>
</li>
<?php endforeach; ?>
</ol>
</nav>
CSS:
.igny8-table-of-contents {
background: var(--wp--preset--color--base, #ffffff);
border: 2px solid rgba(0, 0, 0, 0.12);
border-radius: 16px;
padding: 1.5rem 2rem;
margin-bottom: 2rem;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
.igny8-toc-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
.igny8-toc-header h3 {
margin: 0;
font-size: 1.125rem;
font-weight: 600;
}
.igny8-toc-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.igny8-toc-link {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem 0.75rem;
text-decoration: none;
color: inherit;
border-radius: 8px;
transition: background-color 0.2s ease;
}
.igny8-toc-link:hover {
background: rgba(0, 0, 0, 0.04);
}
.igny8-toc-number {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.5rem;
height: 1.5rem;
background: rgba(59, 130, 246, 0.1);
color: rgba(59, 130, 246, 1);
border-radius: 50%;
font-size: 0.75rem;
font-weight: 600;
flex-shrink: 0;
}
.igny8-toc-text {
flex: 1;
font-size: 0.9375rem;
}
Settings (Future Implementation):
// Plugin settings for TOC
$igny8_toc_settings = [
'enabled' => true,
'show_numbers' => true,
'collapsible' => false,
'sticky' => false,
'min_headings' => 3, // Only show if 3+ H2 headings
];
10. Widget System
Widget Placeholders:
Widgets appear below square images (left/right aligned) where there's natural space.
Placeholder Function:
function igny8_render_widget_placeholder($position, $section_index) {
// Check if widgets are enabled in settings
$widgets_enabled = get_option('igny8_widgets_enabled', false);
if (!$widgets_enabled) {
return '';
}
$placeholder_class = 'igny8-widget-placeholder igny8-widgets-enabled';
$placeholder_class .= ' igny8-widget-' . $position; // left or right
$placeholder_class .= ' igny8-widget-section-' . $section_index;
?>
<div class="<?php echo esc_attr($placeholder_class); ?>"
data-widget-position="<?php echo esc_attr($position); ?>"
data-section-index="<?php echo esc_attr($section_index); ?>">
<!-- Widget content will be inserted here via settings -->
<?php do_action('igny8_widget_placeholder', $position, $section_index); ?>
</div>
<?php
}
Widget Settings (Future Implementation):
// Plugin settings for widgets
$igny8_widget_settings = [
'enabled' => false,
'sections' => [
'section_1' => [
'position' => 'right',
'widget_type' => 'related_posts', // or 'custom_html', 'ad_unit', etc.
'content' => '',
],
'section_3' => [
'position' => 'left',
'widget_type' => 'newsletter_signup',
'content' => '',
],
],
];
Widget Types (Future):
- Related Posts
- Newsletter Signup
- Ad Units
- Custom HTML
- Social Share Buttons
- Author Bio
- Call-to-Action Boxes
11. Updated Structure Overview
WordPress Single Post:
┌─────────────────────────────────────────────┐
│ HEADER │
│ ← Back to Posts │
│ │
│ [H1 Title] [Status Badge] │
│ │
│ 📅 Posted: Date 📄 Words 👤 Author │
│ 🧭 Topic: Cluster Name │
│ 📁 [Category Badges] 🏷️ [Tag Badges] │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ FEATURED IMAGE (Landscape, max 1024px) │
│ │
│ [Image Prompt - first use only] │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ TABLE OF CONTENTS │
│ 📑 Table of Contents │
│ 1. Section Heading One │
│ 2. Section Heading Two │
│ 3. Section Heading Three │
│ ... (clickable, smooth scroll) │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ INTRO SECTION │
│ Opening Narrative │
│ [Content...] │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ SECTION 1 │
│ [Keyword Badge] [Tag Badge] │
│ 1 [H2 Heading] │
│ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ │ │ │ │
│ │ Content │ │ Square Image (50%) │ │
│ │ │ │ Right Aligned │ │
│ │ │ │ [Image Description] │ │
│ └──────────────┘ └──────────────────────┘ │
│ [Widget Placeholder] │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ SECTION 2 │
│ [Keyword Badge] │
│ 2 [H2 Heading] │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ Landscape Image (100% max 1024px) │ │
│ │ [Image Description] │ │
│ └───────────────────────────────────────┘ │
│ │
│ [Content...] │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ SECTION 3 │
│ [Keyword Badge] [Tag Badge] │
│ 3 [H2 Heading] │
│ │
│ ┌──────────────────────┐ ┌──────────────┐ │
│ │ │ │ │ │
│ │ Square Image (50%) │ │ Content │ │
│ │ Left Aligned │ │ │ │
│ │ [Image Description] │ │ │ │
│ └──────────────────────┘ └──────────────┘ │
│ [Widget Placeholder] │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ SECTION 4 (with table example) │
│ [Keyword Badge] │
│ 4 [H2 Heading] │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ Landscape Image (100% max 1024px) │ │
│ │ [Image Description] │ │
│ └───────────────────────────────────────┘ │
│ │
│ [Content before table...] │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ TABLE │ │
│ │ [Data rows and columns] │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ SECTION 5 (reuse - no description) │
│ [Keyword Badge] │
│ 5 [H2 Heading] │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ Featured Image REUSED (no caption) │ │
│ └───────────────────────────────────────┘ │
│ │
│ [Content...] │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ SECTION 6 (reuse - no description) │
│ [Tag Badge] │
│ 6 [H2 Heading] │
│ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Content │ │ Square Image REUSED │ │
│ │ │ │ (no caption) │ │
│ └──────────────┘ └──────────────────────┘ │
│ [Widget Placeholder] │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ METADATA FOOTER (Editor+ only) │
│ ▸ View IGNY8 Metadata │
│ - Content ID: 123 │
│ - Task ID: 456 │
│ - Meta Title: ... │
│ - Meta Description: ... │
│ - Primary Keyword: ... │
│ - Secondary Keywords: [list] │
│ - Cluster ID: 789 │
│ - Sector: Industry Name │
│ - Source: AI Generated │
│ - Last Synced: Date/Time │
└─────────────────────────────────────────────┘
12. App ContentViewTemplate Updates
Changes to body section only (not header):
- Remove "Section Spotlight" label - Replace with keyword badge matching system
- Add Table of Contents below featured image (matching WordPress implementation)
- Match image layout rules from WordPress template:
- Section 1: Square right-aligned 50% (with description)
- Section 2: Landscape full width max 1024px (with description)
- Section 3: Square left-aligned 50% (with description)
- Section 4: Landscape full width max 1024px (with description)
- Sections 5+: Reuse images without descriptions
- Featured image max 1024px centered
- Widget placeholders below square images (empty for now)
- Table detection - full-width image before tables
Implementation Priority:
- Phase 1: Update image sizing (1024px max)
- Phase 2: Implement keyword badge matching
- Phase 3: Add table of contents component
- Phase 4: Add widget placeholder divs
Summary of Files to Update
| File | Changes | Priority |
|---|---|---|
igny8-content-template.css |
Container width breakpoints, image sizing classes, TOC styles, widget placeholder styles | 🔴 High |
igny8-header.php |
Remove emojis, add SVG icons, add Topic field, remove SEO/internal metadata | 🔴 High |
igny8-metadata.php |
Add role check (current_user_can('edit_posts')), include all moved metadata fields |
🔴 High |
igny8-content-sections.php |
Keyword badge matching logic, smart image distribution (Section 1-4 pattern), widget placeholders | 🔴 High |
igny8-featured-image.php |
Max 1024px, landscape priority | 🟡 Medium |
includes/template-functions.php |
Add helper functions: igny8_get_section_badges(), igny8_section_has_table(), igny8_show_image_description(), igny8_generate_table_of_contents() |
🔴 High |
ContentViewTemplate.tsx |
Match section labels, image layouts, add TOC component, widget placeholders | 🟡 Medium |
New File: parts/igny8-table-of-contents.php |
Table of contents component | 🟡 Medium |
New File: admin/settings-page.php |
Widget settings, TOC settings (future) | 🟢 Low |
Configuration Settings (Future Implementation)
// Plugin settings structure
$igny8_plugin_settings = [
'table_of_contents' => [
'enabled' => true,
'show_numbers' => true,
'collapsible' => false,
'sticky' => false,
'min_headings' => 3,
'position' => 'after_featured_image', // or 'before_content', 'floating'
],
'widgets' => [
'enabled' => false,
'sections' => [
'section_1' => [
'position' => 'right',
'widget_type' => 'none', // 'related_posts', 'custom_html', 'ad_unit', etc.
'content' => '',
],
'section_3' => [
'position' => 'left',
'widget_type' => 'none',
'content' => '',
],
],
],
'images' => [
'featured_max_width' => 1024,
'landscape_max_width' => 1024,
'square_width_percentage' => 50,
'show_descriptions_sections' => 4, // Show descriptions in first N sections
],
'badges' => [
'show_section_badges' => true,
'max_badges_per_section' => 2,
'badge_sources' => ['primary_keyword', 'tags', 'categories', 'secondary_keywords'], // Priority order
],
];
Implementation Phases
Phase 1: Core Template Updates (Week 1)
- ✅ Update CSS container widths and image sizing
- ✅ Replace emojis with SVG icons in header
- ✅ Add Topic field to header
- ✅ Move metadata to bottom with role check
- ✅ Implement keyword badge matching logic
Phase 2: Advanced Features (Week 2)
- ✅ Table of contents component
- ✅ Table detection and image positioning
- ✅ Image reuse logic (sections 5+)
Phase 3: App Sync (Week 3)
- ✅ Update ContentViewTemplate.tsx to match WordPress
- ✅ Add TOC component to React app
- ✅ Sync image layouts and sizing
Phase 4: Settings & Configuration (Week 4)
- ⏳ Plugin settings page
- ⏳ TOC configuration options
- ⏳ Widget management interface
- ⏳ Badge display preferences
Last Updated: January 10, 2026
Document Version: 2.0
Status: Design Complete - Ready for Implementation