How to build a full site editing (FSE) block theme from scratch?

Building a Full Site Editing (FSE) block theme from scratch in WordPress can feel like a tall order, but it’s totally achievable once you get a handle on the core concepts. In essence, you’re creating a theme where nearly every aspect of the site – from headers to footers, posts to pages – is controlled through blocks and the WordPress Site Editor. This means less hand-coding and more visual design, giving users unprecedented control over their site’s layout and content directly within the admin.

Getting Started: The Basic Structure

Let’s dive into the absolute bare minimum you need to get an FSE theme up and running. Think of this as your skeleton; we’ll add the meat and muscles later.

Your Theme Folder

First things first, create a new folder inside your wp-content/themes directory. Let’s call it my-fse-theme for now. This will house all your theme’s files.

The style.css File

Every WordPress theme needs a style.css file at its root. Even if you’re not putting all your styles in here, it’s how WordPress identifies your theme.

In my-fse-theme/style.css, add the following:

“`css

/*

Theme Name: My FSE Theme

Theme URI: https://example.com/my-fse-theme

Author: Your Name

Author URI: https://example.com

Description: A simple Full Site Editing block theme.

Version: 1.0.0

Requires at least: 5.9

Requires PHP: 7.4

Text Domain: my-fse-theme

License: GNU General Public License v2 or later

License URI: http://www.gnu.org/licenses/gpl-2.0.html

*/

“`

Fill in your own details here. The Requires at least: 5.9 is crucial as FSE was officially introduced in WordPress 5.9.

The index.php File

Tradition dictates an index.php file, even for FSE themes. In its simplest form, it acts as a fallback. For FSE, it often just loads the block template parts.

In my-fse-theme/index.php, just put:

“`php

// Nothing to see here.

// This file is just a fallback for block themes.

// All template handling is done via the block editor.

“`

Technically, for an FSE theme, index.php isn’t strictly required to render content directly if you have an index.html file in your templates directory, as the block editor takes precedence. However, including it is good practice for compatibility and debugging.

The theme.json File

This is where the magic really starts for FSE themes. theme.json is your central control panel for default styles, block settings, and various design features. It defines everything from typography and color palettes to spacing and layout controls, all accessible within the Site Editor.

In my-fse-theme/theme.json, start with a basic structure:

“`json

{

“version”: 2,

“settings”: {

“color”: {

“palette”: [

{

“slug”: “primary”,

“color”: “#007cba”,

“name”: “Primary”

},

{

“slug”: “secondary”,

“color”: “#f2f2f2”,

“name”: “Secondary”

},

{

“slug”: “dark”,

“color”: “#333333”,

“name”: “Dark”

},

{

“slug”: “light”,

“color”: “#ffffff”,

“name”: “Light”

}

]

},

“typography”: {

“fontSizes”: [

{

“slug”: “small”,

“size”: “0.875rem”,

“name”: “Small”

},

{

“slug”: “normal”,

“size”: “1rem”,

“name”: “Normal”

},

{

“slug”: “large”,

“size”: “1.25rem”,

“name”: “Large”

},

{

“slug”: “x-large”,

“size”: “1.75rem”,

“name”: “Extra Large”

}

],

“fontFamilies”: [

{

“fontFamily”: “-apple-system,BlinkMacSystemFont,\”Segoe UI\”,Roboto,Oxygen-Sans,Ubuntu,Cantarell,\”Helvetica Neue\”,sans-serif”,

“name”: “System Font”,

“slug”: “system-font”

}

]

},

“layout”: {

“contentSize”: “650px”,

“wideSize”: “1000px”

},

“blocks”: {

“core/paragraph”: {

“typography”: {

“dropCap”: false

}

}

}

},

“styles”: {

“blocks”: {

“core/post-title”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–x-large)”

},

“color”: {

“text”: “var(–wp–preset–color–primary)”

}

}

}

}

}

“`

This theme.json sets up some basic color palettes, font sizes, a default system font, and defines the content and wide widths for blocks. It also shows a basic example of applying styles to a specific block (core/post-title). We’ll expand on this a lot, but this is a solid start.

If you’re interested in learning more about building a full site editing (FSE) block theme from scratch, you might find this related article helpful: How to Build a Full Site Editing Block Theme. This resource provides valuable insights and step-by-step guidance that can enhance your understanding of the FSE process, making it easier for you to create a custom theme tailored to your needs.

Building Block Templates and Template Parts

This is the core of what makes an FSE theme work. Instead of traditional PHP template files, you’re using HTML files filled with block markup to define your layouts.

The templates Directory

Create a folder named templates inside your my-fse-theme directory. This is where your main page and post layouts will live.

index.html (The Main Template)

This is your most important template. It’s the default fallback for listing posts and acts as your blog archive.

Create my-fse-theme/templates/index.html:

“`html

  • How to build a full site editing (FSE) block theme from scratch?

    Building a Full Site Editing (FSE) block theme from scratch in WordPress can feel like a tall order, but it’s totally achievable once you get a handle on the core concepts. In essence, you’re creating a theme where nearly every aspect of the site – from headers to footers, posts to pages – is controlled…

“`

This index.html does a few crucial things:

  • It includes a header and footer template part (which we’ll create next).
  • It uses the core/query block to display a list of posts.
  • Inside the core/query block, it uses the core/post-template block to loop through individual posts, displaying the title, featured image, excerpt, and date.
  • It includes the core/query-pagination block.
single.html (Individual Posts)

You’ll want a template for single posts.

Create my-fse-theme/templates/single.html:

“`html

How to build a full site editing (FSE) block theme from scratch?

Building a Full Site Editing (FSE) block theme from scratch in WordPress can feel like a tall order, but it’s totally achievable once you get a handle on the core concepts. In essence, you’re creating a theme where nearly every aspect of the site – from headers to footers, posts to pages – is controlled through blocks and the WordPress Site Editor. This means less hand-coding and more visual design, giving users unprecedented control over their site’s layout and content directly within the admin.

Getting Started: The Basic Structure

Let’s dive into the absolute bare minimum you need to get an FSE theme up and running. Think of this as your skeleton; we’ll add the meat and muscles later.

Your Theme Folder

First things first, create a new folder inside your wp-content/themes directory. Let’s call it my-fse-theme for now. This will house all your theme’s files.

The style.css File

Every WordPress theme needs a style.css file at its root. Even if you’re not putting all your styles in here, it’s how WordPress identifies your theme.

In my-fse-theme/style.css, add the following:

“`css

/*

Theme Name: My FSE Theme

Theme URI: https://example.com/my-fse-theme

Author: Your Name

Author URI: https://example.com

Description: A simple Full Site Editing block theme.

Version: 1.0.0

Requires at least: 5.9

Requires PHP: 7.4

Text Domain: my-fse-theme

License: GNU General Public License v2 or later

License URI: http://www.gnu.org/licenses/gpl-2.0.html

*/

“`

Fill in your own details here. The Requires at least: 5.9 is crucial as FSE was officially introduced in WordPress 5.9.

The index.php File

Tradition dictates an index.php file, even for FSE themes. In its simplest form, it acts as a fallback. For FSE, it often just loads the block template parts.

In my-fse-theme/index.php, just put:

“`php

// Nothing to see here.

// This file is just a fallback for block themes.

// All template handling is done via the block editor.

“`

Technically, for an FSE theme, index.php isn’t strictly required to render content directly if you have an index.html file in your templates directory, as the block editor takes precedence. However, including it is good practice for compatibility and debugging.

The theme.json File

This is where the magic really starts for FSE themes. theme.json is your central control panel for default styles, block settings, and various design features. It defines everything from typography and color palettes to spacing and layout controls, all accessible within the Site Editor.

In my-fse-theme/theme.json, start with a basic structure:

“`json

