WordPress Block Theme Multi-tenant Design —
WordPress Block Theme
WordPress Block Theme Multi-tenant Full Site Editing FSE theme.json Block Patterns Multisite Network Global Styles Template Parts Domain Mapping Production
| Feature | Classic Theme | Block Theme | Hybrid Theme |
|---|---|---|---|
| Template | PHP files | HTML + Blocks | PHP + Blocks |
| Styling | style.css | theme.json | Both |
| Editing | Customizer | Site Editor | Both |
| Patterns | Limited | Full support | Full support |
| Global Styles | ไม่รองรับ | เต็มรูปแบบ | บางส่วน |
| เหมาะกับ | Legacy sites | New projects | Migration |
theme.json Configuration
=== theme.json for Block Theme ===
theme.json — Main configuration
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 2,
"settings": {
"color": {
"palette": [
{ "slug": "primary", "color": "#1e40af", "name": "Primary" },
{ "slug": "secondary", "color": "#7c3aed", "name": "Secondary" },
{ "slug": "accent", "color": "#f59e0b", "name": "Accent" },
{ "slug": "background", "color": "#ffffff", "name": "Background" },
{ "slug": "foreground", "color": "#1f2937", "name": "Foreground" }
],
"gradients": [],
"defaultPalette": false
},
"typography": {
"fontFamilies": [
{
"fontFamily": "Inter, sans-serif",
"slug": "inter",
"name": "Inter",
"fontFace": [{
"fontFamily": "Inter",
"fontWeight": "400 700",
"fontStyle": "normal",
"src": ["file:./assets/fonts/inter.woff2"]
}]
}
],
"fontSizes": [
{ "slug": "small", "size": "0.875rem", "name": "Small" },
{ "slug": "medium", "size": "1rem", "name": "Medium" },
{ "slug": "large", "size": "1.5rem", "name": "Large" },
{ "slug": "x-large", "size": "2.25rem", "name": "Extra Large" }
]
},
"layout": {
"contentSize": "800px",
"wideSize": "1200px"
},
"spacing": {
"units": ["px", "rem", "%"],
"spacingSizes": [
{ "slug": "10", "size": "0.5rem", "name": "XS" },
{ "slug": "20", "size": "1rem", "name": "S" },
{ "slug": "30", "size": "1.5rem", "name": "M" },
{ "slug": "40", "size": "2rem", "name": "L" },
{ "slug": "50", "size": "3rem", "name": "XL" }
]
}
},
"styles": {
"color": { "background": "var(--wp--preset--color--background)", "text": "var(--wp--preset--color--foreground)" },
"typography": { "fontFamily": "var(--wp--preset--font-family--inter)", "fontSize": "var(--wp--preset--font-size--medium)" },
"elements": {
"h1": { "typography": { "fontSize": "var(--wp--preset--font-size--x-large)" } },
"button": { "color": { "background": "var(--wp--preset--color--primary)", "text": "#ffffff" } },
"link": { "color": { "text": "var(--wp--preset--color--primary)" } }
}
}
}
from dataclasses import dataclass
@dataclass
class ThemeComponent:
component: str
file_path: str
purpose: str
required: bool
components = [
ThemeComponent("theme.json", "theme.json", "Global styles and settings", True),
ThemeComponent("style.css", "style.css", "Theme metadata (name, version)", True),
ThemeComponent("Index Template", "templates/index.html", "Main fallback template", True),
ThemeComponent("Header Part", "parts/header.html", "Site header with nav", True),
ThemeComponent("Footer Part", "parts/footer.html", "Site footer", True),
ThemeComponent("Single Template", "templates/single.html", "Single post template", False),
ThemeComponent("Page Template", "templates/page.html", "Page template", False),
ThemeComponent("Patterns", "patterns/*.php", "Reusable block patterns", False),
]
print("=== Block Theme Structure ===")
for c in components:
req = "Required" if c.required else "Optional"
print(f" [{req}] {c.component}")
print(f" Path: {c.file_path} | Purpose: {c.purpose}")
Multisite Multi-tenant
=== WordPress Multisite Setup ===
wp-config.php — Enable Multisite
define('WP_ALLOW_MULTISITE', true);
After network setup:
define('MULTISITE', true);
define('SUBDOMAIN_INSTALL', true);
define('DOMAIN_CURRENT_SITE', 'example.com');
define('PATH_CURRENT_SITE', '/');
define('SITE_ID_CURRENT_SITE', 1);
define('BLOG_ID_CURRENT_SITE', 1);
.htaccess for Multisite
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
WP-CLI — Manage Multisite
wp site create --slug=tenant-a --title="Tenant A" --email=admin@tenant-a.com
wp site create --slug=tenant-b --title="Tenant B" --email=admin@tenant-b.com
wp site list
wp theme enable my-block-theme --network
wp plugin activate woocommerce --url=tenant-a.example.com
Domain Mapping (WordPress 4.5+)
wp site create --slug=tenant-c --title="Tenant C"
-- In admin: Settings > Domains > Add tenant-c.com
Nginx Configuration per site
server {
listen 443 ssl http2;
server_name tenant-a.com www.tenant-a.com;
root /var/www/wordpress;
# ... standard WordPress config
}
@dataclass
class TenantSite:
tenant: str
domain: str
theme_variation: str
plugins: str
storage: str
users: int
tenants = [
TenantSite("Tenant A", "tenant-a.com", "Blue Corporate", "WooCommerce, Yoast", "5GB", 12),
TenantSite("Tenant B", "tenant-b.com", "Green Nature", "LearnDash, BuddyPress", "10GB", 45),
TenantSite("Tenant C", "tenant-c.com", "Purple Creative", "Elementor, WPForms", "3GB", 5),
TenantSite("Tenant D", "tenant-d.com", "Red Bold", "WooCommerce, Mailchimp", "8GB", 20),
]
print("\n=== Multi-tenant Sites ===")
for t in tenants:
print(f" [{t.tenant}] {t.domain}")
print(f" Theme: {t.theme_variation} | Users: {t.users}")
print(f" Plugins: {t.plugins} | Storage: {t.storage}")
Block Patterns
# === Block Patterns for Multi-tenant ===
# patterns/hero-section.php
# <?php
# /**
# * Title: Hero Section
# * Slug: mytheme/hero-section
# * Categories: featured
# * Keywords: hero, banner, header
# */
# ?>
# <!-- wp:cover {"dimRatio":50} -->
# <div class="wp-block-cover">
# <!-- wp:heading {"level":1} -->
# <h1>Welcome to Our Site</h1>
# <!-- /wp:heading -->
# <!-- wp:paragraph -->
# <p>Discover our services and solutions</p>
# <!-- /wp:paragraph -->
# <!-- wp:buttons -->
# <div class="wp-block-buttons">
# <!-- wp:button -->
# <div class="wp-block-button"><a class="wp-block-button__link">Get Started</a></div>
# <!-- /wp:button -->
# </div>
# <!-- /wp:buttons -->
# </div>
# <!-- /wp:cover -->
@dataclass
class BlockPattern:
name: str
slug: str
category: str
blocks_used: str
tenant_customizable: bool
patterns = [
BlockPattern("Hero Section", "mytheme/hero", "Featured", "Cover Heading Paragraph Buttons", True),
BlockPattern("Pricing Table", "mytheme/pricing", "Commerce", "Columns Group Heading List Button", True),
BlockPattern("Testimonials", "mytheme/testimonials", "Social", "Columns Quote Image Paragraph", True),
BlockPattern("FAQ Accordion", "mytheme/faq", "Content", "Details Summary Paragraph", True),
BlockPattern("CTA Banner", "mytheme/cta", "Featured", "Group Heading Paragraph Buttons", True),
BlockPattern("Team Grid", "mytheme/team", "About", "Columns Image Heading Paragraph Social", True),
]
print("Block Patterns:")
for p in patterns:
custom = "Customizable per tenant" if p.tenant_customizable else "Fixed"
print(f" [{p.name}] {p.slug}")
print(f" Category: {p.category} | Blocks: {p.blocks_used}")
print(f" {custom}")
performance = {
"Page Cache": "WP Super Cache / Redis Object Cache",
"CDN": "Cloudflare / BunnyCDN สำหรับ Static Assets",
"Image": "WebP conversion + Lazy Loading",
"Database": "Query Monitor + Index Optimization",
"PHP": "OPcache enabled PHP 8.2+",
"Cron": "Disable wp-cron ใช้ System Cron แทน",
}
print(f"\n\nPerformance Optimization:")
for k, v in performance.items():
print(f" [{k}]: {v}")
เคล็ดลับ
- theme.json: กำหนดทุกอย่างใน theme.json ลด Custom CSS
- Variations: ใช้ Style Variations แยก Look ต่อ Tenant
- Patterns: สร้าง Block Patterns ให้ Tenant เลือกใช้
- Multisite: ใช้ Multisite สำหรับ 5+ Tenant
- Cache: ใช้ Object Cache + Page Cache ทุก Site
WordPress Block Theme คืออะไร
Full Site Editing FSE Block Editor HTML Template theme.json Block Patterns Global Styles Template Parts WordPress 6.0+ ไม่ต้อง PHP Template