The framework
behind the work

There’s a reason every project starts from the same foundation. Let’s take a look at what’s under the hood.


Architecture overview

Modular by design.

The platform is built on a parent-child theme model. The parent theme provides reusable infrastructure — base classes, support managers, contracts, and shared features. The child theme extends it with site-specific functionality. Every new project starts with the same battle-tested foundation.

At the core is the Provider pattern — self-contained domains that own their PHP classes, assets, blocks, configuration, and tests. Similar to Laravel service providers, each provider is scoped to a feature and composes its own managers for assets, blocks, features, ACF fields, and REST endpoints. No deep inheritance chains, no tangled dependencies.


Key points

  • Parent-child theme model — Infrastructure in the parent, specifics in the child. Clean separation that scales across projects.
  • Provider pattern — Each provider is a self-contained domain owning its PHP, SCSS, JS, blocks, config, and tests.
  • Composition over inheritance — Providers compose managers as internal collaborators rather than inheriting from a monolithic base.
  • Autowiring-first DI — PHP-DI resolves dependencies automatically. Explicit definitions only where autowiring can’t.

Tech stack

Modern tooling. No legacy baggage.

Every dependency is chosen deliberately — nothing is included because “that’s what WordPress projects use.” The stack prioritizes type safety, fast builds, and developer confidence.

ToolPurpose
PHP 8.4+Strict types throughout — every file declares strict_types=1
Timber 2.x / TwigClean templating with separation of logic and presentation
PHP-DI 7.0Autowiring-first dependency injection — no service locators
esbuild + SassFast, parallel asset compilation with watch mode
WordPress Block EditorCustom dynamic blocks with React editor components and Twig server-side rendering
Advanced Custom Fields ProStructured content with JSON-synced field groups
DDEVContainerized local development with database snapshots

Feature system

Toggle-able by default. Additive when it matters.

Functionality is organized into two categories — Features and Hooks — each with distinct inheritance rules that prevent accidental breakage across projects.


Features (toggle-able)

Capabilities that child themes can opt in or out of with a single line. Disable comments, restrict block types, enable SVG uploads, activate scroll animations — all controlled declaratively. The DI container resolves each feature and autowires its dependencies automatically.

DisableComments::class,          // enabled
DisablePosts::class => false,    // opted out in child
ScrollReveal::class,             // opted in from parent

Hooks (always-active)

Structural behavior that should never be accidentally disabled — icon enhancements, block style variants, social icon configurations. Inheritance is purely additive: parent and child hooks merge automatically with no opt-out syntax.


Runtime validation

Place a Feature in the hooks array or a Hook in the features array, and the system warns you at runtime and skips the class. Wrong marker interfaces are caught immediately — not silently ignored.


Block architecture

Editor to frontend. One block, full stack.

Custom blocks are built as WordPress dynamic blocks with the full editor-to-frontend pipeline: React/JSX components for the block editor, PHP + Twig for server-side rendering on the frontend, and scoped SCSS for styling in both contexts.


How a block is built

Each block lives inside its provider’s blocks/ directory with a predictable file structure:

  • block.json — Standard WordPress block metadata. Declares the block name, category, supports, and rendering entry point.
  • editor/ — React/JSX components for the block editor. WordPress package imports (@wordpress/blocks, @wordpress/element) map to globals at build time — no bundling of WordPress internals.
  • render.php — Server-side entry point. Receives block attributes, builds a Timber context, and renders a Twig template.
  • style.scss — Block styles applied on both the frontend and within the editor
  • {name}.twig — The Twig template consumed by the renderer.
  • view.js — Optional frontend interactivity (vanilla JS, no framework).

ACF block support

Some blocks use Advanced Custom Fields for structured content entry — giving editors a familiar field-based interface while the developer retains full control over rendering.


Build system

One script. Zero duplication.

A single canonical build script lives in the parent theme. It auto-discovers every provider’s assets and blocks, compiles them in parallel, and outputs production-ready CSS and JavaScript. The child theme invokes the same script — no copies, no drift.


How it works

  • The script uses process.cwd() as the theme root, so it works for any theme that calls it.
  • Sass compiles provider and block SCSS with configurable load paths — child themes can resolve parent SCSS partials (like breakpoints) without duplication.
  • esbuild bundles JavaScript with ES2020 targeting, minification in production, and sourcemaps in development.
  • WordPress editor packages are externalized — block scripts reference wp.blocks, wp.element, etc. as globals instead of shipping them in the bundle.

Watch mode

Incremental recompilation during development. The script watches provider assets/ and blocks/ directories and rebuilds only what changed.


Output mapping

