[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/block-supports/ -> block-style-variations.php (source)

   1  <?php
   2  /**
   3   * Block support to enable per-section styling of block types via
   4   * block style variations.
   5   *
   6   * @package WordPress
   7   * @since 6.6.0
   8   */
   9  
  10  /**
  11   * Determines the block style variation names within a CSS class string.
  12   *
  13   * @since 6.6.0
  14   *
  15   * @param string $class_string CSS class string to look for a variation in.
  16   * @return array|null The block style variation name if found.
  17   */
  18  function wp_get_block_style_variation_name_from_class( $class_string ) {
  19      if ( ! is_string( $class_string ) ) {
  20          return null;
  21      }
  22  
  23      preg_match_all( '/\bis-style-(?!default)(\S+)\b/', $class_string, $matches );
  24      return $matches[1] ?? null;
  25  }
  26  
  27  /**
  28   * Recursively resolves any `ref` values within a block style variation's data.
  29   *
  30   * @since 6.6.0
  31   * @access private
  32   *
  33   * @param array $variation_data Reference to the variation data being processed.
  34   * @param array $theme_json     Theme.json data to retrieve referenced values from.
  35   */
  36  function wp_resolve_block_style_variation_ref_values( &$variation_data, $theme_json ) {
  37      foreach ( $variation_data as $key => &$value ) {
  38          // Only need to potentially process arrays.
  39          if ( is_array( $value ) ) {
  40              // If ref value is set, attempt to find its matching value and update it.
  41              if ( array_key_exists( 'ref', $value ) ) {
  42                  // Clean up any invalid ref value.
  43                  if ( empty( $value['ref'] ) || ! is_string( $value['ref'] ) ) {
  44                      unset( $variation_data[ $key ] );
  45                  }
  46  
  47                  $value_path = explode( '.', $value['ref'] ?? '' );
  48                  $ref_value  = _wp_array_get( $theme_json, $value_path );
  49  
  50                  // Only update the current value if the referenced path matched a value.
  51                  if ( null === $ref_value ) {
  52                      unset( $variation_data[ $key ] );
  53                  } else {
  54                      $value = $ref_value;
  55                  }
  56              } else {
  57                  // Recursively look for ref instances.
  58                  wp_resolve_block_style_variation_ref_values( $value, $theme_json );
  59              }
  60          }
  61      }
  62  }
  63  /**
  64   * Renders the block style variation's styles.
  65   *
  66   * In the case of nested blocks with variations applied, we want the parent
  67   * variation's styles to be rendered before their descendants. This solves the
  68   * issue of a block type being styled in both the parent and descendant: we want
  69   * the descendant style to take priority, and this is done by loading it after,
  70   * in the DOM order. This is why the variation stylesheet generation is in a
  71   * different filter.
  72   *
  73   * @since 6.6.0
  74   * @access private
  75   *
  76   * @param array $parsed_block The parsed block.
  77   * @return array The parsed block with block style variation classname added.
  78   */
  79  function wp_render_block_style_variation_support_styles( $parsed_block ) {
  80      $classes    = $parsed_block['attrs']['className'] ?? null;
  81      $variations = wp_get_block_style_variation_name_from_class( $classes );
  82  
  83      if ( ! $variations ) {
  84          return $parsed_block;
  85      }
  86  
  87      $tree       = WP_Theme_JSON_Resolver::get_merged_data();
  88      $theme_json = $tree->get_raw_data();
  89  
  90      // Only the first block style variation with data is supported.
  91      $variation_data = array();
  92      foreach ( $variations as $variation ) {
  93          $variation_data = $theme_json['styles']['blocks'][ $parsed_block['blockName'] ]['variations'][ $variation ] ?? array();
  94  
  95          if ( ! empty( $variation_data ) ) {
  96              break;
  97          }
  98      }
  99  
 100      if ( empty( $variation_data ) ) {
 101          return $parsed_block;
 102      }
 103  
 104      /*
 105       * Recursively resolve any ref values with the appropriate value within the
 106       * theme_json data.
 107       */
 108      wp_resolve_block_style_variation_ref_values( $variation_data, $theme_json );
 109  
 110      $variation_instance = wp_unique_id( $variation . '--' );
 111      $class_name         = "is-style-$variation_instance";
 112      $updated_class_name = $parsed_block['attrs']['className'] . " $class_name";
 113  
 114      /*
 115       * Even though block style variations are effectively theme.json partials,
 116       * they can't be processed completely as though they are.
 117       *
 118       * Block styles support custom selectors to direct specific types of styles
 119       * to inner elements. For example, borders on Image block's get applied to
 120       * the inner `img` element rather than the wrapping `figure`.
 121       *
 122       * The following relocates the "root" block style variation styles to
 123       * under an appropriate blocks property to leverage the preexisting style
 124       * generation for simple block style variations. This way they get the
 125       * custom selectors they need.
 126       *
 127       * The inner elements and block styles for the variation itself are
 128       * still included at the top level but scoped by the variation's selector
 129       * when the stylesheet is generated.
 130       */
 131      $elements_data = $variation_data['elements'] ?? array();
 132      $blocks_data   = $variation_data['blocks'] ?? array();
 133      unset( $variation_data['elements'] );
 134      unset( $variation_data['blocks'] );
 135  
 136      _wp_array_set(
 137          $blocks_data,
 138          array( $parsed_block['blockName'], 'variations', $variation_instance ),
 139          $variation_data
 140      );
 141  
 142      $config = array(
 143          'version'  => WP_Theme_JSON::LATEST_SCHEMA,
 144          'settings' => array(
 145              'spacing' => array(
 146                  'blockGap' => true,
 147              ),
 148          ),
 149          'styles'   => array(
 150              'elements' => $elements_data,
 151              'blocks'   => $blocks_data,
 152          ),
 153      );
 154  
 155      // Ensure variation state styles know about any custom viewport breakpoints.
 156      if ( isset( $theme_json['settings']['viewport'] ) ) {
 157          $config['settings']['viewport'] = $theme_json['settings']['viewport'];
 158      }
 159  
 160      // Turn off filter that excludes block nodes. They are needed here for the variation's inner block types.
 161      if ( ! is_admin() ) {
 162          remove_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' );
 163      }
 164  
 165      // Temporarily prevent variation instance from being sanitized while processing theme.json.
 166      $styles_registry = WP_Block_Styles_Registry::get_instance();
 167      $styles_registry->register( $parsed_block['blockName'], array( 'name' => $variation_instance ) );
 168  
 169      $variation_theme_json = new WP_Theme_JSON( $config, 'blocks' );
 170      $variation_styles     = $variation_theme_json->get_stylesheet(
 171          array( 'styles' ),
 172          array( 'custom' ),
 173          array(
 174              'include_block_style_variations' => true,
 175              'skip_root_layout_styles'        => true,
 176              'scope'                          => ".$class_name",
 177          )
 178      );
 179  
 180      // Clean up temporary block style now instance styles have been processed.
 181      $styles_registry->unregister( $parsed_block['blockName'], $variation_instance );
 182  
 183      // Restore filter that excludes block nodes.
 184      if ( ! is_admin() ) {
 185          add_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' );
 186      }
 187  
 188      if ( empty( $variation_styles ) ) {
 189          return $parsed_block;
 190      }
 191  
 192      wp_register_style( 'block-style-variation-styles', false, array( 'wp-block-library', 'global-styles' ) );
 193      wp_add_inline_style( 'block-style-variation-styles', $variation_styles );
 194  
 195      /*
 196       * Add variation instance class name to block's className string so it can
 197       * be enforced in the block markup via render_block filter.
 198       */
 199      _wp_array_set( $parsed_block, array( 'attrs', 'className' ), $updated_class_name );
 200  
 201      return $parsed_block;
 202  }
 203  
 204  /**
 205   * Ensures the variation block support class name generated and added to
 206   * block attributes in the `render_block_data` filter gets applied to the
 207   * block's markup.
 208   *
 209   * @since 6.6.0
 210   * @access private
 211   *
 212   * @see wp_render_block_style_variation_support_styles
 213   *
 214   * @param  string $block_content Rendered block content.
 215   * @param  array  $block         Block object.
 216   * @return string                Filtered block content.
 217   */
 218  function wp_render_block_style_variation_class_name( $block_content, $block ) {
 219      if ( ! $block_content || empty( $block['attrs']['className'] ) ) {
 220          return $block_content;
 221      }
 222  
 223      $block_class_name = $block['attrs']['className'];
 224      if ( ! is_string( $block_class_name ) ) {
 225          return $block_content;
 226      }
 227  
 228      /*
 229       * Matches a class prefixed by `is-style`, followed by the
 230       * variation slug, then `--`, and finally an instance number.
 231       */
 232      preg_match( '/\bis-style-(\S+?--\d+)\b/', $block_class_name, $matches );
 233  
 234      if ( empty( $matches ) ) {
 235          return $block_content;
 236      }
 237  
 238      $tags = new WP_HTML_Tag_Processor( $block_content );
 239  
 240      if ( $tags->next_tag() ) {
 241          /*
 242           * Ensure the variation instance class name set in the
 243           * `render_block_data` filter is applied in markup.
 244           * See `wp_render_block_style_variation_support_styles`.
 245           */
 246          $tags->add_class( $matches[0] );
 247      }
 248  
 249      return $tags->get_updated_html();
 250  }
 251  
 252  /**
 253   * Enqueues styles for block style variations.
 254   *
 255   * @since 6.6.0
 256   * @access private
 257   */
 258  function wp_enqueue_block_style_variation_styles() {
 259      wp_enqueue_style( 'block-style-variation-styles' );
 260  }
 261  
 262  // Register the block support.
 263  WP_Block_Supports::get_instance()->register( 'block-style-variation', array() );
 264  
 265  add_filter( 'render_block_data', 'wp_render_block_style_variation_support_styles', 10, 2 );
 266  add_filter( 'render_block', 'wp_render_block_style_variation_class_name', 10, 2 );
 267  add_action( 'wp_enqueue_scripts', 'wp_enqueue_block_style_variation_styles', 1 );
 268  
 269  /**
 270   * Registers block style variations read in from theme.json partials.
 271   *
 272   * @since 6.6.0
 273   * @access private
 274   *
 275   * @param array $variations Shared block style variations.
 276   */
 277  function wp_register_block_style_variations_from_theme_json_partials( $variations ) {
 278      if ( empty( $variations ) ) {
 279          return;
 280      }
 281  
 282      $registry = WP_Block_Styles_Registry::get_instance();
 283  
 284      foreach ( $variations as $variation ) {
 285          if ( empty( $variation['blockTypes'] ) || empty( $variation['styles'] ) ) {
 286              continue;
 287          }
 288  
 289          $variation_name  = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] );
 290          $variation_label = $variation['title'] ?? $variation_name;
 291  
 292          foreach ( $variation['blockTypes'] as $block_type ) {
 293              $registered_styles = $registry->get_registered_styles_for_block( $block_type );
 294  
 295              // Register block style variation if it hasn't already been registered.
 296              if ( ! array_key_exists( $variation_name, $registered_styles ) ) {
 297                  register_block_style(
 298                      $block_type,
 299                      array(
 300                          'name'  => $variation_name,
 301                          'label' => $variation_label,
 302                      )
 303                  );
 304              }
 305          }
 306      }
 307  }


Generated : Thu Aug 20 08:20:25 2026 Cross-referenced by PHPXref