{

“version”: 2,

“settings”: {

“color”: {

“palette”: [

{

“slug”: “primary”,

“color”: “#007cba”,

“name”: “Primary”

},

{

“slug”: “secondary”,

“color”: “#f2f2f2”,

“name”: “Secondary”

},

{

“slug”: “dark”,

“color”: “#333333”,

“name”: “Dark”

},

{

“slug”: “light”,

“color”: “#ffffff”,

“name”: “Light”

}

]

},

“typography”: {

“fontSizes”: [

{

“slug”: “small”,

“size”: “0.875rem”,

“name”: “Small”

},

{

“slug”: “normal”,

“size”: “1rem”,

“name”: “Normal”

},

{

“slug”: “large”,

“size”: “1.25rem”,

“name”: “Large”

},

{

“slug”: “x-large”,

“size”: “1.75rem”,

“name”: “Extra Large”

}

],

“fontFamilies”: [

{

“fontFamily”: “-apple-system,BlinkMacSystemFont,\”Segoe UI\”,Roboto,Oxygen-Sans,Ubuntu,Cantarell,\”Helvetica Neue\”,sans-serif”,

“name”: “System Font”,

“slug”: “system-font”

}

]

},

“layout”: {

“contentSize”: “650px”,

“wideSize”: “1000px”

},

“blocks”: {

“core/paragraph”: {

“typography”: {

“dropCap”: false

}

}

}

},

“styles”: {

“blocks”: {

“core/post-title”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–x-large)”

},

“color”: {

“text”: “var(–wp–preset–color–primary)”

}

}

}

}

}

“`

This theme.json sets up some basic color palettes, font sizes, a default system font, and defines the content and wide widths for blocks. It also shows a basic example of applying styles to a specific block (core/post-title). We’ll expand on this a lot, but this is a solid start.

If you’re interested in learning more about building a full site editing (FSE) block theme from scratch, you might find this related article helpful: How to Build a Full Site Editing Block Theme. This resource provides valuable insights and step-by-step guidance that can enhance your understanding of the FSE process, making it easier for you to create a custom theme tailored to your needs.

Building Block Templates and Template Parts

This is the core of what makes an FSE theme work. Instead of traditional PHP template files, you’re using HTML files filled with block markup to define your layouts.

The templates Directory

Create a folder named templates inside your my-fse-theme directory. This is where your main page and post layouts will live.

index.html (The Main Template)

This is your most important template. It’s the default fallback for listing posts and acts as your blog archive.

Create my-fse-theme/templates/index.html:

“`html

  • How to build a full site editing (FSE) block theme from scratch?

    Building a Full Site Editing (FSE) block theme from scratch in WordPress can feel like a tall order, but it’s totally achievable once you get a handle on the core concepts. In essence, you’re creating a theme where nearly every aspect of the site – from headers to footers, posts to pages – is controlled…

“`

This index.html does a few crucial things:

  • It includes a header and footer template part (which we’ll create next).
  • It uses the core/query block to display a list of posts.
  • Inside the core/query block, it uses the core/post-template block to loop through individual posts, displaying the title, featured image, excerpt, and date.
  • It includes the core/query-pagination block.
single.html (Individual Posts)

You’ll want a template for single posts.

Create my-fse-theme/templates/single.html:

“`html

How to build a full site editing (FSE) block theme from scratch?

“`

This template fetches and displays the post’s title, featured image, actual content, date, author, and comments.

page.html (Individual Pages)

For static pages, you’ll need page.html.

Create my-fse-theme/templates/page.html:

“`html

How to build a full site editing (FSE) block theme from scratch?

“`

This is simpler, primarily displaying the page title and content.

The parts Directory

This directory holds reusable sections of your site, like headers and footers. These are your Template Parts.

Create a folder named parts inside my-fse-theme.

header.html

Your site’s main header.

Create my-fse-theme/parts/header.html:

“`html

“`

This header includes a site logo, site title, and a navigation block. The align:full group stretches across the full width, and the flex layout helps arrange the elements.

footer.html

Your site’s footer.

Create my-fse-theme/parts/footer.html:

“`html

proudly powered by WordPress

“`

A simple footer with a centered paragraph. Notice the backgroundColor and padding settings – these are controlled via theme.json variables and direct WordPress block styles.

Expanding theme.json for Comprehensive Styling

Now that we have a basic structure, let’s make our theme.json more powerful. This file is truly the heart of an FSE theme’s design.

Global Settings (settings)

The settings object in theme.json controls global features and defaults that apply across your entire site. This is where you limit or expand user choices in the Site Editor.

Layout and Wrapper Defaults

We already added contentSize and wideSize. These are crucial for defining your default content width.

“`json

“layout”: {

“contentSize”: “650px”,

“wideSize”: “1000px”

},

“`

You can also enable or disable the layout option for specific blocks if needed, usually on a per-block basis.

Typeography and Font Families

You can define custom font families here. If using Google Fonts or similar, you’d enqueue them traditionally via functions.php (though eventually, core might offer better ways).

“`json

“typography”: {

“customFontSize”: true, // Allow users to set custom font sizes

“dropCap”: false, // Disable drop cap by default for all blocks

“fontFamilies”: [

{

“fontFamily”: “Inter, sans-serif”,

“name”: “Inter”,

“slug”: “inter”,

“fontFace”: [

{

“fontFamily”: “Inter”,

“fontWeight”: “400”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Inter-Regular.woff2” ]

},

{

“fontFamily”: “Inter”,

“fontWeight”: “700”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Inter-Bold.woff2” ]

}

]

},

{

“fontFamily”: ” ‘Merriweather’, serif”,

“name”: “Merriweather”,

“slug”: “merriweather”,

“fontFace”: [

{

“fontFamily”: “Merriweather”,

“fontWeight”: “400”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Merriweather-Regular.woff2” ]

},

{

“fontFamily”: “Merriweather”,

“fontWeight”: “700”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Merriweather-Bold.woff2” ]

}

]

}

],

“fontSizes”: [

{

“name”: “Extra Small”,

“size”: “0.75rem”,

“slug”: “x-small”

},

{

“name”: “Small”,

“size”: “0.875rem”,

“slug”: “small”

},

{

“name”: “Base”,

“size”: “1rem”,

“slug”: “base”

},

{

“name”: “Large”,

“size”: “1.125rem”,

“slug”: “large”

},

{

“name”: “Extra Large”,

“size”: “1.5rem”,

“slug”: “x-large”

},

{

“name”: “Huge”,

“size”: “2.25rem”,

“slug”: “huge”

},

{

“name”: “Gigantic”,

“size”: “3rem”,

“slug”: “gigantic”

}

],

“lineHeights”: true

}

“`

Here, we’ve defined two custom font families (Inter and Merriweather) and enabled fontFace to load local font files. Remember to create an assets/fonts directory and place your .woff2 files there. You can also define custom lineHeights.

Color Palettes and Duotone Filters

This section defines the colors available in the editor.

“`json

