Semáforo en rojo a la izquierda y en verde a la derecha

I don’t know if you’ve noticed or not, but last week we updated our website and installed a new theme. Our new theme is based on WordPress’ Twenty Twenty-Three and it’s been properly modified to better suit our needs. And the good news is, this updated helped us learn more about Full Site Editing.

In this post we’ll look at a trick we’ve implemented in the theme to smart enqueue block styles. Don’t you know what I’m talking about? Keep reading and you’ll understand.

Tweak Block Styles to Suit Our Needs

Let’s start with a simple example. Our website has many testimonials in it, which we use to share the opinion our users have about our plugins. To create these testimonials comfortably, we added the following block pattern in our new theme:

Write testimonial…

Name

Position

A block pattern is, as you may already know, a predefined set of blocks that provide a certain design. For instance, our “Testimonial” block is basically a group that wraps a paragraph and a row. The row contains information of the testimonial’s author: on the one hand, a picture, and on the other hand, their name and further details organized in a column.

All design properties of this block have been designed using the block editor: typography, margins, paddings… All but one. If you pay attention, you’ll notice a quotation mark in the testimonial. We’ve added this image via CSS by adding the is-testimonial class to the group block wrapper:

.wp-block-group.is-testimonial > p:first-child {
  position: relative;
}
.wp-block-group.is-testimonial > p:first-child:before {
  background: var(--wp--preset--color--tertiary);
  content: "";
  display: block;
  font-size: 2rem;
  height: 1em;
  left: -1em;
  -webkit-mask-image: url(data:image/svg+xml;base64,...);
  mask-image: url(data:image/svg+xml;base64,...);
  position: absolute;
  top: -0.3em;
  width: 1em;
}

And that’s the first customization tip you should be aware of: adapting WordPress blocks to our needs can be as easy as assigning them a class and defining the specific styles we want.

Classic Style Enqueuing

WordPress version 5.0 introduced a couple of actions to enqueue the block assets: enqueue_block_assets and enqueue_block_editor_assets. As you can read in the linked documentation, the former enqueues block assets in both the front-end and the editor, while the latter only does so in the editor.

With this in mind, if we want to enqueue the is-testimonial style on our website, all we gotta do is:

add_action(
  'enqueue_block_assets',
  function() {
    $filename = 'styles/testimonial.css';
    wp_enqueue_style(
      'nelio-testimonial',
      get_stylesheet_directory_uri() . $filename,
      array(),
      filemtime( DIR . $filename )
    );
  }
);

Unfortunately, this solution is far from ideal: it enqueues the testimonial styles everywhere, regardless of a page’s content. If the current page has no testimonials, we clearly don’t need these styles. Can we do better?

Smart Style Enqueuing

Smart style enqueuing is all about enqueuing the styles that are relevant to the current page. If a set of CSS does not apply to the current page, don’t include it. So, in our running example, if a page has no testimonials, we don’t want to enqueue testimonial styles. But if it does have at least one testimonial, we do.

This might sound complicated to achieve, but it’s actually pretty easy. When WordPress renders a certain block type, it runs a filter named render_block_{$blockname}. We can use this filter to detect when WordPress is about to render a testimonial block by filtering render_block_group and checking if the group that’s being currently rendered has the is-testimonial class:

function add_testimonial_style( $content, $block ) {
  if ( ! has_classname( 'is-testimonial', $block ) ) {
    return $content;
  }
  
  ob_start();
  require( DIR . 'styles/testimonial.css' );
  $css = ob_get_contents();
  ob_end_clean();
  wp_enqueue_block_support_styles( $css );
  remove_filter( 'render_block_group', 'add_testimonial_style', 10, 2 );
  return $content;
}
add_filter( 'render_block_group', 'add_testimonial_style', 10, 2 );

If it does, we read the CSS rules of a testimonial and enqueue them using wp_enqueue_block_support_styles. Once the styles have been added, there’s no need to keep filtering group renders, so we simply remove the filter.

And that’s how you smart enqueue styles in the front-end! Pretty neat, huh?

Oh and, by the way, has_classname is a helper function we created to safely check if a block has a certain CSS class or not:

function has_classname( string $classname, array $block ):  bool {
  if ( empty( $block['attrs'] ) ) {
    return false;
  }//end if
  $block = $block['attrs'];
  if ( empty( $block['className'] ) ) {
    return false;
  }//end if
  $classes = explode( ' ', $block['className'] );
  return in_array( $classname, $classes, true );
}//end has_classname()

