A professional WordPress site can look simple, but it often contains several kinds of responsibility: presentation, structured projects, contact workflows, APIs, privacy, analytics and operational tooling. I split this implementation into a custom theme and a companion plugin so those responsibilities can be inspected separately.
The decision in one sentence
The theme owns how the site looks; the plugin owns data and behavior that should survive a visual redesign.
What belongs to the theme
The theme controls templates, visual hierarchy, navigation, responsive behavior, typography and progressive interactions. If I replace the theme, the site should look different, but its projects, case studies and stored leads should not disappear.
- Homepage and archive templates
- Article reading layout and table of contents
- Responsive navigation and footer
- Design tokens, cards and component styling
- Small enhancements such as reading progress and back to top
What belongs to the companion plugin
The plugin registers content models and taxonomies, defines metadata, exposes REST routes, renders a dynamic Gutenberg block, stores contact leads, integrates privacy tools and provides operational commands.
This separation lets a maintainer deactivate the theme conceptually and ask: which capabilities still make sense? Projects and their metadata do. A hero layout does not.
Why not put everything in the theme?
Putting CPTs and business behavior in a theme is fast at the beginning. The coupling becomes visible when the theme changes or the same data must be consumed by an API, block or command-line task. A design change should not require a content migration.
Why not build one large plugin for everything?
The opposite extreme also creates friction. A plugin that prints every visual section can make the theme a thin shell and limit normal WordPress template behavior. Presentation changes then become plugin releases even though the business behavior is unchanged.
The cost of two packages
There are real trade-offs:
- Installation and version compatibility must be documented.
- The theme needs standalone-safe fallbacks.
- The plugin needs output that is not tightly coupled to one theme.
- Integration QA covers more combinations.
For this site, that cost is worthwhile because the content, API and operational workflows are expected to survive future redesigns. For a simple brochure site, a smaller solution might be more appropriate.
How the packages communicate
The theme reads settings through a small wrapper and renders plugin shortcodes only when they exist. The plugin does not assume a theme template path. Shared class names are prefixed, and the public API is intentionally narrow.
What I would change at larger scale
For a commercial product I would add a formal compatibility matrix, semantic release automation, migration tests, more complete integration tests and possibly shared Composer packages for contracts. The current project keeps those ideas visible without pretending to be a multi-team platform.
The decision rule I use
Two packages are not automatically better. The useful question is whether data and behavior should survive a theme change. Architecture is a decision process based on ownership, coupling, portability and operational cost, not a folder diagram.
Related implementation
See how the ideas map to working code.
The public repository connects the article’s architecture, security, REST, blocks, CLI, privacy, testing and CI concepts to working code.