Exploring Per-Block Stylesheets in Block Themes

Nick Diego Avatar

·

Per-Block Stylesheets

Updated August 10, 2022

This article explores an alternative workflow for styling blocks in WordPress block themes. You will learn how to conditionally register per-block stylesheets with wp_enqueue_block_style as well as a more advanced registration technique using this function. We will also discuss the advantages and disadvantages of such an approach.

Before diving into the details, let’s begin with some background on block themes and how we got to this point in WordPress theme development. Feel free to skip ahead!

Background

Despite the evolution of WordPress themes over the years, one thing has remained consistent, the style.css file. A requirement of every theme, this file defines important metadata and generally contains all of a theme’s CSS.

With web development becoming more and more sophisticated, style.css files have grown considerably in size. For example, the WordPress Twenty Ten theme has just under 1,500 lines of CSS. Eleven years later, Twenty Twenty-One clocks in at nearly 6,000 lines.

In January 2022, WordPress 5.9 introduced block themes, a new way of building WordPress websites. In block themes, every visual element of a site is composed of blocks. Theme styles are predominantly handled using Global Settings and Styles, which are specified in a theme.json file. These topics are beyond the scope of this article, but suffice it to say, block themes often require much less CSS than classic WordPress themes. Twenty Twenty-Two, the block theme released alongside 5.9, contains a style.css file of just 149 lines!

That said, most block themes still require some additional CSS, especially for block styles that cannot be accomplished in theme.json. Twenty Twenty-Two places such styles in style.css. This approach, while valid, can lead to block-specific CSS being loaded on webpages where the corresponding block is not being used.

To address this issue, WordPress 5.9 introduced the function wp_enqueue_block_style. Designed for block themes, and classic themes that have opted-in to load separate stylesheets, this function allows you to conditionally load block styles only when the given block is rendered on a page. There are also additional benefits to this approach beyond page load optimization. Let’s explore how to use this new functionality.

Implementing Per-Block Stylesheets

Implementing per-block stylesheets in your block theme is relatively easy. The official developer note provides the complete details, but here is a summarized implementation.

While you can use per-block stylesheets in classic themes, this guide is designed specifically for block themes.

Enqueue Function

The wp_enqueue_block_style function accepts two required arguments, a $block_name and an array of $args.

The full block name consists of a block namespace, a requirement for all registered blocks, and the actual name of the block. The namespace for all WordPress Core blocks is simply core. Therefore, as an example, the full block name for the Buttons block would be core/buttons.

The $args array can include the same parameters used in the wp_enqueue_style function, but we only care about handle and src for this article. The handle is essentially a unique name for the styles being enqueued, and of course, we need the stylesheet’s URL.

Unlike the wp_enqueue_style function, wp_enqueue_block_style also accepts a path parameter in its $args. This parameter is very important, and one of the primary reasons why you might want to employ per-block stylesheets in your block theme. When path is set, the block stylesheet will be inlined provided it’s not too large. This process reduces the number of individual assets that a webpage needs to load and can increase performance. Review the developer note for more technical details.

Implementation

Putting it all together, the implementation of a custom stylesheet for the Core Buttons block would look something like this:

functions.php

/**
 * Enqueue Buttons block stylesheet.
 */
function example_theme_enqueue_buttons_block_styles() {
	$block_name = 'core/buttons';
	$args       = array(
		'handle' => 'example-theme-core-buttons-styles',
		'src'    => get_theme_file_uri( 'assets/blocks/core/buttons.css' ),
		'path'   => get_theme_file_path( 'assets/blocks/core/buttons.css' ), 
	);

	wp_enqueue_block_style( $block_name, $args );
}
add_action( 'after_setup_theme', 'example_theme_enqueue_buttons_block_styles' );
Code language: PHP (php)

The code above should be placed in the functions.php file of your theme and assumes that the Buttons block stylesheet is placed in the assets/blocks/core directory. Feel free to modify the code to suit your needs.

The buttons.css file itself could contain a wide variety of custom button styles. In example below, we have added hover effects to buttons. This is functionality not currently supported in WordPress Core and therefore needs to be handled using CSS.