“color”: {

“palette”: [

{

“slug”: “primary”,

“color”: “#007cba”,

“name”: “Primary”

},

{

“slug”: “secondary”,

“color”: “#e0e0e0”,

“name”: “Secondary”

},

{

“slug”: “dark”,

“color”: “#202020”,

“name”: “Dark”

},

{

“slug”: “light”,

“color”: “#ffffff”,

“name”: “Light”

},

{

“slug”: “accent”,

“color”: “#ff6600”,

“name”: “Accent”

}

],

“gradients”: [

{

“name”: “Vivid Cyan Blue to Vivid Purple”,

“gradient”: “linear-gradient(135deg,#00d084 0%,#8224e3 100%)”,

“slug”: “vivid-cyan-blue-to-vivid-purple”

}

],

“duotone”: [

{

“colors”: [ “#191919”, “#ffffff” ],

“slug”: “dark-light”,

“name”: “Dark & Light”

},

{

“colors”: [ “#007cba”, “#ffffff” ],

“slug”: “primary-light”,

“name”: “Primary & Light”

}

],

“defaultPalette”: false,

“defaultGradients”: false,

“defaultDuotone”: false

},

“`

Here, we’ve expanded the color palette, added gradients, and introduced duotone filters for image blocks. Setting defaultPalette, defaultGradients, and defaultDuotone to false removes WordPress’s default options, leaving only yours.

Spacing and Borders

Control padding, margin, block gap, and border options.

“`json

“spacing”: {

“blockGap”: {

“top”: “1.5rem”,

“left”: “1.5rem”

},

“padding”: true, // Enable padding controls for all blocks that support it

“margin”: true, // Enable margin controls

“units”: [“px”, “%”, “em”, “rem”, “vh”, “vw”], // Define available units

“spacingSizes”: [

{

“name”: “None”,

“size”: “0”,

“slug”: “0”

},

{

“name”: “Small”,

“size”: “0.5rem”,

“slug”: “small”

},

{

“name”: “Medium”,

“size”: “1rem”,

“slug”: “medium”

},

{

“name”: “Large”,

“size”: “2rem”,

“slug”: “large”

},

{

“name”: “X-Large”,

“size”: “4rem”,

“slug”: “x-large”

}

]

},

“border”: {

“color”: true,

“radius”: true,

“style”: true,

“width”: true

}

“`

blockGap sets the default spacing between blocks within layouts. spacingSizes are custom presets for padding and margin.

Global Styles (styles)

The styles object in theme.json applies default styles to elements and blocks without needing to target them with CSS directly.

Element Styles

You can style core HTML elements like body, h1, a, etc.

“`json

“styles”: {

“color”: {

“text”: “var(–wp–preset–color–dark)”,

“background”: “var(–wp–preset–color–light)”

},

“typography”: {

“fontFamily”: “var(–wp–preset–font-family–inter)”,

“fontSize”: “var(–wp–preset–font-size–base)”,

“lineHeight”: “1.6”

},

“elements”: {

“link”: {

“color”: {

“text”: “var(–wp–preset–color–primary)”

},

“:hover”: {

“color”: {

“text”: “var(–wp–preset–color–accent)”

}

}

},

“h1”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–gigantic)”,

“lineHeight”: “1.1”

}

},

“h2”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–huge)”,

“lineHeight”: “1.2”

}

}

},

// … rest of the styles

}

“`

Here, we set default text color for the body, general font, and specific styles for links and heading levels. Notice the use of var(--wp--preset--...) to reference your defined presets.

Block Styles

You can also apply default styles to specific blocks. This is where you override core block styles or provide sensible defaults.

“`json

“styles”: {

// …

“blocks”: {

“core/site-title”: {

“color”: {

“text”: “var(–wp–preset–color–primary)”

},

“typography”: {

“fontFamily”: “var(–wp–preset–font-family–merriweather)”,

“fontSize”: “var(–wp–preset–font-size–x-large)”,

“fontWeight”: “700”

}

},

“core/navigation”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–small)”,

“textTransform”: “uppercase”

},

“spacing”: {

“blockGap”: “1.5rem”

}

},

“core/button”: {

“color”: {

“background”: “var(–wp–preset–color–primary)”,

“text”: “var(–wp–preset–color–light)”

},

“border”: {

“radius”: “4px”

},

“typography”: {

“fontWeight”: “bold”

},

“:hover”: {

“color”: {

“background”: “var(–wp–preset–color–accent)”

}

}

},

“core/paragraph”: {

“spacing”: {

“margin”: {

“bottom”: “1.5rem”

}

}

}

}

}

“`

This is a snippet, but you can see how you’d target blocks like core/site-title, core/navigation, core/button, and core/paragraph to apply default styles.

Setting Up a functions.php (When You Still Need PHP)

While FSE themes rely heavily on HTML templates and theme.json, functions.php isn’t entirely obsolete. It’s still useful for things like:

  • Enqueuing custom scripts or stylesheets that aren’t handled by theme.json (rare, but possible).
  • Registering custom block patterns and pattern categories.
  • Adding theme support for features not covered by theme.json (e.g., specific image sizes if you’re using classic image functions).
  • Custom post types and taxonomies.
  • API integrations or backend logic.

Basic functions.php for FSE

Create my-fse-theme/functions.php.

“`php

if ( ! function_exists( ‘my_fse_theme_setup’ ) ) :

/**

  • Sets up theme defaults and registers support for various WordPress features.

*

  • Note that this function is hooked into the after_setup_theme hook, which
  • runs before the init hook. The init hook is too late for some features, such
  • as indicating support for post thumbnails.

*/

function my_fse_theme_setup() {

// Add support for block styles.

add_theme_support( ‘wp-block-styles’ );

// Enqueue editor styles.

add_editor_style( ‘style.css’ ); // Your main stylesheet (or separate editor styles)

// Remove the core default block patterns.

remove_theme_support( ‘core-block-patterns’ );

// Register block pattern categories.

register_block_pattern_category(

‘my-fse-theme’,

array( ‘label’ => esc_html__( ‘My FSE Theme Patterns’, ‘my-fse-theme’ ) )

);

}

endif;

add_action( ‘after_setup_theme’, ‘my_fse_theme_setup’ );

/**

  • Enqueue scripts and styles.

*/

function my_fse_theme_scripts() {

wp_enqueue_style( ‘my-fse-theme-style’, get_stylesheet_uri(), array(), wp_get_theme()->get( ‘Version’ ) );

// If you want to enqueue a separate stylesheet specifically for the editor.

// wp_enqueue_style( ‘my-fse-theme-editor-style’, get_theme_file_uri( ‘/assets/css/editor-style.css’ ), array(), wp_get_theme()->get( ‘Version’ ) );

}

add_action( ‘wp_enqueue_scripts’, ‘my_fse_theme_scripts’ );

// Load custom block patterns.

require_once get_theme_file_path( ‘inc/block-patterns.php’ );

“`

Let’s break down some key parts:

  • add_theme_support( 'wp-block-styles' );: Ensures that block styles (from theme.json and block editor CSS) are loaded on the frontend.
  • add_editor_style( 'style.css' );: Loads your theme’s main style.css (or a dedicated editor stylesheet) into the block editor for consistency.
  • remove_theme_support( 'core-block-patterns' );: If you want to use only your own block patterns, this removes WordPress’s default ones.
  • register_block_pattern_category(): This creates a new category for your custom block patterns, making them easier to find in the editor.
  • wp_enqueue_style( 'my-fse-theme-style', get_stylesheet_uri() ... );: This loads your theme’s main style.css on the frontend. Even though theme.json styles are loaded automatically, you might still put custom CSS in style.css.
  • require_once get_theme_file_path( 'inc/block-patterns.php' );: This is a good way to organize your block patterns, pulling them in from a separate file.
If you’re interested in enhancing your website’s performance after building a full site editing (FSE) block theme from scratch, you might want to check out a related article on optimizing your site’s speed. Understanding how to improve loading times can significantly impact user experience and SEO rankings. For more insights on this topic, you can read about it in this helpful guide that covers various strategies to boost your site’s efficiency.

Crafting Custom Block Patterns

Block patterns are pre-designed block layouts that users can insert with a single click. They’re a huge time-saver and provide a consistent design language.

The inc Directory

Create an inc folder inside my-fse-theme.

block-patterns.php

In my-fse-theme/inc/block-patterns.php:

“`php

/**

  • My FSE Theme: Block Patterns

*

  • @package MyFSETheme

*/

/**

  • Register Block Pattern.

*/

function my_fse_theme_register_block_patterns() {

// Hero Section Pattern.

register_block_pattern(

‘my-fse-theme/hero-section’,

array(

‘title’ => esc_html__( ‘Hero Section with Title and Button’, ‘my-fse-theme’ ),

‘description’ => esc_html__( ‘A simple hero section with a heading, paragraph, and button.’, ‘my-fse-theme’ ),

‘categories’ => array( ‘my-fse-theme’ ), // Your custom category

‘content’ => ‘

Welcome to My FSE Theme

This is an example of a custom block pattern. Customize it freely!

‘,

)

);

// Another pattern example: Two Column Feature

register_block_pattern(

‘my-fse-theme/two-column-feature’,

array(

‘title’ => esc_html__( ‘Two Column Feature with Image and Text’, ‘my-fse-theme’ ),

‘description’ => esc_html__( ‘A two-column layout showing an image on one side and text on the other.’, ‘my-fse-theme’ ),

‘categories’ => array( ‘my-fse-theme’ ),

‘content’ => ‘

Highlight Your Features

Describe your product or service right here. Use engaging language to captivate your audience and explain the benefits.

  • Benefit 1: Point out a key advantage.
  • Benefit 2: Detail another great feature.
  • Benefit 3: Emphasize what makes you unique.

‘,

)

);

}

