Universal Themes: Customization

Making Global Styles and the Customizer work together

In the last post we shared the idea of a “universal” theme. This post looks at how we can use classic WordPress tools (in this case the Customizer) to customize a block theme, while saving these changes in Global Styles – making a universal theme!

Global Styles Everywhere

Block themes use a theme.json file to define many aspects of the theme, e.g. color palettes and typography. Gutenberg provides a tool to edit these settings (called Global Styles). When a user changes these values in the Global Styles UI, the changes are not saved back into the theme.json file but instead are saved to a Custom Post Type (CPT) in WordPress. Because this CPT can be modified using WordPress APIs, this gives us the power to make changes to Global Styles without relying on the interface in Gutenberg.

This simple idea is the basis for the color and typography customization options we have added to our universal theme Blockbase. The rest is just implementation details…

Implementation details!

When the Customizer loads, we create two new sections:

  1. Colors
  2. Fonts
$wp_customize->add_section(
	'customize-global-styles-colors',
	array(
		'capability'  => 'edit_theme_options',
		'description' => sprintf( __( 'Color Customization for %1$s', 'blockbase' ), $theme->name ),
		'title'       => __( 'Colors', 'blockbase' ),
	)
);
$wp_customize->add_section(
	'customize-global-styles-fonts',
	array(
		'capability'  => 'edit_theme_options',
		'description' => sprintf( __( 'Font Customization for %1$s', 'blockbase' ), $theme->name ),
		'title'       => __( 'Fonts', 'blockbase' ),
	)
);

In each of these sections we create new settings and controls – each setting and control relates to a color/font option in Global Styles. We read the content of the theme.json file and use this to populate the settings in the Customizer. There is a helpful function in Gutenberg which merges together the theme.json settings with any user settings:

WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_raw_data();

We read in an array of color palettes from the theme.json file and display these inside the “Colors” section:

Previews

One of the powers of Global Styles is that it relies on CSS variables. This makes it very easy for us to update the Customizer preview when the controls are changed. Some simple javascript injects new values for these CSS variables into the page, meaning that the preview updates instantly.

First we bind a listener to the control:

// For each of the palette items add a listener
userColorPalette.forEach( ( paletteItem ) => {
	const settingName = userColorSectionKey + paletteItem.slug;
	wp.customize( settingName, ( value ) => {
		value.bind( ( newValue ) => {
			paletteItem.color = newValue;
			blockBaseUpdateColorsPreview( userColorPalette );
		} );
	} );
} );

This updates the global variable userColorPalette, which we then use to create our CSS variables:

function blockBaseUpdateColorsPreview( palette ) {
	// build the CSS variables to inject
	let innerHTML = ':root,body{';
	palette.forEach( ( paletteItem ) => {
		innerHTML += `--wp--preset--color--${ paletteItem.slug }:${ paletteItem.color };`;
	} );
	innerHTML += ';}';

	// inject them into the body
	const styleElement = document.getElementById(
		'global-styles-colors-customizations-inline-css'
	);
	styleElement.innerHTML = innerHTML;
}

Saving

Customizer settings are usually saved in a site option or a theme mod. Since we are building a universal theme, we need to save these changes into Global Styles so that they are reflected in the Site Editor. The easiest way we found to achieve this was to hook into the customize_save_after action, read the settings from the controls, and then update the Global Styles CPT with the new settings. This code is used to get and read the CPT:

WP_Theme_JSON_Resolver_Gutenberg::get_user_custom_post_type_id();
get_post( $user_custom_post_type_id );
json_decode( $user_theme_json_post->post_content );

Once you have the CPT in JSON form it’s simply a case of adding the new settings to it, and saving them back into the CPT:

$user_theme_json_post_content->settings->color->palette = $this->user_color_palette;

wp_update_post( json_encode( $user_theme_json_post_content ) );

Color Palettes

Color palettes are simply combination of color settings. When a user selects a palette we simply change all of the associated colors at once. This can be done with the Javascript API:

wp.customize.control( userColorSectionKey ).setting.set( color );

How can I use this?

The code above is just pseudo code, to explain the concepts. For a fully working example see Blockbase freely available in our free themes repository. This code has a GPL license and can therefore be copied and distributed freely.

However, if you are interested in building a universal theme I would encourage you to use Blockbase as a parent theme. This will mean you get all future improvements to your theme for free!

Ephitah

The Customizer gets a bad rap, and it’s true that it is clunky when compared to the direct manipulation that is possible with Gutenberg. Nevertheless, building these kinds of tools to update Global Styles without the framework of the Customizer would have been much more involved. There is some satisfaction in being able to take advantage of all the hard work that has gone into the Customizer in its final years!

Universal themes: Some ideas

With the Full Site Editing project well underway, theme developers need to be thinking about what the future holds for themes. 

Why block themes?

