Start HereQuickstart

Quickstart

Install TAW Theme, start the Vite dev server, scaffold your first MetaBlock, and render it on a page — in under 10 minutes.

Before you start

Make sure your environment meets these requirements:

  • WordPress 6.0+
  • PHP 8.1+
  • Composer 2.0+
  • Node.js 20.19+
  • npm 8+
  • Git, plus an empty repository already created on GitHub (or your git host of choice) for this project

TAW does not use Gutenberg blocks or ACF. Blocks are plain PHP classes and templates — no block editor registration needed.


Step 1 — Install the theme

Scaffold a new TAW Theme project

Run this from your WordPress wp-content/themes directory. Replace my-theme with your desired folder name.

cd wp-content/themes

composer create-project taw/theme my-theme \
  --repository='{"type":"vcs","url":"https://github.com/Relmaur/taw-theme"}'

This creates the theme folder with the correct structure and pulls in the taw/core framework package.

Initialize git and push to your own repository

cd my-theme

git init
git add -A
git commit -m "Initial commit"
git remote add origin <your-repo-url>
git push -u origin main

<your-repo-url> assumes an empty repository already exists for this project — create one on github.com first, or use gh repo create <name> --private --source=. --remote=origin --push to create it, add the remote, and push in one step (in which case skip the last two lines above).

A shared git history with the canonical taw-theme repo isn't required — framework updates sync via direct file copy (update-theme), not a git merge. A single clean commit is enough.

Install PHP and JS dependencies

cd my-theme

composer install   # PHP deps — pulls taw/core
npm install        # Frontend dependencies

Start the Vite dev server

npm run dev

Vite starts on port 5173 with hot module reloading. Leave this running while you develop — CSS and JS changes reflect instantly in the browser without a full reload.

Activate the theme in WordPress

  • Log in to your WordPress admin.
  • Go to Appearance → Themes.
  • Find your new theme and click Activate.

Visit the frontend — you should see the TAW Theme layout rendered with Vite-powered assets.

Set up framework-sync and optional integrations

Your new project ships with .github/workflows/framework-sync.yml from the first commit — a weekly check that bumps taw/core, syncs framework-owned scaffold files, and opens a pull request if anything changed.

If you're working with Claude Code, run the project-init skill now — it checks gh CLI auth, enables the required GitHub setting below with confirmation, triggers a real run to verify the whole pipeline works, and walks through optional integrations (Cloudflare Turnstile, transactional email, CSS Studio, Visual Editor) as explicit yes/no questions.

Setting it up by hand instead just needs one manual setting enabled on the new repo:

Settings → Actions → General → "Allow GitHub Actions to create and approve pull requests"

This is off by default on every new GitHub repo. Without it, the workflow still runs on schedule, but its PR-opening step silently fails — nothing breaks loudly, it just never surfaces the update.


Step 2 — Create your first block

TAW has two block types. For this quickstart we'll build a MetaBlock — a page section backed by metabox fields in the WordPress admin.

Scaffold the block with the CLI

Run the block generator from your theme root:

php bin/taw make:block Hero --type=meta --with-style
composer dump-autoload

This creates Blocks/Hero/ with three files:

Blocks/Hero/
├── Hero.php       ← the block class
├── index.php      ← the template
└── style.scss     ← per-block styles (auto-enqueued)

composer dump-autoload is required after adding any new block class so Composer's PSR-4 classmap picks it up.

Define your metabox fields

Open Blocks/Hero/Hero.php. The CLI generates a skeleton — fill in your fields inside registerMetaboxes() and return the data you need in getData().

<?php
// 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';

    protected function registerMetaboxes(): void
    {
        new Metabox([
            'id'      => 'taw_hero',
            'title'   => 'Hero Section',
            'screens' => ['page'],
            'fields'  => [
                ['id' => 'hero_heading', 'label' => 'Heading', 'type' => 'text',     'required' => true],
                ['id' => 'hero_subtext', 'label' => 'Subtext', 'type' => 'textarea'],
                ['id' => 'hero_image',   'label' => 'Image',   'type' => 'image'],
            ],
        ]);
    }

    protected function getData(int|false $postId): array
    {
        return [
            'heading'   => $this->getMeta($postId, 'hero_heading'),
            'subtext'   => $this->getMeta($postId, 'hero_subtext'),
            'image_url' => $this->getImageUrl($postId, 'hero_image', 'large'),
        ];
    }
}