add_action( ‘init’, ‘my_fse_theme_register_block_patterns’ );

“`

Getting the HTML markup for patterns is easy: build your desired layout in the WordPress editor, switch to “Code editor” view, copy the HTML, and paste it into the content property of your register_block_pattern function. Make sure to escape single quotes ' or use double quotes " judiciously.

Enhancing Core Support and Internationalization

screenshot.png

Create a screenshot.png file (1200x900px recommended) at the root of your theme folder. This image will represent your theme in the “Appearance > Themes” section of WordPress.

readme.txt

A readme.txt file is good practice for describing your theme, its features, installation, and any special instructions.

Internationalization (i18n)

For any user-facing text strings in your functions.php or theme.json (like names of patterns or colors), you’ll want to make them translatable.

In functions.php you’ve already seen esc_html__( 'My FSE Theme Patterns', 'my-fse-theme' ). The 'my-fse-theme' is your text domain. You’ll need to run a tool like WP-CLI (wp i18n make-pot .) to generate a .pot file, which can then be used to create .mo and .po translation files.

For theme.json, the name properties of colors, fonts, or font sizes cannot directly use PHP translation functions. However, WordPress handles these strings for you under the hood if you provide a textdomain in your theme.json:

“`json

{

“version”: 2,

“textdomain”: “my-fse-theme”,

“settings”: {

“color”: {

“palette”: [

{

“slug”: “primary”,

“color”: “#007cba”,

“name”: “Primary” // This string will be translatable

},

// …

]

}

}

}

“`

With the textdomain defined, WordPress will automatically make the name properties translatable in the Site Editor.

Activation and Testing

Once you’ve set up these files:

  1. Upload to WordPress: Place your my-fse-theme folder into wp-content/themes.
  2. Activate: Go to “Appearance > Themes” in your WordPress admin. You should see “My FSE Theme”. Click “Activate”.
  3. Explore the Site Editor: Navigate to “Appearance > Editor”. This is where you can explore the templates (index.html, single.html, page.html), template parts (header.html, footer.html), and global styles (theme.json) visually. Make changes and see them update live.
  4. Test Pages and Posts: Create a few pages and posts to ensure your single.html and page.html templates are rendering correctly, as well as your blog archive (index.html).
  5. Check Block Patterns: When adding a new block to a page/post, click “Patterns” and look for your custom category (e.g., “My FSE Theme Patterns”).

Building a full site editing theme from scratch is a journey, and this guide sets a strong foundation. The key is understanding how theme.json interacts with the block editor, and how HTML files filled with block markup replace traditional PHP templates. With these tools, you have immense power to define a modern, block-first WordPress experience.

“`

This template fetches and displays the post’s title, featured image, actual content, date, author, and comments.

page.html (Individual Pages)

For static pages, you’ll need page.html.

Create my-fse-theme/templates/page.html:

“`html

How to build a full site editing (FSE) block theme from scratch?

Building a Full Site Editing (FSE) block theme from scratch in WordPress can feel like a tall order, but it’s totally achievable once you get a handle on the core concepts. In essence, you’re creating a theme where nearly every aspect of the site – from headers to footers, posts to pages – is controlled through blocks and the WordPress Site Editor. This means less hand-coding and more visual design, giving users unprecedented control over their site’s layout and content directly within the admin.

Getting Started: The Basic Structure

Let’s dive into the absolute bare minimum you need to get an FSE theme up and running. Think of this as your skeleton; we’ll add the meat and muscles later.

Your Theme Folder

First things first, create a new folder inside your wp-content/themes directory. Let’s call it my-fse-theme for now. This will house all your theme’s files.

The style.css File

Every WordPress theme needs a style.css file at its root. Even if you’re not putting all your styles in here, it’s how WordPress identifies your theme.

In my-fse-theme/style.css, add the following:

“`css

/*

Theme Name: My FSE Theme

Theme URI: https://example.com/my-fse-theme

Author: Your Name

Author URI: https://example.com

Description: A simple Full Site Editing block theme.

Version: 1.0.0

Requires at least: 5.9

Requires PHP: 7.4

Text Domain: my-fse-theme

License: GNU General Public License v2 or later

License URI: http://www.gnu.org/licenses/gpl-2.0.html

*/

“`

Fill in your own details here. The Requires at least: 5.9 is crucial as FSE was officially introduced in WordPress 5.9.

The index.php File

Tradition dictates an index.php file, even for FSE themes. In its simplest form, it acts as a fallback. For FSE, it often just loads the block template parts.

In my-fse-theme/index.php, just put:

“`php

// Nothing to see here.

// This file is just a fallback for block themes.

// All template handling is done via the block editor.

“`

Technically, for an FSE theme, index.php isn’t strictly required to render content directly if you have an index.html file in your templates directory, as the block editor takes precedence. However, including it is good practice for compatibility and debugging.

The theme.json File

This is where the magic really starts for FSE themes. theme.json is your central control panel for default styles, block settings, and various design features. It defines everything from typography and color palettes to spacing and layout controls, all accessible within the Site Editor.

In my-fse-theme/theme.json, start with a basic structure:

“`json

{

“version”: 2,

“settings”: {

“color”: {

“palette”: [

{

“slug”: “primary”,

“color”: “#007cba”,

“name”: “Primary”

},

{

“slug”: “secondary”,

“color”: “#f2f2f2”,

“name”: “Secondary”

},

{

“slug”: “dark”,

“color”: “#333333”,

“name”: “Dark”

},

{

“slug”: “light”,

“color”: “#ffffff”,

“name”: “Light”

}

]

},

“typography”: {

“fontSizes”: [

{

“slug”: “small”,

“size”: “0.875rem”,

“name”: “Small”

},

{

“slug”: “normal”,

“size”: “1rem”,

“name”: “Normal”

},

{

“slug”: “large”,

“size”: “1.25rem”,

“name”: “Large”

},

{

“slug”: “x-large”,

“size”: “1.75rem”,

“name”: “Extra Large”

}

],

“fontFamilies”: [

{

“fontFamily”: “-apple-system,BlinkMacSystemFont,\”Segoe UI\”,Roboto,Oxygen-Sans,Ubuntu,Cantarell,\”Helvetica Neue\”,sans-serif”,

“name”: “System Font”,

“slug”: “system-font”

}

]

},

“layout”: {

“contentSize”: “650px”,

“wideSize”: “1000px”

},

“blocks”: {

“core/paragraph”: {

“typography”: {

“dropCap”: false

}

}

}

},

“styles”: {

“blocks”: {

“core/post-title”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–x-large)”

},

“color”: {

“text”: “var(–wp–preset–color–primary)”

}

}

}

}

}

“`

This theme.json sets up some basic color palettes, font sizes, a default system font, and defines the content and wide widths for blocks. It also shows a basic example of applying styles to a specific block (core/post-title). We’ll expand on this a lot, but this is a solid start.

If you’re interested in learning more about building a full site editing (FSE) block theme from scratch, you might find this related article helpful: How to Build a Full Site Editing Block Theme. This resource provides valuable insights and step-by-step guidance that can enhance your understanding of the FSE process, making it easier for you to create a custom theme tailored to your needs.

Building Block Templates and Template Parts

This is the core of what makes an FSE theme work. Instead of traditional PHP template files, you’re using HTML files filled with block markup to define your layouts.

The templates Directory

Create a folder named templates inside your my-fse-theme directory. This is where your main page and post layouts will live.

index.html (The Main Template)

This is your most important template. It’s the default fallback for listing posts and acts as your blog archive.

Create my-fse-theme/templates/index.html:

“`html

  • How to build a full site editing (FSE) block theme from scratch?

    Building a Full Site Editing (FSE) block theme from scratch in WordPress can feel like a tall order, but it’s totally achievable once you get a handle on the core concepts. In essence, you’re creating a theme where nearly every aspect of the site – from headers to footers, posts to pages – is controlled…