To take advantage of the Site Editor, themes need to be built out of blocks – this is why we need block themes. Block themes are an entirely new way of creating themes. Classic themes bundle all of the code needed to control the presentation and functionality of the site, with the theme itself. This often means that themes contain extra code to add features (e.g. a slideshow) as well CSS to control the layout.

With block themes, much of the theme’s functionality and presentation is provided by Gutenberg. This means that themes have a shared set of features and style rules, which brings several benefits for users:

  1. The user experience for customising themes is more consistent.
  2. When users switch themes, they won’t lose features, as Gutenberg provides the same features to all themes.
  3. Users can mix and match aspects of different themes – e.g. the header of one theme with the footer of another.

There are also some benefits for developers:

  1. Less code is needed to build a highly functioning theme.
  2. Features that are used by many themes are now provided by the community, so that everyone benefits from the work done to maintain and improve them.

Can I use block themes today?

Block themes are the future and they will provide users great tools when Full Site Editing launches. For now, only those who are running the latest Gutenberg plugin and are willing to use experimental features like the Site Editor are able to use block themes.

Although the Site Editor is not yet ready, many aspects Full Site Editing will be available in WordPress 5.8. There is an opportunity to start building some form of block themes before Site Editor is ready.

Two editing modes

Full Site Editing brings a new editing mode for themes. For the sake of this article we’ll call the current editing mode, “classic” (Site Editor disabled) and the Full Site Editing mode, “FSE” mode (Site Editor enabled).

Users with classic themes will (probably) need to switch themes if they want to start using the Full Site Editing features. However if we can build block themes in a way that works in “classic” mode, then users will be able to take advantage of Full Site Editing once the tools are available. We have been calling themes like this “universal” themes.

Universal themes

The vision for universal themes is that a user could create a site using WordPress without the Site Editor enabled. Then, when the Site Editor is more mature, users could switch to using that, with all the extra tools that Full Site Editing will bring. 

A universal theme would work in both editing modes.  A user should be able to build a site in classic mode and switch to FSE mode when the Site Editor is more mature or when they are ready to try all the extra tools that Full Site Editing will bring. Changes to a theme in classic mode should be reflected when I enable the Site Editor.

Side note: It is not expected that users would toggle between these two modes. The plan will be for everyone to migrate to the Site Editor at some point. The intention of universal themes is to allow people to easily migrate from classic to FSE, not to encourage switching between them.

Future-proof

We that know that the Site Editor is coming. Building themes in this way will allow users to transition from classic themes to block themes when they are ready without requiring them to switch themes.

To achieve this we need to consider several aspects of themes:

Templates

In classic themes, templates live in the root directory and, by convention, `template-parts`. In block themes they live in `block-templates` and, by convention, `block-template-parts`. We could build templates for both classic and block themes and put them in the respective directories but the problem with this approach is that if users make changes to their templates when in classic mode they will not be reflected if they switch to the Site Editor.

To use the same templates when in classic and FSE mode we can put our templates in the `block-template-parts` directory. We can include them in our block templates in our template file like this:

block-templates/page.html:

<!-- wp:template-part {"slug":"page"} -->

For classic templates we can include the same file using `gutenberg_block_template_part`, which will execute `do_blocks` on this template:

/page.php:

echo gutenberg_block_template_part( 'page' );

This approach means that users can switch from classic to FSE mode without losing their changes.

Classic themes use `wp_nav_menu` to render theme navigation elements, which can be customised in the Dashboard, or in the Customizer. On the other hand, Block themes rely on the Navigation block to display their navigation. A recent change to the Navigation block means that we can connect these interfaces together:

By passing the navigation block an `__unstableLocation` attribute with the location of the classic menu, we can display classic menus inside the Navigation block:

<!-- wp:navigation {"__unstableLocation":"primary"} --><!-- /wp:navigation -->

This allows us to edit menus in the Customizer and the Dashboard when in classic mode, and in the Navigation block when in FSE mode.

Sidenote: A short-coming of this approach is that once the navigation has been edited in FSE mode, it will not be possible to edit it in classic mode.

Customization

In classic mode, the Customizer is the main tool for editing theme settings. Aside from Menus (see above), by default the Customizer allows users to edit Site Identity and Homepage Settings. These are WordPress settings that are editable elsewhere in the Dashboard, and are also available to block themes. The exception to this is “Additional CSS” which is not editable when the Site Editor is enabled. The hope is that the Site Editor will allow users to edit their site directly so that they won’t need to use additional CSS.

Often themes add more customisation options – for example colors and fonts. Global Styles offers some of the same customisations. Using the Global Styles API it is possible to make changes to Global Styles in the Customizer. This would allow users to customise their site using both the Customizer and Global Styles. We’ve been playing with this idea in this PR: Quadrat: Add color customization.

Opting in and out of the Site Editor

