
wordpress-block-editor-fse
Modern WordPress block development and Full Site Editing with theme.json, block themes, and custom blocks for WordPress 6.7+
- Installs
- 463
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill wordpress-block-editor-fse- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads wordpress-block-editor-fse automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
WordPress Block Editor & Full Site Editing
Overview
Full Site Editing (FSE) is production-ready (since WP 6.2) and treats everything as blocks—headers, footers, templates, not just content. Block themes use HTML templates + theme.json instead of PHP files + style.css.
Key Components:
- theme.json: Centralized colors, typography, spacing, layout
- HTML Templates: Block-based files (index.html, single.html)
- Template Parts: Reusable components (header.html, footer.html)
- Block Patterns: Pre-designed block layouts
- Site Editor: Visual template customization
When to Use: ✅ New themes, consistent design systems, non-technical user customization ❌ Complex server logic, team unfamiliar with blocks, heavy PHP dependencies
Full Site Editing Architecture
Block Themes vs Classic Themes
| Block Themes | Classic Themes |
|---|---|
| HTML files with blocks | PHP files with template tags |
| theme.json + CSS | functions.php + style.css |
| Site Editor (visual) | Customizer (settings) |
| User edits templates | Limited customization |
Site Editor Capabilities
- Template editing (pages, posts, archives)
- Template parts (header/footer variations)
- Global styles (colors, typography site-wide)
- Pattern library (save/reuse block compositions)
- Navigation menus (block-based)
- Style variations (alternate design presets)
theme.json Configuration
theme.json v3 (WP 6.7) provides centralized design control. WordPress auto-generates CSS custom properties.
Production Example
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"appearanceTools": true,
"useRootPaddingAwareAlignments": true,
"layout": {
"contentSize": "800px",
"wideSize": "1200px"
},
"color": {
"palette": [
{ "slug": "primary", "color": "#0073aa", "name": "Primary" },
{ "slug": "secondary", "color": "#005177", "name": "Secondary" },
{ "slug": "base", "color": "#ffffff", "name": "Base" },
{ "slug": "contrast", "color": "#000000", "name": "Contrast" }
],
"defaultPalette": false,
"defaultGradients": false
},
"typography": {
"fontFamilies": [
{
"fontFamily": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
"slug": "system",
"name": "System Font"
}
],
"fontSizes": [
{ "slug": "small", "size": "0.875rem", "name": "Small" },
{ "slug": "medium", "size": "1rem", "name": "Medium" },
{
"slug": "large",
"size": "1.5rem",
"name": "Large",
"fluid": { "min": "1.25rem", "max": "1.5rem" }
}
],
"fontWeight": true,
"lineHeight": true
},
"spacing": {
"units": ["px", "em", "rem", "vh", "vw", "%"],
"padding": true,
"margin": true,
"spacingSizes": [
{ "slug": "30", "size": "0.5rem", "name": "XS" },
{ "slug": "40", "size": "1rem", "name": "S" },
{ "slug": "50", "size": "1.5rem", "name": "M" },
{ "slug": "60", "size": "2rem", "name": "L" }
]
},
"border": { "radius": true, "color": true, "width": true }
},
"styles": {
"color": {
"background": "var(--wp--preset--color--base)",
"text": "var(--wp--preset--color--contrast)"
},
"typography": {
"fontFamily": "var(--wp--preset--font-family--system)",
"fontSize": "var(--wp--preset--font-size--medium)",
"lineHeight": "1.6"
},
"elements": {
"link": {
"color": { "text": "var(--wp--preset--color--primary)" },
":hover": {
"color": { "text": "var(--wp--preset--color--secondary)" }
}
},
"h1": {
"typography": {
"fontSize": "var(--wp--preset--font-size--large)",
"fontWeight": "700"
}
},
"button": {
"color": {
"background": "var(--wp--preset--color--primary)",
"text": "var(--wp--preset--color--base)"
},
"border": { "radius": "4px" },
":hover": {
"color": { "background": "var(--wp--preset--color--secondary)" }
}
}
},
"blocks": {
"core/quote": {
"border": {
"width": "0 0 0 4px",
"color": "var(--wp--preset--color--primary)"
},
"spacing": { "padding": { "left": "var(--wp--preset--spacing--60)" } }
}
}
},
"customTemplates": [
{
"name": "page-wide",
"title": "Full Width Page",
"postTypes": ["page"]
}
]
}
CSS Custom Properties Auto-Generated
- Colors:
var(--wp--preset--color--primary) - Fonts:
var(--wp--preset--font-family--system) - Sizes:
var(--wp--preset--font-size--large) - Spacing:
var(--wp--preset--spacing--50)
Fluid Typography
Font sizes with fluid: { min, max } auto-scale using clamp():
{
"slug": "large",
"size": "1.5rem",
"fluid": { "min": "1.25rem", "max": "1.5rem" }
}
Block Theme Architecture
Required Files
my-block-theme/
├── style.css # Theme metadata (REQUIRED)
├── theme.json # Settings/styles (REQUIRED)
├── templates/
│ ├── index.html # Fallback (REQUIRED)
│ ├── single.html
│ ├── page.html
│ └── archive.html
├── parts/
│ ├── header.html
│ └── footer.html
├── patterns/ # Block patterns
│ └── hero.php
└── functions.php # Optional setup
style.css Metadata
/*
Theme Name: My Block Theme
Requires at least: 6.4
Requires PHP: 8.1
Version: 1.0.0
*/
HTML Template Structure
templates/single.html:
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- wp:post-title {"level":1} /-->
<!-- wp:post-featured-image /-->
<!-- wp:post-content /-->
<!-- wp:post-date /-->
</div>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->
templates/index.html (with query loop):
<!-- wp:template-part {"slug":"header"} /-->
<!-- wp:group {"tagName":"main"} -->
<main class="wp-block-group">
<!-- wp:query {"queryId":1,"query":{"perPage":10,"postType":"post"}} -->
<div class="wp-block-query">
<!-- wp:post-template {"layout":{"type":"grid","columnCount":3}} -->
<!-- wp:post-featured-image {"isLink":true} /-->
<!-- wp:post-title {"isLink":true} /-->
<!-- wp:post-excerpt /-->
<!-- /wp:post-template -->
<!-- wp:query-pagination -->
<!-- wp:query-pagination-previous /-->
<!-- wp:query-pagination-numbers /-->
<!-- wp:query-pagination-next /-->
<!-- /wp:query-pagination -->
</div>
<!-- /wp:query -->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer"} /-->
Template Parts
parts/header.html:
<!-- wp:group {"layout":{"type":"flex","justifyContent":"space-between"}} -->
<div class="wp-block-group">
<!-- wp:site-logo {"width":60} /-->
<!-- wp:navigation /-->
</div>
<!-- /wp:group -->
Block Patterns
patterns/hero.php:
<?php
/**
* Title: Hero Section
* Slug: my-theme/hero
* Categories: featured
*/
?>
<!-- wp:cover {"url":"<?php echo esc_url(get_template_directory_uri()); ?>/assets/images/hero.jpg","dimRatio":50,"minHeight":500,"align":"full"} -->
<div class="wp-block-cover alignfull">
<div class="wp-block-cover__inner-container">
<!-- wp:heading {"textAlign":"center","level":1,"fontSize":"xx-large"} -->
<h1>Welcome to Our Site</h1>
<!-- /wp:heading -->
<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
<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>
</div>
<!-- /wp:cover -->
Register pattern categories:
add_action('init', 'register_pattern_categories');
function register_pattern_categories() {
register_block_pattern_category('hero', [
'label' => __('Hero Sections', 'my-theme')
]);
register_block_pattern_category('cta', [
'label' => __('Call to Action', 'my-theme')
]);
}
Custom Block Development
block.json Metadata (Block API v3)
blocks/testimonial/block.json:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "my-theme/testimonial",
"title": "Testimonial",
"category": "widgets",
"icon": "format-quote",
"attributes": {
"content": {
"type": "string",
"source": "html",
"selector": ".testimonial-content"
},
"author": { "type": "string", "default": "" },
"role": { "type": "string", "default": "" },
"rating": { "type": "number", "default": 5 }
},
"supports": {
"html": false,
"align": ["wide", "full"],
"color": { "background": true, "text": true },
"spacing": { "padding": true, "margin": true }
},
"render": "file:./render.php"
}
Attribute Sources
Different ways to extract data from HTML:
"attributes": {
"title": {
"type": "string",
"source": "html",
"selector": "h2"
},
"linkUrl": {
"type": "string",
"source": "attribute",
"selector": "a",
"attribute": "href"
},
"isActive": {
"type": "boolean",
"default": false
},
"items": {
"type": "array",
"source": "query",
"selector": ".item",
"query": {
"text": { "type": "string", "source": "text" }
}
}
}
Server-Side Rendering (render.php)
blocks/testimonial/render.php:
<?php
$content = $attributes['content'] ?? '';
$author = $attributes['author'] ?? '';
$role = $attributes['role'] ?? '';
$rating = absint($attributes['rating'] ?? 5);
$wrapper_attributes = get_block_wrapper_attributes([
'class' => 'testimonial-block',
]);
?>
<div <?php echo $wrapper_attributes; ?>>
<blockquote class="testimonial-content">
<?php echo wp_kses_post($content); ?>
</blockquote>
<?php if ($rating > 0) : ?>
<div class="testimonial-rating">
<?php for ($i = 1; $i <= 5; $i++) : ?>
<span class="star <?php echo $i <= $rating ? 'filled' : 'empty'; ?>">
<?php echo $i <= $rating ? '★' : '☆'; ?>
</span>
<?php endfor; ?>
</div>
<?php endif; ?>
<?php if ($author || $role) : ?>
<cite class="testimonial-author">
<span class="author-name"><?php echo esc_html($author); ?></span>
<?php if ($role) : ?>
<span class="author-role"><?php echo esc_html($role); ?></span>
<?php endif; ?>
</cite>
<?php endif; ?>
</div>
Client-Side Rendering (React)
blocks/testimonial/index.js:
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, RichText, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, RangeControl, TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
registerBlockType('my-theme/testimonial', {
edit: ({ attributes, setAttributes }) => {
const { content, author, role, rating } = attributes;
const blockProps = useBlockProps();
return (
<>
<InspectorControls>
<PanelBody title={__('Settings', 'my-theme')}>
<TextControl
label={__('Author', 'my-theme')}
value={author}
onChange={(v) => setAttributes({ author: v })}
/>
<TextControl
label={__('Role', 'my-theme')}
value={role}
onChange={(v) => setAttributes({ role: v })}
/>
<RangeControl
label={__('Rating', 'my-theme')}
value={rating}
onChange={(v) => setAttributes({ rating: v })}
min={1}
max={5}
/>
</PanelBody>
</InspectorControls>
<div {...blockProps}>
<RichText
tagName="blockquote"
value={content}
onChange={(v) => setAttributes({ content: v })}
placeholder={__('Testimonial text...', 'my-theme')}
/>
<div className="testimonial-rating">
{[1, 2, 3, 4, 5].map((star) => (
<span
key={star}
onClick={() => setAttributes({ rating: star })}
>
{star <= rating ? '★' : '☆'}
</span>
))}
</div>
<cite>
<RichText
tagName="span"
value={author}
onChange={(v) => setAttributes({ author: v })}
placeholder={__('Author', 'my-theme')}
/>
</cite>
</div>
</>
);
},
save: () => null, // Server-side rendering
});
Block Registration
functions.php:
add_action('init', 'register_custom_blocks');
function register_custom_blocks() {
register_block_type(__DIR__ . '/blocks/testimonial');
}
InspectorControls (Settings Sidebar)
Common controls for block settings:
import {
InspectorControls,
PanelColorSettings,
MediaUpload
} from '@wordpress/block-editor';
import {
PanelBody,
SelectControl,
ToggleControl,
RangeControl,
Button
} from '@wordpress/components';
<InspectorControls>
<PanelBody title="Layout">
<SelectControl
label="Columns"
value={columns}
options={[
{ label: '2', value: 2 },
{ label: '3', value: 3 },
{ label: '4', value: 4 }
]}
onChange={(v) => setAttributes({ columns: parseInt(v) })}
/>
<ToggleControl
label="Enable Shadow"
checked={enableShadow}
onChange={(v) => setAttributes({ enableShadow: v })}
/>
<RangeControl
label="Border Radius"
value={borderRadius}
onChange={(v) => setAttributes({ borderRadius: v })}
min={0}
max={50}
/>
</PanelBody>
<PanelBody title="Media">
<MediaUpload
onSelect={(media) => setAttributes({ imageUrl: media.url })}
allowedTypes={['image']}
render={({ open }) => (
<Button onClick={open} variant="secondary">
{imageUrl ? 'Change Image' : 'Select Image'}
</Button>
)}
/>
</PanelBody>
<PanelColorSettings
title="Colors"
colorSettings={[
{
value: bgColor,
onChange: (v) => setAttributes({ bgColor: v }),
label: 'Background'
}
]}
/>
</InspectorControls>
Block Supports
Enable WordPress features:
"supports": {
"html": false,
"anchor": true,
"align": ["wide", "full"],
"color": {
"background": true,
"text": true,
"gradients": true
},
"spacing": {
"padding": true,
"margin": true,
"blockGap": true
},
"typography": {
"fontSize": true,
"lineHeight": true,
"fontWeight": true
}
}
Custom Post Types with Block Editor
add_action('init', 'register_book_cpt');
function register_book_cpt() {
register_post_type('book', [
'labels' => [
'name' => __('Books', 'my-theme'),
'singular_name' => __('Book', 'my-theme'),
],
'public' => true,
'has_archive' => true,
'supports' => ['title', 'editor', 'thumbnail'],
'show_in_rest' => true, // REQUIRED for block editor
'menu_icon' => 'dashicons-book',
'template' => [ // Default blocks
['core/paragraph', ['placeholder' => 'Book description...']],
['core/image'],
['my-theme/book-details'],
],
'template_lock' => 'insert', // Can't add/remove blocks
]);
// Register taxonomy
register_taxonomy('genre', 'book', [
'labels' => ['name' => __('Genres', 'my-theme')],
'hierarchical' => true,
'show_in_rest' => true, // REQUIRED
]);
}
Template Locking
false: No restrictions'all': Cannot modify structure'insert': Cannot add/remove, can reorder'contentOnly': Content edits only
Register in theme.json
"customTemplates": [
{
"name": "single-book",
"title": "Book Template",
"postTypes": ["book"]
}
]
Development Workflow
@wordpress/scripts
package.json:
{
"scripts": {
"start": "wp-scripts start",
"build": "wp-scripts build"
},
"devDependencies": {
"@wordpress/scripts": "^27.0.0"
}
}
Commands:
npm install
npm run start # Development with hot reload
npm run build # Production build (minified)
wp-env Setup
.wp-env.json:
{
"core": "WordPress/WordPress#6.7",
"phpVersion": "8.3",
"themes": ["./my-block-theme"],
"config": {
"WP_DEBUG": true,
"SCRIPT_DEBUG": true
}
}
Usage:
npx @wordpress/env start
# Access: http://localhost:8888
# Admin: admin / password
npx @wordpress/env stop
npx @wordpress/env clean # Reset database
Migration from Classic Themes
Template Tag to Block Mapping
| Classic | Block Equivalent |
|---|---|
the_title() | <!-- wp:post-title /--> |
the_content() | <!-- wp:post-content /--> |
the_post_thumbnail() | <!-- wp:post-featured-image /--> |
the_date() | <!-- wp:post-date /--> |
wp_nav_menu() | <!-- wp:navigation /--> |
get_header() | <!-- wp:template-part {"slug":"header"} /--> |
get_footer() | <!-- wp:template-part {"slug":"footer"} /--> |
get_sidebar() | <!-- wp:template-part {"slug":"sidebar"} /--> |
Migration Steps
- Extract design tokens from style.css → theme.json
- Convert PHP templates to HTML block templates
- Add block support in functions.php:
add_theme_support('wp-block-styles');
add_theme_support('align-wide');
add_theme_support('responsive-embeds');
- Test thoroughly with real content
Block Validation
WordPress validates block markup against registered block definitions. Invalid blocks show errors in the editor:
Common validation errors:
- Attribute type mismatch (string vs number)
- Missing required attributes
- Incorrect HTML structure
- Changed attribute names
Fix validation errors:
// Add deprecated versions for backward compatibility
const deprecated = [
{
attributes: {
oldName: { type: 'string' }
},
migrate: (attributes) => ({
newName: attributes.oldName
}),
save: (props) => {
// Old save function
}
}
];
Performance & Best Practices
Performance
✅ Use server-side rendering (render.php) when possible
✅ Leverage block supports (reduces custom CSS)
✅ Disable unused features: "defaultPalette": false
✅ Use CSS custom properties for consistency
❌ Avoid client-side rendering for static content
❌ Don't override core blocks with !important
Accessibility
✅ Semantic HTML (<header>, <main>, <footer>)
✅ Keyboard navigation for custom blocks
✅ WCAG AA color contrast (4.5:1 minimum)
✅ Alt text for all images
❌ Don't assume FSE = accessible (test required)
Anti-Patterns
❌ Mixing classic and block approaches
❌ Hardcoding colors (use CSS variables)
❌ Reinventing block supports
❌ Skipping accessibility testing
❌ Using get_header() in HTML templates
Related Skills
- wordpress-plugin-fundamentals: Hook system, CPTs
- react: Block editor components
- typescript: Type-safe block development
- php-security: Sanitize block attributes
Key Reminders
- theme.json is mandatory for block themes
- HTML templates replace PHP in FSE
- Server-side rendering often better than client-side
- Block supports reduce custom code
- Accessibility requires testing
Red Flags
- More than 5 CSS files → Use theme.json
- PHP tags in HTML templates → Use blocks
- Client rendering for static content → Use render.php
- No keyboard testing → Accessibility issues
- Hardcoded values → Use CSS custom properties
WordPress: 6.7+ | PHP: 8.1+ | Tools: @wordpress/scripts, wp-env
FAQ
What does the wordpress-block-editor-fse skill do?
Modern WordPress block development and Full Site Editing with theme.json, block themes, and custom blocks for WordPress 6.7+
How do I install wordpress-block-editor-fse?
Run npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill wordpress-block-editor-fse in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/wordpress-block-editor-fse/ (personal) or .claude/skills/wordpress-block-editor-fse/ (project).
Which AI agents can use wordpress-block-editor-fse?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is wordpress-block-editor-fse safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains wordpress-block-editor-fse?
It is published by bobmatnyc in bobmatnyc/claude-mpm-skills. It has 463 recorded installs.
More from bobmatnyc/claude-mpm-skills
drizzle-orm
bobmatnyc/claude-mpm-skills
Type-safe SQL ORM for TypeScript with zero runtime overhead
playwright-e2e-testing
bobmatnyc/claude-mpm-skills
Playwright modern end-to-end testing framework with cross-browser automation, auto-wait, and built-in test runner
drizzle
bobmatnyc/claude-mpm-skills
Type-safe SQL ORM for TypeScript with zero runtime overhead
pytest
bobmatnyc/claude-mpm-skills
pytest - Python's most powerful testing framework with fixtures, parametrization, plugins, and framework integration for FastAPI, Django, Flask
drizzle-migrations
bobmatnyc/claude-mpm-skills
Migration-first database development workflow using Drizzle ORM for TypeScript/J...
tailwind-css
bobmatnyc/claude-mpm-skills
Tailwind CSS utility-first framework for rapid UI development with responsive design and dark mode
Popular frontend & design skills
setup-matt-pocock-skills
mattpocock/skills
Configure this repo for the engineering skills: set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.
frontend-design
anthropics/skills
Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.
hyperframes-animation
heygen-com/hyperframes
All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.
prototype
mattpocock/skills
Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.
hyperframes-core
heygen-com/hyperframes
The HyperFrames composition contract — build one renderable project. Use for composition structure, the data- timing attributes, class="clip", tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Read before writing composition HTML.
vercel-react-best-practices
vercel-labs/agent-skills
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.