wordpress-block-editor-fse

安装量: 123
排名: #6999

安装

npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill wordpress-block-editor-fse

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:

templates/index.html (with query loop):

Template Parts

parts/header.html:

Block Patterns

patterns/hero.php:

Welcome to Our Site

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:

'testimonial-block', ]); ?>
>
0) : ?>

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';

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}
/>

setAttributes({ imageUrl: media.url })} allowedTypes={['image']} render={({ open }) => ( )} />

setAttributes({ bgColor: v }), label: 'Background' } ]} />

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() the_content() the_post_thumbnail() the_date() wp_nav_menu() get_header() get_footer() get_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 (

,
,
) ✅ 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

返回排行榜