One of the largest obstacles to building a reusable Presentation Engine was the diversity of the content itself.
Every content type stored different information.
- Books contained authors and cover images.
- Songs contained artists and album artwork.
- Games contained developers and box art.
- Artists used portrait images.
- Quotes often contained no images at all.
- Elements, Concepts, and Fragments each exposed different metadata.
If presentation templates had to understand every one of these structures, the architecture would quickly become unmaintainable.
The solution was to introduce a normalized presentation object known simply as a Card.
The Problem
Originally, templates reached directly into WordPress and Advanced Custom Fields to retrieve whatever information they required.
Book Template
↓
get_field('cover_image')
↓
get_field('author')
↓
the_title()
↓
the_permalink()
The song template looked different.
The artist template looked different.
Every content type duplicated similar logic in slightly different ways.
Changing one field often meant updating multiple templates.
The Architectural Shift
Instead of allowing presentation templates to understand every content type, each post is converted into a standardized card before presentation begins.
Book
↓
Book Card
Song
↓
Song Card
Artist
↓
Artist Card
Game
↓
Game Card
Although the source data differs, every card shares the same public interface.
The Card Builder
Every content type owns a dedicated builder responsible for translating its internal data into the normalized structure.
inc/
└── presentation/
└── cards/
├── artist.php
├── book.php
├── concept.php
├── element.php
├── excerpt.php
├── fragment.php
├── game.php
├── image.php
├── lyric.php
├── movie.php
├── profile.php
├── quote.php
├── show.php
└── song.php
Each file understands only one content type.
The Public Contract
Although builders may retrieve data differently, they all return the same basic structure.
[
'title' => ...,
'url' => ...,
'image' => ...,
'excerpt' => ...,
'meta' => ...,
'icon' => ...,
'type' => ...
]
Templates never need to know how those values were produced.
The Translation Layer
The card builder acts as a translation layer between WordPress and the Presentation Engine.
WordPress
↓
Custom Fields
↓
Relationships
↓
Taxonomies
↓
Card Builder
↓
Normalized Card
Everything above the card builder speaks WordPress.
Everything below it speaks cards.
The Role of kp_build_card()
Rather than allowing callers to instantiate builders directly, a single dispatcher selects the appropriate implementation.
kp_build_card(
$type,
$post_id,
$map
);
Internally, this resolves the appropriate builder for the requested content type.
song
↓
cards/song.php
artist
↓
cards/artist.php
game
↓
cards/game.php
This keeps the collector completely independent of individual content types.
Why Dedicated Builders?
An obvious alternative would have been a single massive function containing dozens of conditional statements.
if ($type == 'book') { ... }
if ($type == 'song') { ... }
if ($type == 'artist') { ... }
While functional, this approach becomes increasingly difficult to maintain.
Dedicated builders provide much stronger separation of concerns.
- Each file owns one content type.
- Changes remain localized.
- Developers can work independently.
- Testing becomes simpler.
- Adding new content types requires minimal modification.
Supporting Many Content Types
Once the card architecture existed, adding new content types became a predictable process.
Create CPT
↓
Create Card Builder
↓
Register Metadata
↓
Presentation Engine Works
Most of the rendering pipeline requires no additional changes.
Migration Benefits
The normalized card system became especially valuable during the bilingual template migration.
Legacy WP_Query objects could be converted into cards before presentation, allowing old and new systems to share the same rendering path.
WP_Query
↓
Posts
↓
kp_build_card()
↓
Cards
↓
Presentation
Without the normalized card system, the bilingual migration strategy would have required duplicate rendering logic.
Reducing Template Complexity
A presentation template no longer needs to understand WordPress internals.
Instead of retrieving fields directly:
get_field(...)
get_permalink(...)
get_the_title(...)
get_post_thumbnail(...)
Templates simply consume the normalized object.
$item['title']
$item['url']
$item['image']
$item['excerpt']
The template becomes significantly smaller and easier to understand.
The Card as an API
One useful way to think about the card is as a public API.
The internal implementation of a content type may evolve over time, but as long as the card contract remains stable, the rest of the presentation system remains unaffected.
This isolates change and protects the Presentation Engine from implementation details.
Future Possibilities
Because cards are independent of WordPress templates, they could eventually support:
- REST API responses.
- Static site generation.
- Mobile applications.
- Knowledge graph visualizations.
- Alternative front-end frameworks.
Only the card builders would need to understand the underlying content model.
Key Design Principle
Normalize content once. Render it everywhere.
The normalized card system forms the bridge between the WordPress data model and the Presentation Engine. By translating every content type into a shared presentation contract, the architecture eliminates template-specific knowledge, simplifies rendering, and creates a stable interface that future systems can depend upon regardless of how individual content types evolve.