buttons.css

/* Add hover effect to "Filled" buttons.
--------------------------------------------- */
.wp-block-button:not(.is-style-outline) .wp-block-button__link:hover,
.wp-block-button:not(.is-style-outline) .wp-block-button__link.has-primary-background-color:hover {
    background-color: var(--wp--preset--color--primary-light) !important;
}

.wp-block-button:not(.is-style-outline) .wp-block-button__link.has-secondary-background-color:hover {
    background-color: var(--wp--preset--color--secondary-light) !important;
}

.wp-block-button:not(.is-style-outline) .wp-block-button__link.has-tertiary-background-color:hover {
    background-color: var(--wp--preset--color--tertiary-light) !important;
}
Code language: CSS (css)

Once implemented, the custom stylesheet will be inlined whenever a webpage includes the Buttons block. The markup will be printed towards the top of the <head> container below any Core inline block styles.

Custom Inline Styles for the Core Buttons Block
Custom inline styles for the Core Buttons Custom inline styles for the Core Buttons block.

Implementation at Scale

While the example above accurately details how to enqueue a custom stylesheet for the Buttons block, it’s not easily scalable. In practice, there needs to be a way to automatically enqueue every block stylesheet in a theme at once. The following example will show you one method of accomplishing this.

Setup

The way a theme is set up plays an important role in how stylesheets are enqueued. You need to know the path of each CSS file and the associated block name. You could approach this in many ways, but all options will likely rely on a designated folder/file naming convention.

The full name of a registered block follows the format namespace/block-name. Therefore, in the example below, you can see a designated folder for each namespace within the assets/blocks directory. Individual stylesheets are named according to the registered block name and are placed inside of the correct namespace folders.

example-theme
├── assets
│   ├── blocks
│   │   ├── core
│   │   │   ├── buttons.css
│   │   │   ├── quote.css
│   │   │   └── ...
│   │   ├── namespace
│   │   │   ├── block-name.css
│   │   │   └── ...
│   │   └── ...
│   └── ...
├── functions.php
└── ...
Code language: CSS (css)

Implementation

With the setup complete, let’s create a function to register each block stylesheet based on this structure. This function should generally be placed in the theme’s functions.php file. Note that you will need to modify the code below should you wish to use a different file/folder naming convention.

functions.php

/**
 * Enqueue individual block stylesheets.
 */
function example_theme_enqueue_block_styles() {

	// Get all available block namespaces.
	$block_namespaces = glob( dirname( __FILE__ ) . '/assets/blocks/*/' );
	$block_namespaces = array_map(
		function( $type_path ) { return basename( $type_path ); },
		$block_namespaces
	);

	foreach ( $block_namespaces as $block_namespace ) {

		// Get all available block styles of the given block namespace.
		$block_styles = glob( dirname( __FILE__ ) . '/assets/blocks/' . $block_namespace . '/*.css' );
		$block_styles = array_map(
			function( $styles_path ) { return basename( $styles_path, '.css' ); },
			$block_styles
		);

		foreach ( $block_styles as $block_style ) {

			// Enqueue individual block stylesheets.
			wp_enqueue_block_style(
				$block_namespace . '/' . $block_style,
				array(
					'handle' => 'example-theme-' . $block_namespace . '-' . $block_style . '-styles',
					'src'    => get_theme_file_uri( 'assets/blocks/' . $block_namespace . '/' . $block_style . '.css' ),

					// Add "path" to allow inlining of block styles when possible.
					'path'   => get_theme_file_path( 'assets/blocks/' . $block_namespace . '/' . $block_style . '.css' ),
				),
			);
		}
	}
}
add_action( 'after_setup_theme', 'example_theme_enqueue_block_styles' );
Code language: PHP (php)

This function might look intimidating – let’s break it down.

  • First, the function looks in the assets/blocks folder and gets an array of all block namepaces, which are simply the folder names.
  • Then for each namespace, it creates an array of all available blocks that have custom stylesheets that need to be enqueued. The name of each CSS file is the name of the corresponding block.
  • Finally, the function loops through each block and enqueues the stylesheet using wp_enqueue_block_style.