“`

This index.html does a few crucial things:

  • It includes a header and footer template part (which we’ll create next).
  • It uses the core/query block to display a list of posts.
  • Inside the core/query block, it uses the core/post-template block to loop through individual posts, displaying the title, featured image, excerpt, and date.
  • It includes the core/query-pagination block.
single.html (Individual Posts)

You’ll want a template for single posts.

Create my-fse-theme/templates/single.html:

“`html

How to build a full site editing (FSE) block theme from scratch?

“`

This template fetches and displays the post’s title, featured image, actual content, date, author, and comments.

page.html (Individual Pages)

For static pages, you’ll need page.html.

Create my-fse-theme/templates/page.html:

“`html

How to build a full site editing (FSE) block theme from scratch?

“`

This is simpler, primarily displaying the page title and content.

The parts Directory

This directory holds reusable sections of your site, like headers and footers. These are your Template Parts.

Create a folder named parts inside my-fse-theme.

header.html

Your site’s main header.

Create my-fse-theme/parts/header.html:

“`html

“`

This header includes a site logo, site title, and a navigation block. The align:full group stretches across the full width, and the flex layout helps arrange the elements.

footer.html

Your site’s footer.

Create my-fse-theme/parts/footer.html:

“`html

proudly powered by WordPress

“`

A simple footer with a centered paragraph. Notice the backgroundColor and padding settings – these are controlled via theme.json variables and direct WordPress block styles.

Expanding theme.json for Comprehensive Styling

Now that we have a basic structure, let’s make our theme.json more powerful. This file is truly the heart of an FSE theme’s design.

Global Settings (settings)

The settings object in theme.json controls global features and defaults that apply across your entire site. This is where you limit or expand user choices in the Site Editor.

Layout and Wrapper Defaults

We already added contentSize and wideSize. These are crucial for defining your default content width.

“`json

“layout”: {

“contentSize”: “650px”,

“wideSize”: “1000px”

},

“`

You can also enable or disable the layout option for specific blocks if needed, usually on a per-block basis.

Typeography and Font Families

You can define custom font families here. If using Google Fonts or similar, you’d enqueue them traditionally via functions.php (though eventually, core might offer better ways).

“`json

“typography”: {

“customFontSize”: true, // Allow users to set custom font sizes

“dropCap”: false, // Disable drop cap by default for all blocks

“fontFamilies”: [

{

“fontFamily”: “Inter, sans-serif”,

“name”: “Inter”,

“slug”: “inter”,

“fontFace”: [

{

“fontFamily”: “Inter”,

“fontWeight”: “400”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Inter-Regular.woff2” ]

},

{

“fontFamily”: “Inter”,

“fontWeight”: “700”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Inter-Bold.woff2” ]

}

]

},

{

“fontFamily”: ” ‘Merriweather’, serif”,

“name”: “Merriweather”,

“slug”: “merriweather”,

“fontFace”: [

{

“fontFamily”: “Merriweather”,

“fontWeight”: “400”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Merriweather-Regular.woff2” ]

},

{

“fontFamily”: “Merriweather”,

“fontWeight”: “700”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Merriweather-Bold.woff2” ]

}

]

}

],

“fontSizes”: [

{

“name”: “Extra Small”,

“size”: “0.75rem”,

“slug”: “x-small”

},

{

“name”: “Small”,

“size”: “0.875rem”,

“slug”: “small”

},

{

“name”: “Base”,

“size”: “1rem”,

“slug”: “base”

},

{

“name”: “Large”,

“size”: “1.125rem”,

“slug”: “large”

},

{

“name”: “Extra Large”,

“size”: “1.5rem”,

“slug”: “x-large”

},

{

“name”: “Huge”,

“size”: “2.25rem”,

“slug”: “huge”

},

{

“name”: “Gigantic”,

“size”: “3rem”,

“slug”: “gigantic”

}

],

“lineHeights”: true

}

“`

Here, we’ve defined two custom font families (Inter and Merriweather) and enabled fontFace to load local font files. Remember to create an assets/fonts directory and place your .woff2 files there. You can also define custom lineHeights.

Color Palettes and Duotone Filters

This section defines the colors available in the editor.

“`json

“color”: {

“palette”: [

{

“slug”: “primary”,

“color”: “#007cba”,

“name”: “Primary”

},

{

“slug”: “secondary”,

“color”: “#e0e0e0”,

“name”: “Secondary”

},

{

“slug”: “dark”,

“color”: “#202020”,

“name”: “Dark”

},

{

“slug”: “light”,

“color”: “#ffffff”,

“name”: “Light”

},

{

“slug”: “accent”,

“color”: “#ff6600”,

“name”: “Accent”

}

],

“gradients”: [

{

“name”: “Vivid Cyan Blue to Vivid Purple”,

“gradient”: “linear-gradient(135deg,#00d084 0%,#8224e3 100%)”,

“slug”: “vivid-cyan-blue-to-vivid-purple”

}

],

“duotone”: [

{

“colors”: [ “#191919”, “#ffffff” ],

“slug”: “dark-light”,

“name”: “Dark & Light”

},

{

“colors”: [ “#007cba”, “#ffffff” ],

“slug”: “primary-light”,

“name”: “Primary & Light”

}

],

“defaultPalette”: false,

“defaultGradients”: false,

“defaultDuotone”: false

},

“`

Here, we’ve expanded the color palette, added gradients, and introduced duotone filters for image blocks. Setting defaultPalette, defaultGradients, and defaultDuotone to false removes WordPress’s default options, leaving only yours.

Spacing and Borders

Control padding, margin, block gap, and border options.

“`json

“spacing”: {

“blockGap”: {

“top”: “1.5rem”,

“left”: “1.5rem”

},

“padding”: true, // Enable padding controls for all blocks that support it

“margin”: true, // Enable margin controls

“units”: [“px”, “%”, “em”, “rem”, “vh”, “vw”], // Define available units

“spacingSizes”: [

{

“name”: “None”,

“size”: “0”,

“slug”: “0”

},

{

“name”: “Small”,

“size”: “0.5rem”,

“slug”: “small”

},

{

“name”: “Medium”,

“size”: “1rem”,

“slug”: “medium”

},

{

“name”: “Large”,

“size”: “2rem”,

“slug”: “large”

},

{

“name”: “X-Large”,

“size”: “4rem”,

“slug”: “x-large”

}

]

},

“border”: {

“color”: true,

“radius”: true,

“style”: true,

“width”: true

}

“`

blockGap sets the default spacing between blocks within layouts. spacingSizes are custom presets for padding and margin.

Global Styles (styles)

The styles object in theme.json applies default styles to elements and blocks without needing to target them with CSS directly.

Element Styles

You can style core HTML elements like body, h1, a, etc.

“`json

“styles”: {

“color”: {

“text”: “var(–wp–preset–color–dark)”,

“background”: “var(–wp–preset–color–light)”

},

“typography”: {

“fontFamily”: “var(–wp–preset–font-family–inter)”,

“fontSize”: “var(–wp–preset–font-size–base)”,

“lineHeight”: “1.6”

},

“elements”: {

“link”: {

“color”: {

“text”: “var(–wp–preset–color–primary)”

},

“:hover”: {

“color”: {

“text”: “var(–wp–preset–color–accent)”

}

}

},

“h1”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–gigantic)”,

“lineHeight”: “1.1”

}

},

“h2”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–huge)”,

“lineHeight”: “1.2”

}

}

},

// … rest of the styles

}

“`

Here, we set default text color for the body, general font, and specific styles for links and heading levels. Notice the use of var(--wp--preset--...) to reference your defined presets.

Block Styles

You can also apply default styles to specific blocks. This is where you override core block styles or provide sensible defaults.

“`json

“styles”: {

// …

“blocks”: {

“core/site-title”: {

“color”: {

“text”: “var(–wp–preset–color–primary)”

},

“typography”: {

“fontFamily”: “var(–wp–preset–font-family–merriweather)”,

“fontSize”: “var(–wp–preset–font-size–x-large)”,

“fontWeight”: “700”

}

},

“core/navigation”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–small)”,

“textTransform”: “uppercase”

},

“spacing”: {

“blockGap”: “1.5rem”

}

},

