ThemesCore concepts

TAW Core concepts

Understand how TAW Core and TAW Theme fit together, from PHP blocks and MetaBlocks to metabox storage, assets, and other runtime systems.

Overview

TAW Core defines the runtime and APIs for PHP blocks, metaboxes, and assets; TAW Theme is the concrete theme that uses those APIs to build real layouts.

Think of TAW Core as the engine and TAW Theme as the car body and interior. You customize the theme, not the engine.

TAW Core vs TAW Theme

TAW Core exposes the base classes (BaseBlock, MetaBlock) and services (BlockRegistry, ViteLoader, metabox engine) that power the framework.

TAW Theme wires those pieces together with one call in functions.php, defines blocks under Blocks/, and ships the built assets. functions.php belongs to the framework and is replaced on sync:

// functions.php in your theme
require_once get_template_directory() . '/vendor/autoload.php';

TAW\Core\Theme\Theme::bootstrapFullSite(get_template_directory());

Your own setup goes in three files the theme owns, which bootstrapFullSite() loads when they exist: inc/options.php (options pages), inc/performance.php (returns the array passed to Theme::performance()) and inc/customizations.php (theme supports, menus, any other hooks).

When this code runs, TAW Core discovers your blocks, registers metaboxes and options pages, and configures the asset pipeline for the current environment.

Blocks directory and auto-discovery

TAW Theme keeps all block classes under the Blocks/ directory at the root of the theme.

Each block lives in its own folder, Blocks/<Name>/, with a class file named after the folder and an index.php view. A simple UI block looks like this:

// Blocks/Button/Button.php
namespace TAW\Blocks\Button;

use TAW\Core\Block\Block;

class Button extends Block
{
    protected string $id = 'button';

    // Props the view receives when the caller doesn't pass them.
    protected function defaults(): array
    {
        return ['text' => '', 'url' => '#', 'variant' => 'primary'];
    }
}

Blocks/Button/index.php renders the props as variables ($text, $url, $variant). Anywhere in a template or another block's view, render it with props:

<?php (new \TAW\Blocks\Button\Button())->render(['text' => 'Get started', 'url' => '/contact']); ?>

As long as you follow the Blocks/ convention and extend the right base class, TAW Core takes care of discovery and wiring.

UI Blocks vs MetaBlocks

TAW blocks fall into two broad categories:

When to use a UI Block

Use a UI Block when the block:

  • Does not own its data
  • Receives everything it needs via arguments or a view-model
  • Can be reused across many pages with different inputs

The Button class above is a UI Block: Block with defaults(), rendered with props.

When to use a MetaBlock

Choose a MetaBlock when the block should define and persist its own fields.

// Blocks/Hero/Hero.php
namespace TAW\Blocks\Hero;

use TAW\Core\Block\MetaBlock;
use TAW\Core\Metabox\Metabox;

class Hero extends MetaBlock
{
    protected string $id = 'hero';

    // The fields editors fill in, on every page.
    protected function registerMetaboxes(): void
    {
        new Metabox([
            'id'      => 'taw_hero',
            'title'   => __('Hero Section', 'taw-theme'),
            'screens' => ['page'],
            'fields'  => [
                ['id' => 'hero_heading', 'label' => __('Heading', 'taw-theme'), 'type' => 'text'],
                ['id' => 'hero_image',   'label' => __('Image', 'taw-theme'),   'type' => 'image'],
            ],
        ]);
    }

    // What Blocks/Hero/index.php receives as variables ($heading, $image_id).
    protected function getData(int|false $postId): array
    {
        return [
            'heading'  => $this->getMeta($postId, 'hero_heading'),
            'image_id' => $this->getMeta($postId, 'hero_image'),
        ];
    }
}

In a page template, queue the block before get_header() (so its CSS and JS load in the head), then render it where it belongs:

use TAW\Core\Block\BlockRegistry;

BlockRegistry::queue('hero');
get_header();
BlockRegistry::render('hero');

MetaBlocks integrate directly with the metabox engine so editors fill fields in the WordPress admin and see the block on the front end.

Metabox engine and options pages

The metabox engine in TAW Core handles field registration, storage, and retrieval for MetaBlocks and for global options pages.

A typical options page definition in your theme might look like this:

// somewhere in your theme bootstrap
use TAW\Core\Options\OptionsPage;

OptionsPage::make('Theme Settings', 'theme_settings')
    ->tab('Branding', function ($tab) {
        $tab->text('site_tagline');
        $tab->image('logo');
    })
    ->register();

Under the hood, TAW Core maps these fields to wp_options and gives you helpers to read them in templates.

$tagline = taw_option('site_tagline');

Use MetaBlocks for editor-managed content that belongs to a specific post, and options pages for configuration that should apply everywhere.

Asset pipeline: Vite, per-block assets, Tailwind v4

TAW Core ships with a Vite-based asset loader and Tailwind v4 integration. TAW Theme provides the actual entrypoints and block-level files.

A minimal Vite entry for your theme might look like this:

// resources/js/theme.js
import '../css/theme.css';
// import per-block scripts here

Tailwind v4 is wired through the Vite plugin:

// vite.config.js
import tailwindcss from '@tailwindcss/vite';

export default {
  plugins: [
    tailwindcss(),
    // other plugins
  ],
};

TAW Theme decides which entrypoints exist; TAW Core ensures those assets are enqueued for front-end requests.

Other runtime systems you may use

Beyond blocks, metaboxes, and assets, TAW Core also ships with additional runtime modules that you can opt into from your theme when you need them.

These modules stay out of the way until you use them. Reach for them when your theme needs SVG helpers, form handling, or mail delivery, not by default.

TAW Core includes SVG helpers under TAW\Helpers\Svg for safe SVG uploads and rendering, which you often use alongside performance helpers documented in the TAW Theme guide. Start with the TAW Theme page to see how these helpers fit into a concrete theme.

For interactive experiences, TAW Core provides a Forms runtime via TAW\Core\Form\Form plus a submissions handler that stores form submissions and can trigger webhooks. When you need user input stored and processed in a structured way, this forms layer gives you a consistent API inside your theme rather than ad-hoc handling.

For email, the Mail runtime exposes TAW\Core\Mail\Mailer together with MailTemplate so you can send templated HTML or MJML emails from your theme using the same conventions as the rest of the framework. The TAW Core overview shows how these tools sit alongside blocks and the asset pipeline, and points you to deeper docs for each area.

Think of a new project as configuring TAW Core once, then spending your time in TAW Theme:

Boot TAW Core in your theme

Call TAW\Core\Theme::boot() from your theme's functions.php and configure Theme::performance([...]) as needed. Once the core is booted, block discovery, metaboxes, and assets become available.

Design your Blocks directory

Create a Blocks/ directory and sketch your main sections: UI Blocks for reusable pieces, MetaBlocks for content editors manage. Name blocks after the role they play in the layout (for example, Hero, FeatureGrid, FooterNav).

Define fields and options

For MetaBlocks, implement fields() so each block declares its own metabox fields. For site-wide settings, add options pages that store values in wp_options. Use these APIs instead of ad‑hoc get_post_meta() calls.

Wire up assets and Tailwind

Configure your Vite entrypoints, import Tailwind v4, and set up any per-block scripts or styles. Run Vite in dev for HMR, and build to public/build/ for production. Confirm that blocks enqueue only the assets they need.

Once you are comfortable with these concepts, move on to implementing concrete blocks and layouts in your TAW Theme and iterating on the editor experience.