SourceOutput
Provider SCSS (assets/scss/index.scss)dist/css/{slug}.css
Provider JS (assets/js/*.js)dist/js/{slug}/*.js
Block editor JS (blocks/{name}/editor/index.js)dist/js/{name}.js
Block frontend style (blocks/{name}/style.scss)dist/css/{name}.css
Block editor style (blocks/{name}/editor/editor.scss)dist/css/{name}-editor.css

Testing

Three layers. Full stack. Every commit.

Most WordPress projects ship with zero automated tests. This platform has 430+ PHP tests, JavaScript unit tests with DOM assertions, and end-to-end browser automation with automated accessibility audits.


PHP — unit & integration

PHPUnit 9 + WorDBless

WorDBless loads WordPress without a database — tests execute in milliseconds, not seconds. Two suites cover different concerns: Unit tests are fully isolated with no WordPress side effects, while Integration tests exercise hooks, filters, and the full registration lifecycle.

Test directories mirror source structure exactly. A class at src/Providers/Support/Asset/AssetManager.php has tests at tests/Unit/Providers/Support/Asset/AssetManagerTest.php. The HasContainer trait builds a real DI container with autowiring for realistic test scenarios — no mocking the container itself.


Javascript — unit & integration

Vitest + Testing Library

Frontend JavaScript is tested in a jsdom environment using Vitest and Testing Library. Tests verify real DOM behavior: ARIA attribute toggling, keyboard navigation, focus management, animation class application, and data sorting.

The parent theme provides a shared base config and test setup file — including custom mocks for browser APIs that jsdom doesn’t implement (IntersectionObserver with a trigger() helper, window.matchMedia). The child theme inherits this infrastructure and adds its own test files mirroring the provider directory structure.

An example of whats covered:

  • Dropdown component — ARIA toggles, keyboard navigation, focus wrapping, outside-click dismissal
  • Header — light/dark mode toggle, localStorage persistence, overlay focus trapping
  • Shutter cards block — card activation, keyboard interaction, animation class cycling
  • Projects block — sort by title/date, ascending/descending direction

End-to-end — browser automation & accessibility

Playwright + axe-core

Playwright tests run against the live local development site in Chromium — real browser, real rendered HTML, real interactions.

  • Smoke tests verify every page loads with expected structure and zero console errors.
  • Interaction tests cover dark/light mode persistence across page reloads, mobile hamburger menu behavior, overlay focus trapping, and keyboard escape handling at a 375px mobile viewport.
  • Accessibility audits run automated WCAG 2.1 AA checks on every page using axe-core. Any violation fails the build — accessibility isn’t aspirational, it’s enforced.

HTML reports are generated for debugging failures, with screenshots captured automatically when a test fails.


Design System

Tokens, not magic numbers.

The visual layer is built on CSS custom properties and Sass utilities — a small, deliberate system that adapts across light mode, dark mode, and high-contrast environments without duplicating styles.


Key points

  • CSS custom properties as the shared interface — color tokens, spacing scale, and typography variables defined once and consumed everywhere.
  • Light/dark mode via custom property swaps — one set of component styles, two visual modes.
  • Responsive breakpoints at 576px, 768px, 992px, and 1440px with Sass mixins (media(), media-down(), container() for container queries).
  • Block style variants — Editorial choices registered as WordPress block styles (Muted paragraphs, Animated covers, styled containers) that editors select from the block toolbar.

Accessibility

Built in. Not bolted on.

Accessibility is a structural concern, not a cleanup pass before launch. It’s enforced at every layer — Sass utilities, template markup, JavaScript behavior, and automated testing.


Key points

  • WCAG utility mixins — visually-hidden, forced-colors, high-contrast, reduced-motion — available to every SCSS file in the system.
  • Reduced motion support — Scroll reveal animations respect prefers-reduced-motion. Users who opt out of motion see no animation, not broken animation.
  • Windows High Contrast — Forced-colors media query support ensures interfaces remain usable in high-contrast environments.
  • Semantic markup — Twig templates use proper heading hierarchy, landmark elements (main, nav, article), and ARIA attributes where semantics alone aren’t sufficient.
  • Icon accessibility — SVG icons are injected with aria-hidden=”true” to prevent redundant screen reader announcements.
  • Automated audits — Playwright + axe-core runs WCAG 2.1 AA checks against every page as part of the E2E test suite. Violations fail the build.

Security

Hardened defaults. Not optional add-ons.

Security decisions are made once in the parent theme and inherited by every project. They’re not configuration options — they’re the baseline.


Key points

  • SVG sanitization — Every SVG upload is run through enshrined/svg-sanitize, stripping scripts, event handlers, and XSS vectors before the file reaches the media library. Admin-only upload restriction adds an additional gate.
  • Block allowlist — Only approved blocks are available in the editor. No embed blocks (XSS surface), no raw HTML block. The allowlist is managed as a Feature, so child themes can adjust it if needed.
  • Comments disabled — The entire comment subsystem is removed: admin UI, menu items, frontend output, and the edit-comments redirect. Not hidden — removed.

Developer Experience

Convention-driven. Minimal ceremony.

Adding functionality to the platform follows predictable patterns. There’s no setup wizard, no boilerplate generator — just conventions that the build system and container understand automatically.


Key points

  • Makefile commandsmake start, make build, make test, make watch, make clean. One command for every workflow, from local environment setup to full test runs.
  • PSR-4 autoloading — Namespace matches directory path exactly. Add a PHP class, run composer dump-autoload, and it’s available everywhere.
  • Auto-discovered assets — Create a provider directory with an assets/scss/index.scss file, and the build system finds it on the next compile. No manifest to update, no config to edit.
  • Auto-discovered blocks — Add a blocks/{name}/ directory with a block.json, and the BlockManager registers it. The build script compiles its styles and editor scripts automatically.
  • Feature in one line — Create a class implementing Feature, add it to the provider’s $features array, and the container autowires its dependencies and registers it.
  • Watch mode — Real-time SCSS and JS recompilation during development. Change a file, see the result.