“core/button”: {

“color”: {

“background”: “var(–wp–preset–color–primary)”,

“text”: “var(–wp–preset–color–light)”

},

“border”: {

“radius”: “4px”

},

“typography”: {

“fontWeight”: “bold”

},

“:hover”: {

“color”: {

“background”: “var(–wp–preset–color–accent)”

}

}

},

“core/paragraph”: {

“spacing”: {

“margin”: {

“bottom”: “1.5rem”

}

}

}

}

}

“`

This is a snippet, but you can see how you’d target blocks like core/site-title, core/navigation, core/button, and core/paragraph to apply default styles.

Setting Up a functions.php (When You Still Need PHP)

While FSE themes rely heavily on HTML templates and theme.json, functions.php isn’t entirely obsolete. It’s still useful for things like:

  • Enqueuing custom scripts or stylesheets that aren’t handled by theme.json (rare, but possible).
  • Registering custom block patterns and pattern categories.
  • Adding theme support for features not covered by theme.json (e.g., specific image sizes if you’re using classic image functions).
  • Custom post types and taxonomies.
  • API integrations or backend logic.

Basic functions.php for FSE

Create my-fse-theme/functions.php.

“`php

if ( ! function_exists( ‘my_fse_theme_setup’ ) ) :

/**

  • Sets up theme defaults and registers support for various WordPress features.

*

  • Note that this function is hooked into the after_setup_theme hook, which
  • runs before the init hook. The init hook is too late for some features, such
  • as indicating support for post thumbnails.

*/

function my_fse_theme_setup() {

// Add support for block styles.

add_theme_support( ‘wp-block-styles’ );

// Enqueue editor styles.

add_editor_style( ‘style.css’ ); // Your main stylesheet (or separate editor styles)

// Remove the core default block patterns.

remove_theme_support( ‘core-block-patterns’ );

// Register block pattern categories.

register_block_pattern_category(

‘my-fse-theme’,

array( ‘label’ => esc_html__( ‘My FSE Theme Patterns’, ‘my-fse-theme’ ) )

);

}

endif;

add_action( ‘after_setup_theme’, ‘my_fse_theme_setup’ );

/**

  • Enqueue scripts and styles.

*/

function my_fse_theme_scripts() {

wp_enqueue_style( ‘my-fse-theme-style’, get_stylesheet_uri(), array(), wp_get_theme()->get( ‘Version’ ) );

// If you want to enqueue a separate stylesheet specifically for the editor.

// wp_enqueue_style( ‘my-fse-theme-editor-style’, get_theme_file_uri( ‘/assets/css/editor-style.css’ ), array(), wp_get_theme()->get( ‘Version’ ) );

}

add_action( ‘wp_enqueue_scripts’, ‘my_fse_theme_scripts’ );

// Load custom block patterns.

require_once get_theme_file_path( ‘inc/block-patterns.php’ );

“`

Let’s break down some key parts:

  • add_theme_support( 'wp-block-styles' );: Ensures that block styles (from theme.json and block editor CSS) are loaded on the frontend.
  • add_editor_style( 'style.css' );: Loads your theme’s main style.css (or a dedicated editor stylesheet) into the block editor for consistency.
  • remove_theme_support( 'core-block-patterns' );: If you want to use only your own block patterns, this removes WordPress’s default ones.
  • register_block_pattern_category(): This creates a new category for your custom block patterns, making them easier to find in the editor.
  • wp_enqueue_style( 'my-fse-theme-style', get_stylesheet_uri() ... );: This loads your theme’s main style.css on the frontend. Even though theme.json styles are loaded automatically, you might still put custom CSS in style.css.
  • require_once get_theme_file_path( 'inc/block-patterns.php' );: This is a good way to organize your block patterns, pulling them in from a separate file.
If you’re interested in enhancing your website’s performance after building a full site editing (FSE) block theme from scratch, you might want to check out a related article on optimizing your site’s speed. Understanding how to improve loading times can significantly impact user experience and SEO rankings. For more insights on this topic, you can read about it in this helpful guide that covers various strategies to boost your site’s efficiency.

Crafting Custom Block Patterns

Block patterns are pre-designed block layouts that users can insert with a single click. They’re a huge time-saver and provide a consistent design language.

The inc Directory

Create an inc folder inside my-fse-theme.

block-patterns.php

In my-fse-theme/inc/block-patterns.php:

“`php

/**

  • My FSE Theme: Block Patterns

*

  • @package MyFSETheme

*/

/**

  • Register Block Pattern.

*/

function my_fse_theme_register_block_patterns() {

// Hero Section Pattern.

register_block_pattern(

‘my-fse-theme/hero-section’,

array(

‘title’ => esc_html__( ‘Hero Section with Title and Button’, ‘my-fse-theme’ ),

‘description’ => esc_html__( ‘A simple hero section with a heading, paragraph, and button.’, ‘my-fse-theme’ ),

‘categories’ => array( ‘my-fse-theme’ ), // Your custom category

‘content’ => ‘

Welcome to My FSE Theme

This is an example of a custom block pattern. Customize it freely!

‘,

)

);

// Another pattern example: Two Column Feature

register_block_pattern(

‘my-fse-theme/two-column-feature’,

array(

‘title’ => esc_html__( ‘Two Column Feature with Image and Text’, ‘my-fse-theme’ ),

‘description’ => esc_html__( ‘A two-column layout showing an image on one side and text on the other.’, ‘my-fse-theme’ ),

‘categories’ => array( ‘my-fse-theme’ ),

‘content’ => ‘

Highlight Your Features

Describe your product or service right here. Use engaging language to captivate your audience and explain the benefits.

  • Benefit 1: Point out a key advantage.
  • Benefit 2: Detail another great feature.
  • Benefit 3: Emphasize what makes you unique.

‘,

)

);

}

add_action( ‘init’, ‘my_fse_theme_register_block_patterns’ );

“`

Getting the HTML markup for patterns is easy: build your desired layout in the WordPress editor, switch to “Code editor” view, copy the HTML, and paste it into the content property of your register_block_pattern function. Make sure to escape single quotes ' or use double quotes " judiciously.

Enhancing Core Support and Internationalization

screenshot.png

Create a screenshot.png file (1200x900px recommended) at the root of your theme folder. This image will represent your theme in the “Appearance > Themes” section of WordPress.

readme.txt

A readme.txt file is good practice for describing your theme, its features, installation, and any special instructions.

Internationalization (i18n)

For any user-facing text strings in your functions.php or theme.json (like names of patterns or colors), you’ll want to make them translatable.

In functions.php you’ve already seen esc_html__( 'My FSE Theme Patterns', 'my-fse-theme' ). The 'my-fse-theme' is your text domain. You’ll need to run a tool like WP-CLI (wp i18n make-pot .) to generate a .pot file, which can then be used to create .mo and .po translation files.

For theme.json, the name properties of colors, fonts, or font sizes cannot directly use PHP translation functions. However, WordPress handles these strings for you under the hood if you provide a textdomain in your theme.json:

“`json

{

“version”: 2,

“textdomain”: “my-fse-theme”,

“settings”: {

“color”: {

“palette”: [

{

“slug”: “primary”,

“color”: “#007cba”,

“name”: “Primary” // This string will be translatable

},

// …

]

}

}

}

“`

With the textdomain defined, WordPress will automatically make the name properties translatable in the Site Editor.

Activation and Testing

Once you’ve set up these files:

  1. Upload to WordPress: Place your my-fse-theme folder into wp-content/themes.
  2. Activate: Go to “Appearance > Themes” in your WordPress admin. You should see “My FSE Theme”. Click “Activate”.
  3. Explore the Site Editor: Navigate to “Appearance > Editor”. This is where you can explore the templates (index.html, single.html, page.html), template parts (header.html, footer.html), and global styles (theme.json) visually. Make changes and see them update live.
  4. Test Pages and Posts: Create a few pages and posts to ensure your single.html and page.html templates are rendering correctly, as well as your blog archive (index.html).
  5. Check Block Patterns: When adding a new block to a page/post, click “Patterns” and look for your custom category (e.g., “My FSE Theme Patterns”).

Building a full site editing theme from scratch is a journey, and this guide sets a strong foundation. The key is understanding how theme.json interacts with the block editor, and how HTML files filled with block markup replace traditional PHP templates. With these tools, you have immense power to define a modern, block-first WordPress experience.

“`

