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 byTheme::boot()) discoveredBlocks/Hero/Hero.phpautomatically — no registration required.Metaboxrendered the admin UI and saved field values topost_meta.BlockRegistry::queue('hero')scheduledstyle.scssfor<head>— only on pages that use this block.BlockRegistry::render('hero')calledgetData(), extracted the result, and loadedindex.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
| Command | What it does |
|---|---|
php bin/taw make:block Name --type=meta | Scaffold a MetaBlock |
php bin/taw make:block Name --type=ui | Scaffold a UI Block |
php bin/taw make:block Name --group=sections | Scaffold inside Blocks/sections/Name/ |
php bin/taw make:block Name --with-style --with-script | Include style.scss and script.js |
php bin/taw export:block Hero | Export a block as a portable ZIP |
php bin/taw import:block path/to/Hero.zip | Import a block from a ZIP |
php bin/taw sync --json | Check for drift: taw/core version + taw-theme scaffold |
php bin/taw sync --apply | Also write safe (Tier 1) scaffold changes |
composer dump-autoload | Rebuild classmap after adding new block classes |