When designing the JekCMS theme system, we evaluated six architecture patterns used by WordPress, Ghost, Kirby, Statamic, October CMS, and Bolt. The layout we settled on — templates, partials, and a functions.php with hook registration — was chosen not for simplicity but for predictability.
WordPress themes bundle presentation and logic together in the same file, which makes them approachable for beginners but difficult to test and maintain at scale. Ghost's Handlebars templates enforce a strict data-presentation separation at the cost of expressiveness — there is no straightforward way to perform conditional logic in a Handlebars template without a helper. JekCMS chose a middle path: templates are PHP files, which allows full expressiveness, but business logic belongs in registered hook functions rather than in the template itself.
The functions.php Contract
The functions.php file in the theme root is loaded early in the request cycle, before any template rendering begins. It has access to the complete hook API: add_action, add_filter, remove_action, and remove_filter. These function signatures are intentionally identical to WordPress's hook system — a deliberate design decision to reduce the learning curve for the large number of developers who already know WordPress.
Partials With Scoped Data
Partials live in templates/partials/ and are loaded with the get_partial('name') helper. Unlike WordPress's get_template_part(), JekCMS's implementation accepts a second argument for passing data directly into the partial scope without requiring global variables. This makes partials easier to test in isolation.
Arrays Over Objects
The query layer exposes a set of functions — get_posts(), get_post(), get_categories() — that return plain PHP arrays rather than objects. Plain arrays are easier to serialize, pass between functions, and cache. The trade-off is that array access syntax ($post['title']) is slightly more verbose than property access ($post->title), but the predictability benefit justifies this.