This is simpler, primarily displaying the page title and content.

The parts Directory

This directory holds reusable sections of your site, like headers and footers. These are your Template Parts.

Create a folder named parts inside my-fse-theme.

header.html

Your site’s main header.

Create my-fse-theme/parts/header.html:

“`html

“`

This header includes a site logo, site title, and a navigation block. The align:full group stretches across the full width, and the flex layout helps arrange the elements.

footer.html

Your site’s footer.

Create my-fse-theme/parts/footer.html:

“`html

proudly powered by WordPress

“`

A simple footer with a centered paragraph. Notice the backgroundColor and padding settings – these are controlled via theme.json variables and direct WordPress block styles.

Expanding theme.json for Comprehensive Styling

Now that we have a basic structure, let’s make our theme.json more powerful. This file is truly the heart of an FSE theme’s design.

Global Settings (settings)

The settings object in theme.json controls global features and defaults that apply across your entire site. This is where you limit or expand user choices in the Site Editor.

Layout and Wrapper Defaults

We already added contentSize and wideSize. These are crucial for defining your default content width.

“`json

“layout”: {

“contentSize”: “650px”,

“wideSize”: “1000px”

},

“`

You can also enable or disable the layout option for specific blocks if needed, usually on a per-block basis.

Typeography and Font Families

You can define custom font families here. If using Google Fonts or similar, you’d enqueue them traditionally via functions.php (though eventually, core might offer better ways).

“`json

“typography”: {

“customFontSize”: true, // Allow users to set custom font sizes

“dropCap”: false, // Disable drop cap by default for all blocks

“fontFamilies”: [

{

“fontFamily”: “Inter, sans-serif”,

“name”: “Inter”,

“slug”: “inter”,

“fontFace”: [

{

“fontFamily”: “Inter”,

“fontWeight”: “400”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Inter-Regular.woff2” ]

},

{

“fontFamily”: “Inter”,

“fontWeight”: “700”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Inter-Bold.woff2” ]

}

]

},

{

“fontFamily”: ” ‘Merriweather’, serif”,

“name”: “Merriweather”,

“slug”: “merriweather”,

“fontFace”: [

{

“fontFamily”: “Merriweather”,

“fontWeight”: “400”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Merriweather-Regular.woff2” ]

},

{

“fontFamily”: “Merriweather”,

“fontWeight”: “700”,

“fontStyle”: “normal”,

“src”: [ “file:./assets/fonts/Merriweather-Bold.woff2” ]

}

]

}

],

“fontSizes”: [

{

“name”: “Extra Small”,

“size”: “0.75rem”,

“slug”: “x-small”

},

{

“name”: “Small”,

“size”: “0.875rem”,

“slug”: “small”

},

{

“name”: “Base”,

“size”: “1rem”,

“slug”: “base”

},

{

“name”: “Large”,

“size”: “1.125rem”,

“slug”: “large”

},

{

“name”: “Extra Large”,

“size”: “1.5rem”,

“slug”: “x-large”

},

{

“name”: “Huge”,

“size”: “2.25rem”,

“slug”: “huge”

},

{

“name”: “Gigantic”,

“size”: “3rem”,

“slug”: “gigantic”

}

],

“lineHeights”: true

}

“`

Here, we’ve defined two custom font families (Inter and Merriweather) and enabled fontFace to load local font files. Remember to create an assets/fonts directory and place your .woff2 files there. You can also define custom lineHeights.

Color Palettes and Duotone Filters

This section defines the colors available in the editor.

“`json

“color”: {

“palette”: [

{

“slug”: “primary”,

“color”: “#007cba”,

“name”: “Primary”

},

{

“slug”: “secondary”,

“color”: “#e0e0e0”,

“name”: “Secondary”

},

{

“slug”: “dark”,

“color”: “#202020”,

“name”: “Dark”

},

{

“slug”: “light”,

“color”: “#ffffff”,

“name”: “Light”

},

{

“slug”: “accent”,

“color”: “#ff6600”,

“name”: “Accent”

}

],

“gradients”: [

{

“name”: “Vivid Cyan Blue to Vivid Purple”,

“gradient”: “linear-gradient(135deg,#00d084 0%,#8224e3 100%)”,

“slug”: “vivid-cyan-blue-to-vivid-purple”

}

],

“duotone”: [

{

“colors”: [ “#191919”, “#ffffff” ],

“slug”: “dark-light”,

“name”: “Dark & Light”

},

{

“colors”: [ “#007cba”, “#ffffff” ],

“slug”: “primary-light”,

“name”: “Primary & Light”

}

],

“defaultPalette”: false,

“defaultGradients”: false,

“defaultDuotone”: false

},

“`

Here, we’ve expanded the color palette, added gradients, and introduced duotone filters for image blocks. Setting defaultPalette, defaultGradients, and defaultDuotone to false removes WordPress’s default options, leaving only yours.

Spacing and Borders

Control padding, margin, block gap, and border options.

“`json

“spacing”: {

“blockGap”: {

“top”: “1.5rem”,

“left”: “1.5rem”

},

“padding”: true, // Enable padding controls for all blocks that support it

“margin”: true, // Enable margin controls

“units”: [“px”, “%”, “em”, “rem”, “vh”, “vw”], // Define available units

“spacingSizes”: [

{

“name”: “None”,

“size”: “0”,

“slug”: “0”

},

{

“name”: “Small”,

“size”: “0.5rem”,

“slug”: “small”

},

{

“name”: “Medium”,

“size”: “1rem”,

“slug”: “medium”

},

{

“name”: “Large”,

“size”: “2rem”,

“slug”: “large”

},

{

“name”: “X-Large”,

“size”: “4rem”,

“slug”: “x-large”

}

]

},

“border”: {

“color”: true,

“radius”: true,

“style”: true,

“width”: true

}

“`

blockGap sets the default spacing between blocks within layouts. spacingSizes are custom presets for padding and margin.

Global Styles (styles)

The styles object in theme.json applies default styles to elements and blocks without needing to target them with CSS directly.

Element Styles

You can style core HTML elements like body, h1, a, etc.

“`json

“styles”: {

“color”: {

“text”: “var(–wp–preset–color–dark)”,

“background”: “var(–wp–preset–color–light)”

},

“typography”: {

“fontFamily”: “var(–wp–preset–font-family–inter)”,

“fontSize”: “var(–wp–preset–font-size–base)”,

“lineHeight”: “1.6”

},

“elements”: {

“link”: {

“color”: {

“text”: “var(–wp–preset–color–primary)”

},

“:hover”: {

“color”: {

“text”: “var(–wp–preset–color–accent)”

}

}

},

“h1”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–gigantic)”,

“lineHeight”: “1.1”

}

},

“h2”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–huge)”,

“lineHeight”: “1.2”

}

}

},

// … rest of the styles

}

“`

Here, we set default text color for the body, general font, and specific styles for links and heading levels. Notice the use of var(--wp--preset--...) to reference your defined presets.

Block Styles

You can also apply default styles to specific blocks. This is where you override core block styles or provide sensible defaults.

“`json

“styles”: {

// …

“blocks”: {

“core/site-title”: {

“color”: {

“text”: “var(–wp–preset–color–primary)”

},

“typography”: {

“fontFamily”: “var(–wp–preset–font-family–merriweather)”,

“fontSize”: “var(–wp–preset–font-size–x-large)”,

“fontWeight”: “700”

}

},

“core/navigation”: {

“typography”: {

“fontSize”: “var(–wp–preset–font-size–small)”,

“textTransform”: “uppercase”

},

“spacing”: {

“blockGap”: “1.5rem”

}

},

“core/button”: {

“color”: {

“background”: “var(–wp–preset–color–primary)”,

“text”: “var(–wp–preset–color–light)”

},

“border”: {

“radius”: “4px”

},

“typography”: {

“fontWeight”: “bold”

},

“:hover”: {

“color”: {

“background”: “var(–wp–preset–color–accent)”

}

}

},

“core/paragraph”: {

“spacing”: {

“margin”: {

“bottom”: “1.5rem”

}

}

}

}

}

“`