TAW discovers the metabox automatically — it appears in the WordPress page editor as soon as the class is loaded.

Write the template

Open Blocks/Hero/index.php. The variables returned by getData() are extracted into the template scope automatically.

<?php
// Blocks/Hero/index.php

if (empty($heading)) return;
?>

<section class="hero">
    <h1><?php echo esc_html($heading); ?></h1>

    <?php if ($subtext): ?>
        <p><?php echo esc_html($subtext); ?></p>
    <?php endif; ?>

    <?php if ($image_url): ?>
        <img src="<?php echo esc_url($image_url); ?>" alt="">
    <?php endif; ?>
</section>

Step 3 — Render the block on a page

TAW blocks are not Gutenberg blocks. You queue and render them from your PHP templates.

Queue and render in a WordPress template

Open (or create) front-page.php in your theme root. Queue the block's assets before get_header(), then render it where it should appear in the page.

<?php
// front-page.php

use TAW\Core\Block\BlockRegistry;

BlockRegistry::queue('hero');  // schedules CSS/JS for <head>
get_header();
?>

<?php BlockRegistry::render('hero'); ?>

<?php get_footer(); ?>

TAW automatically passes the current post ID to getData() and extracts the result into the template.

Fill in content in the WordPress admin

  • Open a page in the WordPress editor.
  • Scroll down — your Hero Section metabox appears below the editor.
  • Fill in the Heading, Subtext, and Image fields.
  • Publish and visit the page.

You should see your Hero block rendered with the content you entered.


What you just built

  • BlockLoader::loadAll() (called by Theme::boot()) discovered Blocks/Hero/Hero.php automatically — no registration required.
  • Metabox rendered the admin UI and saved field values to post_meta.
  • BlockRegistry::queue('hero') scheduled style.scss for <head> — only on pages that use this block.
  • BlockRegistry::render('hero') called getData(), extracted the result, and loaded index.php.

Add a second block type: UI Block

UI Blocks are stateless components that receive props at render time — no metaboxes, no post_meta.

php bin/taw make:block Button --type=ui --with-style
composer dump-autoload
// Blocks/Button/Button.php
namespace TAW\Blocks\Button;

use TAW\Core\Block\Block;

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

    protected function defaultData(): array
    {
        return [
            'text'  => '',
            'url'   => '#',
            'style' => 'primary',
        ];
    }
}

Use it inside any template or MetaBlock template:

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

Props are merged over the defaults() array — missing props always have safe fallbacks.


Multi-block pages

Queue multiple blocks at once, then render each where it belongs:

<?php
// front-page.php
use TAW\Core\Block\BlockRegistry;

BlockRegistry::queue('hero', 'features', 'stats', 'testimonials', 'cta');
get_header();
?>

<?php BlockRegistry::render('hero'); ?>
<?php BlockRegistry::render('features'); ?>
<?php BlockRegistry::render('stats'); ?>
<?php BlockRegistry::render('testimonials'); ?>
<?php BlockRegistry::render('cta'); ?>

<?php get_footer(); ?>

Production build

When you're ready to deploy:

npm run build

Vite outputs content-hashed CSS and JS to public/build/. WordPress loads these automatically when the dev server is not running.


Useful CLI commands

CommandWhat it does
php bin/taw make:block Name --type=metaScaffold a MetaBlock
php bin/taw make:block Name --type=uiScaffold a UI Block
php bin/taw make:block Name --group=sectionsScaffold inside Blocks/sections/Name/
php bin/taw make:block Name --with-style --with-scriptInclude style.scss and script.js
php bin/taw export:block HeroExport a block as a portable ZIP
php bin/taw import:block path/to/Hero.zipImport a block from a ZIP
php bin/taw sync --jsonCheck for drift: taw/core version + taw-theme scaffold
php bin/taw sync --applyAlso write safe (Tier 1) scaffold changes
composer dump-autoloadRebuild classmap after adding new block classes

Next steps