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

No sé si te habrás dado cuenta o no, pero la semana pasada actualizamos nuestra web con un nuevo tema. Nuestro nuevo tema está basado en el Twenty Twenty-Three de WordPress, al cual le hemos aplicado varias modificaciones para adaptarlo a nuestras necesidades. Hacía ya días que nos apetecía modernizar un poco la web y pensamos que esta era la oportunidad perfecta para hacerlo y para aprender más sobre Full Site Editing porque, efectivamente, este tema es totalmente compatible con FSE.

Pues bien, en esta entrada veremos un truco que hemos implementado en el tema para encolar de forma inteligente estilos de bloque. ¿Que no sabes de qué te estoy hablando? No pasa nada, sigue leyendo y lo entenderás.

Adaptando los bloques según nuestras necesidades

Empecemos con un ejemplo sencillo. Nuestra web tiene bastantes testimonios con los que compartimos la opinión que nuestros usuarios tienen sobre nuestros plugins. Para poder crear estos testimonios de forma cómoda, hemos decidido crear el siguiente patrón de bloques en el tema:

Escribe un testimonio…

Nombre

Cargo

el cual, en un alarde de originalidad, hemos llamado «Testimonio». El patrón consiste, como puedes intuir, en un bloque grupo que envuelve el párrafo donde escribir el testimonio y un bloque fila con información sobre quién ha dicho eso. Este bloque fila tiene, como ves, una imagen y un bloque pila con otros dos párrafos (para el nombre y el cargo que ocupa la autora del testimonio), con las propiedades de tipografía pertinentes para que se muestren en negrita y cursiva respectivamente.

Pues bien, si te fijas, el párrafo inicial tiene un símbolo de abrir comillas. ¿Cómo lo hemos metido? ¡Muy sencillo! El bloque grupo que representa nuestro testimonio tiene la clase is-testimonial, la cual tiene asociado el siguiente estilo CSS:

.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;
}

Como ves, adaptar los bloques de WordPress a nuestras necesidades puede ser tan fácil como asignarles una clase propia y definir los estilos específicos que queremos.

Encolado de estilos clásico

Desde la versión 5.0 de WordPress, disponemos de un par de acciones con las que encolar los recursos asociados a un bloque: enqueue_block_assets y enqueue_block_editor_assets. Tal y como puedes leer en la documentación enlazada, la primera acción sirve para encolar los recursos en el front-end y en el editor, mientras que la segunda acción solo los encola en el editor.

Teniendo en cuenta esto, si queremos encolar el estilo anterior en nuestra web para que los testimonios «funcionen», deberíamos hacer algo tal que así:

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

Ahora bien, esta solución no es ideal, porque estamos encolando los estilos de un testimonio en todas las páginas de nuestra web, independientemente del contenido de la misma. Idealmente, querríamos encolar este estilo únicamente cuando la página que estamos visualizando contiene un testimonio y, por lo tanto, los necesita. ¿Cómo lo conseguimos?

Nelio A/B Testing

Pruebas A/B nativas en WordPress

Usa tu editor de páginas favorito en WordPress para crear variaciones y lanza pruebas A/B con solo un par de clics. No se necesita saber nada de programación para que funcione.

Explorando el encolado inteligente de estilos

Cuando hablamos de «encolado inteligente» de estilos, ¿a qué nos referimos exactamente? Pues fácil: a encolar únicamente aquellos estilos que son relevantes para la página actual. Es decir, si la página actual tiene un testimonio, necesito encolar los estilos de un testimonio. Pero si no hay ninguno, no.

Esto, que puede parecer complicado de hacer, lo podemos conseguir fácilmente con el filtro render_block_{$blockname}. Tal y como su nombre indica, el filtro render_block_{$blockname} se ejecuta cuando WordPress está pintando un bloque de tipo $blockname. Sabiendo que disponemos de este filtro y que nuestro patrón de testimonio es un bloque grupo (con lo que $blockname es "group") al que le hemos añadido la clase is-testimonial, podemos hacer lo siguiente:

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 );

¿Qué hace el código anterior? Fácil: cuando estamos renderizando un bloque group, miramos si block tiene la clase is-testimonial. Si no la tiene, es un grupo cualquiera y no un testimonio, así que nos vamos directamente. Pero si sí está, obtenemos las reglas CSS de un testimonio del fichero que toque, las encolamos con wp_enqueue_block_support_styles y eliminamos el filtro para evitar seguir encolando las mismas reglas cuando vuelva a salir otro testimonio.

Por cierto, la función has_classname es una función auxiliar que hemos creado nosotros mismos:

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()

Y con esto ya tenemos un encolado inteligente que solo añade el estilo cuando se precisa. Lo único que nos queda ahora para redondear esta solución es encolar los estilos en el editor de WordPress también, lo cual podemos hacerlo con la acción que he mencionado antes:

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 )
    );
  }
);

En el editor vamos a encolar siempre todos los estilos… pero eso no nos preocupa.

Implementando el encolado inteligente de forma genérica

El «problema» de la solución anterior es que debemos replicarla para cada tipo de bloque y clase que tenemos. Por ejemplo, nuestra web también tiene un bloque cover con la clase is-featured-testimonial, con lo que, siguiendo el ejemplo anterior, deberíamos crear el siguiente código:

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 );

el cual es casi, casi, casi idéntico a lo que ya teníamos, siendo los únicos cambios:

  • El nombre de la función
  • El nombre del filtro
  • El nombre de la clase
  • El nombre del fichero CSS

En Nelio somos amantes del principio DRY (ya sabes, aquél que dice «no te repitas»), con lo que no podíamos dejar escapar la oportunidad de buscar aquella solución que nos permitiera añadir nuevos estilos a los bloques de WordPress sin tener que definir una y otra vez las funciones PHP necesarias para encolarlos.

La idea para abstraer el código anterior y conseguir una solución genérica para el encolado inteligente de estilos es muy simple: basta con nombrar de forma inteligente los ficheros CSS y luego aprovecharnos de ello. Por ejemplo, en la entrada de hoy te he hablado de:

  1. un estilo de testimonios «normales» que se aplica a un bloque Grupo
  2. un estilo de testimonios «destacados» que se aplica a un bloque Fondo

Pues con nombres como estos:

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

puedo saber, rápidamente, (a) el tipo de bloque al que aplica y (b) qué clase debe tener ese bloque para que realmente sea necesario cargar el fichero en el front. Así que ahora puedo crear una función auxiliar como la siguiente:

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()

que, dado el nombre de un fichero CSS, añade los filtros pertinentes para encolar los estilos CSS en el editor y en el front según convenga. Finalmente, un simple bucle nos permite cargar todos los estilos del tirón:

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

Espero que te haya gustado la entrada de hoy y, de ser así, compártela para darle más difusión. ¡Nos vemos!

Imagen destacada de 汤 泽坤 en Unsplash.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

He leído y acepto la política de privacidad de Nelio Software

Tus datos personales se almacenarán en SiteGround y serán usados por Nelio Software con el único objetivo de publicar tu comentario aquí. Con el envío de este comentario, nos das el consentimiento expreso para ello. Escríbenos para acceder, rectificar, limitar o eliminar tus datos personales.