The framework
behind the work
There’s a reason every project starts from the same foundation.
Architecture overview
Modular by design.
The platform is built on three layers, each distributed as a Composer package through a private Satis registry. At the foundation is Mythus, a theme-agnostic mu-plugin framework that owns the provider pattern, dependency injection container (PHP-DI), contracts system, and all support managers. Above that sits IX, a parent theme that bridges Mythus to Timber and Twig for templating. The child theme extends IX with site-specific functionality. Every new project starts from the same foundation — and improvements flow back upstream through the registry.
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
- Three-layer architecture — Mythus (framework), IX (Timber/Twig bridge), child theme (site-specific). Each layer is a standalone Composer package distributed via a private Satis registry.
- 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
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.
| Tool | Purpose |
|---|---|
| PHP 8.4+ | Strict types throughout — every file declares strict_types=1 |
| Timber 2.x / Twig | Clean templating with separation of logic and presentation |
| PHP-DI 7.0 | Autowiring-first dependency injection — no service locators |
| esbuild + Sass | Fast, parallel asset compilation with watch mode |
| WordPress Block Editor | Custom dynamic blocks with React editor components and Twig server-side rendering |
| Advanced Custom Fields Pro | Structured content with JSON-synced field groups |
| DDEV | Containerized local development with database snapshots |
Feature system
Features you can turn off and hooks you can’t accidentally break
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
Each block owns its full pipeline from editor to frontend
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 build script shared across every theme
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
| Source | Output |
|---|---|
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
430+ automated tests across three layers of the stack
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
A design system built on CSS custom properties and Sass utilities
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.
Operations
One command for everything.
The entire platform — local environment, builds, tests, deployments, database syncing, pattern exports — is operated through a single Makefile. No runbooks, no tribal knowledge. Every workflow is a make command.
The Make system
The Makefile is the single interface to the platform. It abstracts DDEV, Composer, npm, WP-CLI, SSH, and rsync behind short, memorable commands — so the developer never needs to remember which tool does what or which flags to pass.
| Command | What it does |
|---|---|
make start | Start DDEV, restore latest DB snapshot, install deps, build assets |
make stop | Snapshot database, prune old snapshots, stop DDEV |
make build | Compile both themes’ assets (parent first, then child) |
make test | Run PHP + JS test suites across both themes |
make watch | Incremental recompilation during development |
make push-production | Export local DB + uploads and sync to production |
make pull-production | Pull production DB + uploads to local DDEV |
make pull-patterns | Export CMS block patterns from production to PHP files |
Staging equivalents exist for every push/pull command. The system handles URL replacement, cache flushing, file ownership, and cleanup automatically — nothing is left to manual steps.
Git push deployments
Deployments are a git push. A bare repository on the server receives pushes and a post-receive hook handles the rest — checking out the code, restoring environment config, installing production dependencies, building assets, and fixing file ownership.
Branch routing determines the target environment:
- main → production
- develop → staging
No CI/CD service in the middle, no container orchestration, no deploy queue. Push to the branch, the server builds and serves it. The entire deployment pipeline is a shell script — auditable, debuggable, and fast.
Environment sync
Database and uploads move bidirectionally between local, staging, and production with a single command. Each sync exports the database, transfers it over SSH, imports it on the target, runs search-replace to swap URLs, flushes caches and rewrites, and rsyncs the uploads directory. Cleanup happens automatically — no temp files left behind on either end.
Developer experience
Adding functionality follows predictable patterns with no boilerplate
There’s no setup wizard, no boilerplate generator — just conventions that the build system and container understand automatically.
Key points
- 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.