Estimated reading time: 6 minutes
Prerequisites: Global Styles & theme.json.
Last updated
WordPress 6.1 introduced four new PHP filters for theme.json. You can use the filters to override both the WordPress default theme.json, theme settings, and user settings.
This post is explorative, as I myself is learning about the filters and their use cases. The documentation about these filters is very limited. I was actually unaware of the filters until Gutenberg contributor Nick Diego highlighted that we needed to improve the documentation.
The announcement post on WordPress.org:
The documentation that Nick kindly added to the block developer handbook:
How it works
Here is how it works: WordPress loads a default theme.json file (source). This file has all the default settings including colors and gradients. WordPress merges this data with settings from other sources, in the following order:
- The block itself (block supports)
- The theme.json file in the active theme (and parent theme if applicable)
- User settings: The options in the Styles sidebar in the Site Editor
Just like there are four sources (the announcement post calls them layers), there are four filters:
- wp_theme_json_data_default: hooks into the default data provided by WordPress
- wp_theme_json_data_blocks: hooks into the data provided by the blocks
- wp_theme_json_data_theme: hooks into the data provided by the theme
- wp_theme_json_data_user: hooks into the data provided by the user
This is important because it means that we can:
- Override theme settings from our plugin, but permit user settings.
- Override default settings, but the permit theme and user settings.
Basic usage
I have copied this code example from the announcement post. When you override the settings there are some formatting differences between the PHP version and theme.json. In theme.json you use both JavaScript objects and arrays. In the example, the color palette object is a nested array instead.
Create a new function with the theme json data as a parameter. Hook the function to the filter: add_filter( ‘wp_theme_json_data_theme’,’prefix_filter_theme_json_theme’ );
function prefix_filter_theme_json_theme( $theme_json ){
}
add_filter( 'wp_theme_json_data_theme', 'prefix_filter_theme_json_theme' );
Add your changes in the form of an array. Note that you must include the version, even if you are not changing it:
function filter_theme_json_theme( $theme_json ){
$new_data = array(
'version' => 2,
'settings' => array(
'color' => array(
'palette' => array(
array(
'slug' => 'base',
'color' => 'white',
'name' => __( 'Base', 'text-domain' ),
),
),
),
),
);
return $theme_json->update_with( $new_data );
}
add_filter( 'wp_theme_json_data_theme', 'filter_theme_json_theme' );
Return the changes using update_with which is a part of the WP_Theme_JSON_Data class:
https://developer.wordpress.org/reference/classes/wp_theme_json_data/
https://developer.wordpress.org/reference/classes/wp_theme_json_data/update_with/
Differences compared to block_editor_settings_all
So what makes the new filters different from the block_editor_settings_all filter? They have slightly different use cases:
- When you use block_editor_settings_all, WordPress has already merged the data from the four sources into one.
- You can use block_editor_settings_all to filter other things than theme.json settings, like locking.
- The wp_theme_json_data filters work directly with the WP_Theme_JSON_Data class.
- The formatting is different, the wp_theme_json_data filters uses a format that is very close to what you use in your theme.json file.
How to use the filters in a classic theme
So, the first thing I wanted to test was if using the filter in a classic theme would trigger the check that tells WordPress wether the theme is a block theme or not.
As you might know, including a theme.json file in a classic theme activates the template editor and changes the group block markup; does the filter do the same, or can we use it to enable features and avoid these side effects?
I added this code to functions.php in Twenty Twenty-One:
function tt1_filter_theme_json_theme( $theme_json ){
$new_data = array(
'version' => 2,
'settings' => array(
'typography' => array(
'dropCap' => false
),
),
);
return $theme_json->update_with( $new_data );
}
add_filter( 'wp_theme_json_data_default', 'tt1_filter_theme_json_theme' );
Since the theme does not have a theme.json file, I used wp_theme_json_data_default. The result was the following:
- Existing paragraphs with drop cap already enabled were unchanged.
- The drop cap option was not available for new paragraphs.
- The group block was unchanged, meaning the wp-block-group__inner-container was still used.
- The template editor was not enabled.
So far so good; lets try something more challenging. What happens in Twenty Twenty-One if I enable useRootPaddingAwareAlignments and add default spacing?
function tt1_filter_theme_json_theme( $theme_json ){
$new_data = array(
'version' => 2,
'settings' => array(
"useRootPaddingAwareAlignments" => true,
),
"styles" => array(
'spacing' => array(
'padding' => array(
'top' => '1rem',
'right' => '1rem',
'bottom' => '1rem',
'left' => '1rem'
)
)
)
);
return $theme_json->update_with( $new_data );
}
add_filter( 'wp_theme_json_data_default', 'tt1_filter_theme_json_theme' );
Result: Not much. The test group block that I placed in the post content has a new class: has-global-padding, but there is no CSS output for it.
WordPress did not apply the site wide padding because the theme does not have a wrapping div element with the class wp-site-blocks: This is the div that receive the padding in block themes. On a positive note it doesn’t break anything. You are not going to break your theme styles if you happen to activate a plugin that filters theme.json -at least not with this particular example.
Limited support for filtering default styles
Next, lets try to style some blocks. Can I enable the link color option and set a default link color for post titles?
function tt1_filter_theme_json_theme( $theme_json ){
$new_data = array(
'version' => 2,
'settings' => array(
'color' => array(
'link' => true
)
),
"styles" => array(
'blocks' => array(
'core/post-title' => array(
'elements' => array(
'link' => array(
'color' => array(
'text' => 'red'
)
)
)
)
)
)
);
return $theme_json->update_with( $new_data );
}
add_filter( 'wp_theme_json_data_default', 'tt1_filter_theme_json_theme' );
The result is mixed. The filter above enabled the link color setting. But the default color that I set for the link was only used on the front, not in the block editor. Even when I disabled the themes own link color CSS, the editor did not include the link color.
What if I add a default text and background color?
function tt1_filter_theme_json_theme( $theme_json ){
$new_data = array(
'version' => 2,
"styles" => array(
'blocks' => array(
'core/post-title' => array(
'color' => array(
'text' => 'red',
'background' => 'blue'
)
)
)
)
);
return $theme_json->update_with( $new_data );
}
add_filter( 'wp_theme_json_data_default', 'tt1_filter_theme_json_theme' );
With this example, the colors are applied in the front but not in the block editor.
I am not sure if this is a bug, but I have opened an issue for it in the Gutenberg GitHub repository.
Are the filters a replacement for theme.json?
One of the things I wanted to explore was if it is possible to completely replace theme.json with this filter. The answer is sort of, but there are internal checks for the theme.json file that enables or disables features based on if the file exist.
When the file exists, WordPress enables the following:
- Layout support for blocks. The layout in this case is the “Inner blocks use content width” and related options for container blocks.
- Theme support overrides. For example if a theme has a color palette both in theme.json and
add_theme_support('editor-color-palette')
.
If the file does not exist:
- WordPress loads a set of default CSS rules for layout and margin, as well as some block styles. This is essentially a fallback intended for classic themes.
- WordPress re-adds the inner div to the group block markup.
If you want to learn more please review the usage of wp_theme_has_theme_json() function in WordPress 6.2.