Newly created custom block stylesheets are automatically enqueued so long as they follow the prescribed setup. This is a much more efficient workflow for theme developers who want to employ this functionality.

Advantages & Disadvantages

The purpose of this article is not to convince you that implementing per-block stylesheets is the best way to structure a block theme. Instead, the goal is to showcase what’s possible while being transparent about the pitfalls. Let’s explore some of the advantages and disadvantages of this approach.

Advantages

Performance

A per-block stylesheet is only loaded when the corresponding block is present. This reduces the amount of unnecessary CSS on a page. Furthermore, if you are using the path parameter described above, custom block styles are inlined meaning fewer assets need to be loaded.

Simplification

Block themes already contain much less CSS than classic WordPress themes. However, you can still end up with a massive style.css file filled with custom block styles. Per-block stylesheets allow you to simplify style.css down to essential global styles that always need to be loaded.

Organization

From the perspective of a theme developer, separating block styles into individual files can also be a very handy workflow. It allows you to easily identify all of the styles that impact each specific block. Those who have used Sass to build WordPress themes will likely be familiar with this approach. In fact, you could combine Sass with this workflow, see below.

Disadvantages

Duplicate Code

Perhaps the biggest downside is the potential for duplicate code. For example, if you were to define custom styles for the Buttons block, you would likely want to apply similar styling to the button markup in the Search block. With this setup, you would need to apply similar styles in both buttons.css and search.css.

Stylesheets Aren’t Minified

Block stylesheets are not automatically minified by WordPress which has implications especially when styles are inlined. This is more of a minor annoyance rather than a serious disadvantage. You can minify the stylesheets yourself prior to distributing a theme.

Performance

It may seem odd that performance is also a disadvantage, but if you are not employing the path parameter, per-block stylesheets will not be inlined. Depending on how many stylesheets you are using, this can lead to a lot of assets that need to be fetched. While stylesheets will be cached, there is theoretically a chance that this approach could decrease performance compared to a single large styles.css file. Therefore, using the path parameter is recommended.

Sass Implementation

Added August 10, 2022

After publishing this article, it became clear that combining per-block stylesheets with Sass would be the optimal deployment of this technique. Sass addresses the disadvantage of unminified stylesheets and is commonly used in web development. Therefore, let’s look at a basic example of how Sass can be implemented.

If you are not familiar with Sass, check out their excellent documentation and learn how to install Sass on your machine.

Once Sass is installed, we just need to modify the theme folder to include an src or “source” folder that will house the block .scss files. Given the file structure already outlined in this article, the changes would look something like this:

example-theme
├── assets
│   ├── blocks
│   │   ├── core
│   │   │   ├── buttons.css
│   │   │   ├── quote.css
│   │   │   └── ...
│   │   └── ...
│   └── ...
├── functions.php
├── src
│   ├── blocks
│   │   ├── core
│   │   │   ├── buttons.scss
│   │   │   ├── quote.scss
│   │   │   └── ...
│   │   └── ...
│   └── ...
└── ...
Code language: CSS (css)

Notice how src is essentially a mirror of the assets folder. With this transition to Sass, you will never actually interact with the .css files in the assets folder because they will be generated and minified by Sass.

To begin using this setup, navigate to your theme directory in the terminal and run the following command. Edit your per-block .scss stylesheets as needed and Sass will watch for changes and update the asset stylesheets accordingly.

sass --watch src/blocks/core:assets/blocks/core --no-source-map

Note that we are using the --no-source-map flag to surpress source maps. While all implementations are different, we have found these are generally not needed in block theme development, but feel free to enable.

Sass is incredibly an powerful tool and for there are many more complicated ways to deploy this functionality with per-block stylesheet, but hopefully this example provides a glimpse at what’s possible.

Conclusion

So what do you think? Do you find this approach intriguing? Would it fit into your existing workflow, or is it too drastic of a change? Have you started building block themes? Let me know in the comments or reach out directly on Twitter.

As always, external links referenced in this article as well as other related resources are listed below. Stay tuned for more articles on block theming, custom block development, and building with modern WordPress techniques. Until next time!