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+

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.

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.


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
composer dump-autoloadRebuild classmap after adding new block classes

Next steps