Skip to content
⚓ A parent theme framework for Timber · PHP 8.3+ · WP 6.4+ · Timber 2.0

WordPress theme development,
ship-shape & Bristol fashion

The modern WordPress parent theme framework for Timber & Twig — MVC-inspired controllers, Laravel-style convention-over-configuration, and expressive object-oriented PHP on a Composer + PSR-4 foundation. Cleaner child themes, less boilerplate, faster to ship.

Read the docs →

How PressGang compares to classic WordPress and Timber

Classic theme
<?php // archive-event.php
get_header(); ?>

<?php if ( have_posts() ) : ?>
  <?php while ( have_posts() ) :
    the_post(); ?>
    <h2><a href="<?php the_permalink(); ?>">
      <?php the_title(); ?></a></h2>
    <?php the_excerpt(); ?>
  <?php endwhile; ?>
<?php endif;

get_footer();
// functions.php
add_action('init', function () {
  register_post_type('event', [
    'labels'      => [ 'name' => 'Events' ],
    'supports'    => ['title', 'editor'],
    'has_archive' => true,
  ]);
});
Logic, markup and the loop tangled in one file — repeated for every template — plus hook-based registration piling up in functions.php.
Timber context 🌲
<?php // archive-event.php
$context = Timber::context();

$context['events'] = Timber::get_posts([
  'post_type'      => 'event',
  'posts_per_page' => 12,
]);