What about the editor, you ask? Well, we need to enqueue them again using the enqueue_block_editor_assets action I mentioned earlier:

add_action(
  'enqueue_block_editor_assets',
  function() {
    $filename = 'styles/testimonial.css';
    wp_enqueue_style(
      'nelio-testimonial',
      get_stylesheet_directory_uri() . $filename,
      array(),
      filemtime( DIR . $filename )
    );
  }
);

Automatic Smart Style Enqueuing

The previous solution is great, but we can still do better. How? Well, right now, we have to replicate the previous steps for each block type and custom class we add. For example, our website also has a “Featured Testimonial,” which we built using a cover block with the is-featured-testimonial class. To load the appropriate styles, we need the following code:

function add_featured_testimonial_style( $content, $block ) {
  $classes = explode( ' ', $block['attrs']['className'] );
  if ( ! in_array( 'is-featured-testimonial', $classes, true ) ) {
    return $content;
  }
  
  ob_start();
  require( DIR . 'styles/featured-testimonial.css' );
  $css = ob_get_contents();
  ob_end_clean();
  wp_enqueue_block_support_styles( $css );
  remove_filter( 'render_block_cover', 'add_featured_testimonial_style', 10, 2 );
  return $content;
}
add_filter( 'render_block_cover', 'add_featured_testimonial_style', 10, 2 );

which is basically what we already had with some minor tweaks:

  • The name of the function
  • The name of the filter
  • The name of the class
  • The name of the CSS file

We’re clearly repeating a lot of stuff here… and that’s something that bugs us. Being lovers of the DRY principle, we couldn’t miss the opportunity to remove this code duplication by abstracting a better solution. We implemented a simple, powerful, and generic solution to smart style enqueuing: just name CSS files properly and let these names guide the style enqueuing process.

For instance, in this post we’ve talked about:

  1. a “normal testimonial” based on a group block
  2. a “featured testimonial” based on a cover block

So all we gotta do is create the following CSS files:

  • styles/group--is-testimonial.css
  • styles/cover--is-featured-testimonial.css

and their names quickly tell us (a) the type of block they apply to and (b) the classname the related block should have to make this style mandatory in the front.

Now we can create a helper function like this:

function add_block_stylesheet( string $filename ): void {
  $parts     = explode( '--', str_replace( '.css', '', $filename ) );
  $blockname = $parts[0];
  $classname = isset( $parts[1] ) ? $parts[1] : '';
  add_action(
    'enqueue_block_editor_assets',
    function() use ( $filename ): void {
      $handle = array_reverse( explode( '/', $filename ) )[0];
      $handle = str_replace( '.css', '', "nelio--{$handle}" );
      wp_enqueue_style(
        $handle,
        trailingslashit( get_stylesheet_directory_uri() ) . $filename,
        array(),
        filemtime( DIR . $filename )
      );
    }
  );
  $enqueue_in_front = function( string $content, array $block )
    use ( &$enqueue_in_front, $filename, $blockname, $classname ): string {
    if ( empty( $classname ) || has_classname( $classname, $block ) ) {
      ob_start();
      require( DIR . $filename );
      $css = ob_get_contents();
      ob_end_clean();
      wp_enqueue_block_support_styles( $css );
      remove_filter( "render_block_{$blockname}", $enqueue_in_front, 10, 2 );
    }//end if
    return $content;
  };
  add_filter( "render_block_{$blockname}", $enqueue_in_front, 10, 2 );
}//end add_block_stylesheet()

that can parse the name of a CSS file and automatically add the required filters we previously had to define manually.

Finally, we simply loop through all our CSS files:

array_map(
  'add_block_stylesheet',
  array_map( 'basename', glob( DIR . 'assets/block-styles/**/*.css' ) ),
);

and voilà! We have all the required hooks in place to smart enqueue styles when needed. Fantastic!

I hope you liked today’s post and, if you did, please share it with your friends and followers. Thanks for reading us and see you in the next post!

Featured Image by 汤泽坤on Unsplash.

2 responses to “DevTips – Smart Enqueuing of Block Styles in Full Site Editing”

    1. Antonio Villegas Avatar

      Thanks for your kind words!

Leave a Reply

Your email address will not be published. Required fields are marked *

I have read and agree to the Nelio Software Privacy Policy

Your personal data will be located on SiteGround and will be treated by Nelio Software with the sole purpose of publishing this comment here. The legitimation is carried out through your express consent. Contact us to access, rectify, limit, or delete your data.