One unresolved challenge for universal themes is how to opt-in users to the Site Editor. At present the Site Editor is enabled for all block themes, which includes universal themes (if your theme has a file in the location `block-templates/index.html` it will be treated as block theme).

To be able to launch universal themes before the Site Editor is ready, we will need a mechanism to temporarily disable it. There is more discussion on this issue.

What’s next?

At the moment universal themes are just an idea; there are still many unresolved questions about how we could achieve this vision. For an example of a theme that tries to take a universal approach see Quadrat.

Look out for more posts about this idea as we keep experimenting with it.

Blockbase: A parent theme for block themes

Phase 2 of Gutenberg introduces Full Site Editing to WordPress; to make this possible, we need a new way of building themes – using blocks.

How do block themes work?

Block themes use templates made entirely of blocks. The layout is configured using a combination of theme.json and CSS. The settings in theme.json are used to generate CSS – this is part of the Global Styles feature. The theme CSS is added after the theme.json CSS and together these rules define the layout for a theme. If you’re just getting started with block themes, this guide should help.

Can I style my whole theme with Global Styles?

Global Style allows us to set styles for some aspects of the theme, but many common customisations are not yet available. We are actively helping to add more customisations to Global Styles, but in the meantime we have been working on a mechanism to define a lot more theme styles in a custom section of theme.json, until they are available in Global Styles.

The result of this work is a new theme called Blockbase, loosely based on the classic theme Blank Canvas. This block theme attempts to make all the common theme styles configurable in theme.json, and provides the CSS needed to make them work until the blocks themselves support these settings. As more of these configurations are added to Global Styles, we will remove the corresponding CSS from Blockbase.

Can Blockbase be used to build more block themes?

We are currently using Blockbase as a parent theme for all of our block themes; in time we hope that it won’t be needed. This means that child themes only need to define their settings in a theme.json file, and the parent theme CSS will adjust to these settings. This approach gives us an agreed standard when configuring block styles. It also will make it trivial to move to the Global Styles implementation when that becomes available in core.

Blockbase is intended to be a representation of all the theme style settings that we believe should eventually live in Global Styles and be configurable by users. Some themes need customizations beyond what would be possible with Global Styles. These more unique styles continue to be defined in the theme CSS file.

How it works

Style configurations live in the “styles” section of theme.json. Rules can be defined for both “blocks” and “elements”, and these will be interpreted by Global Styles and generate the appropriate CSS.

The theme.json file also contains a section for “custom” configurations, inside the “settings” property. Settings defined in “custom” are output by Global Styles as CSS variables. These variables are used by the theme CSS files to style blocks and other elements.

Child themes of Blockbase are able to redefine these CSS variables by setting different values in its theme.json. The child theme inherits the Blockbase CSS, but with the new CSS variables defined in theme.json.

To avoid a child theme needing to redefine every value in the theme.json file, child themes can create a child-theme.json file and a script will generate a theme.json file, taking the defaults from Blockbase and updating it with the new values from the child theme.

This mechanism will be particularly useful as more customizations are added to Global Styles; in many cases it will only be necessary to update Blockbase itself and rebuild each child theme, to update the theme.json files. You can find out more about this in the theme’s readme file.

Code size

Aside from the benefits outlined above, we have also found that this approach to building themes has resulted in a drastic reduction in the amount of code we need to write for both the parent theme and any child themes. As much as possible we rely on the styles that come with Gutenberg, and only add extra rules where necessary. Blockbase CSS is, at the time of writing about 1000 lines of code, whereas Seedlet CSS is over 4000.

As more of these customizations are added to Global Styles, we will be able to remove the corresponding CSS from Blockbase, so we anticipate the size of the theme CSS to reduce over time.

How Can I Use This?

Blockbase is available on the Automattic themes repo. If there is interest from others in using this approach to build themes we can investigate making it available as an npm package for others to use.

Getting Started with Block Themes

The rationale

With Full Site Editing on the horizon for WordPress, Theme creators need to start to learn how to make themes in a different way. Full Site Editing is sea change in the way that themes work.

When Themes were first added to WordPress, they were simple; just a few template files and some CSS. Over time, as users demanded more from WordPress, themes have grown to contain much of the visual functionality of the site. This has enabled WordPress to be extended as far as your dreams allow, and has contributed to the popularity of WordPress today.

This approach for themes has brought some challenges with it. With so much functionality baked into a theme, changing themes becomes a difficult thing to do. Not only will you lose some of the functionality of your site, you probably have to learn a new UI, since every theme works differently.

The Full Site Editing project aims to address these concerns by moving a lot of this functionality into the Block Editor. This will simplify Themes again so that they will become more like the initial implementation; a presentation layer over the top of the content.

One major benefit of this approach is that users will be able to edit parts of their site that before were only editable with code.

