Theme Paradigms: Classic, Universal & Block-Based

WP Rig supports three theme-development paradigms: classic (traditional PHP templates), universal (a hybrid theme with theme.json and block support), and block-based (Full Site Editing). The active paradigm is a single config value — theme.themeType — and every feature gate in the framework follows from it automatically. This document explains what each paradigm includes, how resolution works under the hood, and how to switch paradigms.

The three paradigms

Classic (Standard WP Rig)

The classic paradigm produces a traditional WordPress theme: PHP templates under the theme root (header.php, front-page.php, sidebar.php), a Customizer-first settings story, and no Full Site Editing surface. WP Rig enforces a true-classic guarantee: when you choose classic during rig-init, the framework strips any stale themeType override from config.local.json, moves leftover FSE artifacts (templates/, parts/) into a timestamped .rig-backup/ folder, and verifies that WordPress core does not detect a block theme. theme.json is intentionally kept — it is hybrid-legitimate, does not trigger the Site Editor, and the design token system owns it.

Universal (Hybrid)

The universal paradigm is the middle ground: a classic-style PHP theme that also carries theme.json, block editor support, block patterns, and the block-based CSS baseline. Universal themes can use the Navigation block, block visibility, and pattern-driven content while keeping full control over templates in PHP. Most modern client themes live here.

Block-based (Full Site Editing)

The block-based paradigm is a Full Site Editing theme: block templates under templates/ and parts/, the Site Editor as the primary surface, and the full rig:bake round-trip for moving Site Editor work back into git-tracked theme files. Block-based features are also available in universal themes — the two share every block-era capability; block-based simply makes FSE the primary workflow.


Paradigm tags

Every WP Rig feature is tagged with a paradigm scope that maps to the theme types where it is enabled. The tag → theme-type matrix lives once in config/paradigms.json:

TagEnabled inExample feature
allclassic, universal, block-basedDesign tokens, icon API, asset pipeline
classicclassic, universalClassic menu system, classic widgets
universaluniversalUniversal-only overrides
block-baseduniversal, block-basedrig:bake, block patterns, _blocks-based.css

The governing rule: block-based features are never wired into the classic core. They activate only when theme.themeType is universal or block-based.


How the active paradigm resolves

The active theme type resolves through the shared configuration chain, in increasing precedence:

config/config.default.json   →   config/config.json   →   config/config.local.json

The setting lives under theme.themeType and accepts exactly three values: classic, universal, or block-based. Both the JS build and the PHP runtime read the same merged chain and fail fast on invalid values — there is no silent fallback.

// config/config.json — change the paradigm in one line
{
  "theme": {
    "themeType": "universal"
  }
}

Changing this value is the single source of truth: every gate in the build and runtime follows automatically. There is no per-feature switch to hunt down.


How gating works in code

The paradigm system has a JS half (build-time) and a PHP half (runtime):

  • JS: scripts/lib/paradigm.js exposes getActiveThemeType() and isFeatureEnabled(tag) — the build uses these to include or exclude assets (for example, _blocks-based.css never ships in a classic build).
  • PHP: inc/Paradigm.php exposes Paradigm::is_enabled(tag) — used at runtime for component and feature registration.
  • Components: a paradigm-gated component declares const PARADIGM = 'block-based' (or classic) and uses Paradigm_Component_Trait; Theme skips inactive components automatically during discovery.
// A component gated to block-era themes
class Component implements Component_Interface {
	use Paradigm_Component_Trait;

	const PARADIGM = 'block-based';

	public static function is_active(): bool {
		return self::paradigm_active();
	}
}

Switching paradigms

Set your paradigm interactively during theme initialization:

npm run rig-init

When you select classic, rig-init runs the true-classic teardown: it removes the theme.themeType and theme.enableBlocks keys from config.local.json (all other keys preserved) so your interactive choice is authoritative, and moves any leftover templates/ and parts/ folders to .rig-backup/<timestamp>/ — never deleted, always recoverable. To go the other way, edit theme.themeType in config/config.json and rebuild:

npm run build

The build regenerates paradigm-dependent artifacts (the token pipeline, theme.json binding, included CSS/JS) to match the new type.


Child themes and bundling

Child theme conversion is paradigm-aware: npm run setup-child derives its component keep-list from the active theme type — the classic core for classic themes, plus the Editor, Blocks, Block_Patterns, Block_Styles, and Icons components for universal and block-based themes. A bundled theme never leaks its local config overrides, and framework version metadata is stripped so the shipped child carries only its own version.