This is a snippet, but you can see how you’d target blocks like core/site-title, core/navigation, core/button, and core/paragraph to apply default styles.

Setting Up a functions.php (When You Still Need PHP)

While FSE themes rely heavily on HTML templates and theme.json, functions.php isn’t entirely obsolete. It’s still useful for things like:

  • Enqueuing custom scripts or stylesheets that aren’t handled by theme.json (rare, but possible).
  • Registering custom block patterns and pattern categories.
  • Adding theme support for features not covered by theme.json (e.g., specific image sizes if you’re using classic image functions).
  • Custom post types and taxonomies.
  • API integrations or backend logic.

Basic functions.php for FSE

Create my-fse-theme/functions.php.

“`php

if ( ! function_exists( ‘my_fse_theme_setup’ ) ) :

/**

  • Sets up theme defaults and registers support for various WordPress features.

*

  • Note that this function is hooked into the after_setup_theme hook, which
  • runs before the init hook. The init hook is too late for some features, such
  • as indicating support for post thumbnails.

*/

function my_fse_theme_setup() {

// Add support for block styles.

add_theme_support( ‘wp-block-styles’ );

// Enqueue editor styles.

add_editor_style( ‘style.css’ ); // Your main stylesheet (or separate editor styles)

// Remove the core default block patterns.

remove_theme_support( ‘core-block-patterns’ );

// Register block pattern categories.

register_block_pattern_category(

‘my-fse-theme’,

array( ‘label’ => esc_html__( ‘My FSE Theme Patterns’, ‘my-fse-theme’ ) )

);

}

endif;

add_action( ‘after_setup_theme’, ‘my_fse_theme_setup’ );

/**

  • Enqueue scripts and styles.

*/

function my_fse_theme_scripts() {

wp_enqueue_style( ‘my-fse-theme-style’, get_stylesheet_uri(), array(), wp_get_theme()->get( ‘Version’ ) );

// If you want to enqueue a separate stylesheet specifically for the editor.

// wp_enqueue_style( ‘my-fse-theme-editor-style’, get_theme_file_uri( ‘/assets/css/editor-style.css’ ), array(), wp_get_theme()->get( ‘Version’ ) );

}

add_action( ‘wp_enqueue_scripts’, ‘my_fse_theme_scripts’ );

// Load custom block patterns.

require_once get_theme_file_path( ‘inc/block-patterns.php’ );

“`

Let’s break down some key parts:

  • add_theme_support( 'wp-block-styles' );: Ensures that block styles (from theme.json and block editor CSS) are loaded on the frontend.
  • add_editor_style( 'style.css' );: Loads your theme’s main style.css (or a dedicated editor stylesheet) into the block editor for consistency.
  • remove_theme_support( 'core-block-patterns' );: If you want to use only your own block patterns, this removes WordPress’s default ones.
  • register_block_pattern_category(): This creates a new category for your custom block patterns, making them easier to find in the editor.
  • wp_enqueue_style( 'my-fse-theme-style', get_stylesheet_uri() ... );: This loads your theme’s main style.css on the frontend. Even though theme.json styles are loaded automatically, you might still put custom CSS in style.css.
  • require_once get_theme_file_path( 'inc/block-patterns.php' );: This is a good way to organize your block patterns, pulling them in from a separate file.
If you’re interested in enhancing your website’s performance after building a full site editing (FSE) block theme from scratch, you might want to check out a related article on optimizing your site’s speed. Understanding how to improve loading times can significantly impact user experience and SEO rankings. For more insights on this topic, you can read about it in this helpful guide that covers various strategies to boost your site’s efficiency.

Crafting Custom Block Patterns

Block patterns are pre-designed block layouts that users can insert with a single click. They’re a huge time-saver and provide a consistent design language.

The inc Directory

Create an inc folder inside my-fse-theme.

block-patterns.php

In my-fse-theme/inc/block-patterns.php:

“`php

/**

  • My FSE Theme: Block Patterns

*

  • @package MyFSETheme

*/

/**

  • Register Block Pattern.

*/

function my_fse_theme_register_block_patterns() {

// Hero Section Pattern.

register_block_pattern(

‘my-fse-theme/hero-section’,

array(

‘title’ => esc_html__( ‘Hero Section with Title and Button’, ‘my-fse-theme’ ),

‘description’ => esc_html__( ‘A simple hero section with a heading, paragraph, and button.’, ‘my-fse-theme’ ),

‘categories’ => array( ‘my-fse-theme’ ), // Your custom category

‘content’ => ‘

Welcome to My FSE Theme

This is an example of a custom block pattern. Customize it freely!

‘,

)

);

// Another pattern example: Two Column Feature

register_block_pattern(

‘my-fse-theme/two-column-feature’,

array(

‘title’ => esc_html__( ‘Two Column Feature with Image and Text’, ‘my-fse-theme’ ),

‘description’ => esc_html__( ‘A two-column layout showing an image on one side and text on the other.’, ‘my-fse-theme’ ),

‘categories’ => array( ‘my-fse-theme’ ),

‘content’ => ‘

Highlight Your Features

Describe your product or service right here. Use engaging language to captivate your audience and explain the benefits.

  • Benefit 1: Point out a key advantage.
  • Benefit 2: Detail another great feature.
  • Benefit 3: Emphasize what makes you unique.

‘,

)

);

}

add_action( ‘init’, ‘my_fse_theme_register_block_patterns’ );

“`

Getting the HTML markup for patterns is easy: build your desired layout in the WordPress editor, switch to “Code editor” view, copy the HTML, and paste it into the content property of your register_block_pattern function. Make sure to escape single quotes ' or use double quotes " judiciously.

Enhancing Core Support and Internationalization

screenshot.png

Create a screenshot.png file (1200x900px recommended) at the root of your theme folder. This image will represent your theme in the “Appearance > Themes” section of WordPress.

readme.txt

A readme.txt file is good practice for describing your theme, its features, installation, and any special instructions.

Internationalization (i18n)

For any user-facing text strings in your functions.php or theme.json (like names of patterns or colors), you’ll want to make them translatable.

In functions.php you’ve already seen esc_html__( 'My FSE Theme Patterns', 'my-fse-theme' ). The 'my-fse-theme' is your text domain. You’ll need to run a tool like WP-CLI (wp i18n make-pot .) to generate a .pot file, which can then be used to create .mo and .po translation files.

For theme.json, the name properties of colors, fonts, or font sizes cannot directly use PHP translation functions. However, WordPress handles these strings for you under the hood if you provide a textdomain in your theme.json:

“`json

{

“version”: 2,

“textdomain”: “my-fse-theme”,

“settings”: {

“color”: {

“palette”: [

{

“slug”: “primary”,

“color”: “#007cba”,

“name”: “Primary” // This string will be translatable

},

// …

]

}

}

}

“`

With the textdomain defined, WordPress will automatically make the name properties translatable in the Site Editor.

Activation and Testing

Once you’ve set up these files:

  1. Upload to WordPress: Place your my-fse-theme folder into wp-content/themes.
  2. Activate: Go to “Appearance > Themes” in your WordPress admin. You should see “My FSE Theme”. Click “Activate”.
  3. Explore the Site Editor: Navigate to “Appearance > Editor”. This is where you can explore the templates (index.html, single.html, page.html), template parts (header.html, footer.html), and global styles (theme.json) visually. Make changes and see them update live.
  4. Test Pages and Posts: Create a few pages and posts to ensure your single.html and page.html templates are rendering correctly, as well as your blog archive (index.html).
  5. Check Block Patterns: When adding a new block to a page/post, click “Patterns” and look for your custom category (e.g., “My FSE Theme Patterns”).

Building a full site editing theme from scratch is a journey, and this guide sets a strong foundation. The key is understanding how theme.json interacts with the block editor, and how HTML files filled with block markup replace traditional PHP templates. With these tools, you have immense power to define a modern, block-first WordPress experience.