Full Site Editing is several projects combining to create fundamental changes to the way WordPress works.

The Block Editor

Gutenberg – the name of the plugin containing the Block Editor

Fundamental to Block Themes is the Block Editor. Allowing users to express all their content as a Block brings us several benefits:

  • A consistent UI for creating/editing content
  • A reliable API for manipulating content
  • A transferable way to move content around

With this foundation Themes can lean much more heavily on the user’s content than they have before. For example, themes can provide a ready-made collection of blocks that users can insert (Block Patterns).

The Site Editor

The Site Editor allows users to edit templates in the same way that they already edit content blocks. Templates are blocks, so all the things that users are comfortable and familiar with in the Block Editor work the same way in the Site Editor. This allows users to manipulate their site content and layout in the same way as the Post and Page content. No longer will users have to learn different interfaces for different themes.

Preview of the empty site editor

Global Styles

Global Styles lets Themes express their design in a way which can be edited via the Block Editor and Site Editor. Rather than the design living in a CSS file, the most common settings will be defined in the editor itself, which gives the user the power to change them. This will enable users to modify the presentation of their site without writing any custom CSS.

Creating a Block Theme

The easiest way to start creating a new Block Theme is to use the empty theme template:

  1. Clone the theme-experiments repo:
    git clone https://github.com/WordPress/theme-experiments.git
  2. Run this php script to create a new Block Theme:
    php new-empty-theme.php

This provides the bare minimum needed to get started – in time, the amount of boilerplate code needed will shrink.

Next, install and activate the Gutenberg plugin – this will give you access to the features you’ll need for Full Site Editing.

Now when you enable your newly created Block Theme, the Side Editor will appear in the Dashboard sidebar, and the Customizer, Widgets and Menus will disappear.

Next Steps

To create your theme there are three areas to focus on:

  1. Templates
  2. Patterns
  3. Global Styles

We’ll be expanding on each of these in our upcoming posts. Stay tuned!

A schema for Gutenberg blocks

At Jetpack we recently created a new Gutenberg block which displays “Related Posts”. This block is similar in content to the “Latest Posts” block in WordPress.org; it’s just a list of posts. The difference is the way the posts are displayed:

There are plans afoot to update the “Latest Posts” block, so that it contains an image and a post summary (see image below).

As these two blocks grow they seem to be converging on similar layouts, but with different content.

There are similar overlaps with other blocks. For example the layout of the WordPress.org gallery block could also be used to display a list of posts, as could the recently launched Tiled Gallery block. Evidently there are many layout possibilities for a list of posts!

The problem

As the number of Gutenberg blocks grows these overlaps become more obvious. As we start to build pages with Gutenberg it starts to become apparent that this is going to get very messy very quickly.

Essentially blocks have a content and a presentation element. Taking the example we began with, we can see overlap between many different types of content:

Lists of content

One type of content is a list of text, images and links. These come in many different forms, but they share the same structure:

  • Latest posts
  • List of posts in a category
  • List of post in a tag
  • Archive of posts by month/year etc
  • List of categories/tags
  • List featured posts
  • List of images
  • List of authors
  • List of [custom post type]

Similarly, all of the above types of content could be displayed in any of the following ways:

Layouts

Here are some examples of the different types of layout that this content could use:

The challenge is that each of these different types content can be displayed in each of these different layouts. If we wanted to create blocks for each of these combinations we’d end up with over a hundred different blocks. On the other hand we could build one single block which was so complex that it could display all these combinations within one block. Neither of these would be sensible solutions.

Can we use Block Style Variations?

Block Style Variations offer an API which allows us to modify the CSS for a block – they let us overlay a presentation layer on top of an existing block. This is ideal when modifying the way a block looks. However many of the layout variations I have examined above require more than just a different CSS, they need different markup and additional JavaScript.

Separating Content and Presentation

One solution to this problem could be to separate content from presentation in our blocks. What if we were able to define semantics for the content in our blocks, which could be interpreted by different layouts? This would mean users would first think about the content they wanted to show, and then all display options would be open to them.

A Shared Data Structure

The overlap between these blocks occurs because they all share the same data structure (an array of text, image and link). It would be possible to define a standard data structure that blocks can use. When blocks share a data structure, then it should be possible for them to share common layouts. This would mean a photo gallery display could be reused to display a list of posts, or a block that displays a list of recent posts could be reused to show a list of authors.

How Could this work?

“Block Transformations” are an existing mechanism which converts one block into another. At present every transformation has to be coded separately.

Could we re-imagine the way that transformations work, so that if blocks share a common data-structure, they can easily be transformed into each other, without writing custom transformation code?

Similarly, could we make changes to the way “Block Style Variations” work, so that they can apply a layout to our structured data?

Feedback

What do you think? Is this a problem? Are there other solutions I haven’t considered? Leave a comment!