Skip to main content

Engineering Journal

The Page + CPT Hybrid: How We Rebuilt KODENZO’s Case Study System

Why /case-studies/ is a real WordPress Page and every individual study is a separate Custom Post Type — and the three real rendering bugs that decision surfaced once it shipped.

When we rebuilt the Case Studies system, the first real decision wasn’t visual — it was routing. /case-studies/ already existed as a real, published WordPress Page before this build started. That mattered more than it sounds.

The collision problem

WordPress resolves one template per URL. The obvious approach — a Custom Post Type with its own archive at /case-studies/ — would have collided with the Page that already owned that exact URL. Two things can’t answer the same request.

The fix was to register the kodenzo_case_study CPT with has_archive set to false, and give it a rewrite slug of case-studies for single posts only. That produces URLs like /case-studies/woocommerce-stabilization/ without ever registering anything at the bare /case-studies/ path. The Page keeps that URL. The CPT only ever claims the paths nested under it. It’s the same relationship an ordinary “Blog” Page has with individual post permalinks nested underneath it — WordPress supports this natively, but only if you register the CPT correctly the first time.

The practical rule this taught us: before building any new subpage, check two things, not one — whether the URL is already a CPT archive, and whether it’s already a real WordPress Page. Get either wrong and the page silently won’t render, with no error to point at.

One rendering function, two templates

The hub page (page-case-studies.php) and the individual study template (single-kodenzo_case_study.php) both needed to render the same diagram and mockup graphics. WordPress only loads one template file per request, so a function defined inside the single-study template was never reachable from the hub — even though both files are part of the same theme.

The fix was moving render_case_diagram() and render_case_mockup() out of the template entirely and into Kodenzo_Case_Studies, a class already loaded on every request through functions.php. Once the rendering logic lived on the class instead of inside one template, both pages could call it identically. This is a small structural lesson, but it’s the one that scales: anything two templates need has to live somewhere both templates can reach — never inside either one.

Three bugs, one shared fix pattern

After the hub and single-study templates shipped, a live review surfaced what looked like three unrelated problems. They weren’t.

The hero diagram was squeezed into a small sidebar box. The site’s .kodenzo-hero component is a flex row, built everywhere else on the site to hold exactly one child. The case-study hero had two top-level children — the text content and a separate wrapper around the diagram — so the browser laid them out side by side instead of stacked. The fix was structural, not visual: nest the diagram inside the same content wrapper as the text, restoring the single-child assumption every other hero on the site already relies on.

The Architecture section’s text was silently missing for five of eight case studies. The section was gated entirely on the presence of a diagram object, but only three of the eight studies had one — all eight had real architecture prose. Decoupling the two conditions (render on diagram or text, and only draw the diagram graphic when it exists) fixed all five at once, from a single template change.

Two case studies had an empty box where their “What We Built” visual should be. Their implementation_visual field was set to a value the rendering function’s switch statement didn’t recognize, so nothing matched and nothing rendered — no error, just silence. Fixing the two data values solved the immediate bug; adding a default case to the switch statement means the same class of typo can never silently disappear again.

The common thread across all three: none of them were caught by a syntax error or a broken page. They were caught by looking at the rendered result and comparing it against what should have been there. Code that runs without error and code that’s correct are not the same claim.

Keep Learning. Keep Building.

More engineering articles, guides and product updates live in the Knowledge Hub.