Timber::render(
  'archive-event.twig',
  $context
);
{# archive-event.twig #}
{% for event in events %}
  <h2><a href="{{ event.link }}">
    {{ event.title }}</a></h2>
  {{ event.preview }}
{% endfor %}
A leap forward in separating view concerns — but every template still hand-rolls its own context and render call, post types still register through functions.php hooks, and context management gets untidy as themes grow.
With PressGang ⚓
// EventsController.php
// routed by convention — no stub file
class EventsController
  extends AbstractController {

  protected array $context_getters
    = ['events'];

  protected function get_events() {
    return Timber::get_posts([
      'post_type' => 'event',
    ]);
  }
}
// config/custom-post-types.php
'event' => [
  'supports'    => ['title', 'editor'],
  'has_archive' => true,
],
Same Twig template — PressGang adds controller routing, context binding, and config-driven registration, all on Composer autoloading and class inheritance for modern, modular development. No render calls, no stub files.

A framework, not a fight

Some frameworks rebuild WordPress in another framework's image — rerouted requests, reshuffled directories, magic everywhere. PressGang doesn't circumvent WordPress; it modernises it, with Timber at the core. Standard install layout, standard template hierarchy, and the classic WordPress way always one step away if you want it. If you know WordPress, you already know your way around the deck.

shipped with the parent theme
PageControllerpage.twig
PostControllersingle.twig
PostsControllerarchive.twig
SearchControllersearch.twig
NotFoundController404.twig

// extend any of them in your child theme
class PageController extends BasePageController {
  // override only what you need
}
00 · PARENT THEME

A parent theme framework

Your child theme requires PressGang via Composer and inherits a working foundation out of the box — controllers for the whole WordPress template hierarchy (pages, posts, archives, search, 404, even WooCommerce), ready to render before you write a line of code.

Everything is extensible: subclass any controller to override just what you need, and child theme config/ files merge over the parent's defaults.

Getting started docs →
01 · CONTROLLERS

Controllers as view models

Each controller gathers data via Timber in get_context(), builds a context, and selects a Twig template — a clear separation of concerns, side-effect free. Optionally, declare the template contract in $context_getters and each key wires to its getter automatically — even less boilerplate.

Controller docs →
src/Controllers/EventsController.php
class EventsController extends AbstractController {

  protected array $context_getters = ['events'];

  protected function get_events(): iterable {
    return Quartermaster::posts('event')
      ->whereMetaDate('start', '>=')
      ->orderByMeta('start', 'ASC')
      ->get();
  }
}
config/custom-post-types.php
// Declared, not hooked. No functions.php spaghetti.
return [
  'event' => [
    'labels'      => [ 'name' => 'Events' ],
    'supports'    => ['title', 'editor', 'thumbnail'],
    'has_archive' => true,
    'rewrite'     => [ 'slug' => 'events' ],
  ],
];
02 · CONFIG

Config-driven bootstrapping

Post types, taxonomies, menus, blocks, scripts, styles — declared in config/ arrays, registered by convention. Inspired by Laravel: no queries, no side effects, just declarative registration you can diff and review.

Config docs →
03 · ROUTING

Template routing — no stub files

Opt in, and requests route straight from WordPress's template hierarchy to your controllers by naming convention. Most themes need no template PHP files at all — and explicit stubs always win, so you can adopt it one deleted file at a time.

Template routing docs →
resolved by convention
front-pageFrontPageController
archive-eventEventsController
single-eventEventController
taxonomy-event-typeEventTypeController
searchSearchController

// {candidate}.twig renders when present
config/snippets.php
// One class per concern — on or off in one line
return [
  'PressGang\Snippets\DisableEmojis'   => [],
  'PressGang\Snippets\ImageSizes'      => [
    'hero' => ['width' => 1920, 'crop' => true],
  ],
  'MyAgency\Snippets\Analytics'        => [
    'measurement_id' => 'G-XXXXXXX',
  ],
];
04 · SNIPPETS

Snippets — no more functions.php junk drawer

Each concern is its own class implementing the same clean, minimal interface, enabled or disabled with a single config line. Developers and agencies can build their own modular, reusable snippets and install them via Composer across every project — instead of relying on theme bloat or yet another plugin.

pressgang-snippets repo →·Snippet docs →

Why crews choose PressGang

Modern PHP for WordPress — without abandoning WordPress. Compare it to any WordPress starter theme, Timber boilerplate, or full-stack restructure.

🌲 Timber 2.0 native
Built on Timber and Twig from the keel up — not bolted on. Templates stay presentation-only.
🏗️ Standard WP structure
A parent theme, not a stack overhaul. Works with any host, any deploy pipeline, any plugin.
📦 Composer & PSR-4
Dependency management and autoloading the way the rest of the PHP world does it.
🧭 Convention over configuration
Laravel-inspired conventions bootstrap the repetitive parts. Escape hatches everywhere.
🧪 Tested without WordPress
PHPUnit + BrainMonkey unit tests run with no WordPress install required.
🔓 No lock-in
Plain WordPress underneath. Drop down to raw WP APIs whenever you like — nothing gets smuggled in.
STANDALONE PACKAGE · QUARTERMASTER

A fluent query builder for WordPress args

Quartermaster is a separate, standalone package — a fluent, args-first builder for WP_Query and WP_Term_Query. Build complex query arrays in readable, composable steps, then inspect the exact output with toArgs() and explain().

100% WordPress-native under the hood: plain WP args out — no ORM, no hidden query engine, no lock-in. Use it with PressGang or in any WordPress project.

Explore Quartermaster → composer require pressgang-wp/quartermaster
$events = Quartermaster::posts('event')
  ->paged(12)
  ->whereMetaDate('start', '>=')
  ->orderByMeta('start', 'ASC')
  ->wpQuery();

// Same plain WP_Query args — 5 readable steps
AI-READY · BOSUN

Your AI crew, already briefed

PressGang's best features leave barely a trace in a child theme's code — great for developers who know the ship, invisible to AI agents who don't. Bosun composes CLAUDE.md + AGENTS.md from what each theme actually has installed and enabled — so every agent that comes aboard knows the ropes.

No more hand-rolled query arrays and stub files from an unbriefed agent — working code that misses everything that makes the framework worth sailing.

Meet Bosun →
terminal
$ wp package install https://github.com/pressgang-wp/pressgang-bosun.git
$ wp bosun install

 Inventory: pressgang 2.1 · quartermaster 1.3
 Template routing detected — guidance aboard
 Composed CLAUDE.md + AGENTS.md
 Skills installed → .claude/skills/

All hands briefed.

The fleet

The PressGang ecosystem — Quartermaster is fully standalone and works in any WordPress project; Capstan and Bosun enhance the PressGang developer experience.

⚓ Quartermaster
Fluent, args-first builder for WP_Query & WP_Term_Query. Readable, composable queries — plain WP args out. No ORM, no lock-in.
composer require pressgang-wp/quartermaster
Explore Quartermaster
🛞 Capstan
WP-CLI scaffolding for PressGang — WordPress core, parent theme, child theme in one command. Dry-run by default: preview every plan before it writes a byte.
wp capstan new
Explore Capstan
🧭 Bosun
Composes AI agent guidelines and skills for PressGang themes — generated from what each theme actually has installed and enabled.
wp bosun install
Explore Bosun

Weigh anchor

Scaffold a full project with Capstan, or start from the pressgang-child starter theme.

$ composer global require pressgang-wp/capstan
$ wp capstan new