[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * Block state support for frontend CSS generation.
   4   *
   5   * Generates scoped CSS for per-instance state styles declared in block attributes,
   6   * including pseudo-states (e.g., `style[':hover']`) and responsive states
   7   * (e.g., `style['@mobile']` and `style['@mobile'][':hover']`).
   8   *
   9   * @package WordPress
  10   * @since 7.1.0
  11   */
  12  
  13  /**
  14   * Converts internal preset references to CSS custom property references.
  15   *
  16   * State styles are emitted as CSS rules and cannot rely on preset classnames.
  17   * Converting `var:preset|color|contrast` to
  18   * `var(--wp--preset--color--contrast)` ensures preset values are emitted as
  19   * declarations by the style engine.
  20   *
  21   * @since 7.1.0
  22   *
  23   * @param mixed $value Style value to normalize.
  24   * @return mixed Normalized style value.
  25   */
  26  function wp_normalize_state_preset_vars( $value ) {
  27      if ( is_array( $value ) ) {
  28          foreach ( $value as $key => $nested_value ) {
  29              $value[ $key ] = wp_normalize_state_preset_vars( $nested_value );
  30          }
  31          return $value;
  32      }
  33  
  34      if ( ! is_string( $value ) || ! str_starts_with( $value, 'var:preset|' ) ) {
  35          return $value;
  36      }
  37  
  38      $unwrapped_name = str_replace( '|', '--', substr( $value, strlen( 'var:' ) ) );
  39      return "var(--wp--$unwrapped_name)";
  40  }
  41  
  42  /**
  43   * Normalizes a state style object before generating CSS declarations.
  44   *
  45   * @since 7.1.0
  46   *
  47   * @param array $style State style object.
  48   * @return array Normalized state style object.
  49   */
  50  function wp_normalize_state_style_for_css_output( $style ) {
  51      // Layout is processed separately by wp_render_layout_support_flag(), so we remove it before declaration generation.
  52      unset( $style['layout'] );
  53      $style = wp_normalize_state_preset_vars( $style );
  54      return $style;
  55  }
  56  
  57  /**
  58   * Adds fallback border-style declarations for visible border declarations.
  59   *
  60   * CSS does not render border color or width unless a border style is also set.
  61   * State styles are emitted as stylesheet rules rather than inline styles, so
  62   * they cannot rely on the block-library inline-style attribute fallback rules.
  63   *
  64   * @since 7.1.0
  65   *
  66   * @param array $declarations CSS declarations generated by the style engine.
  67   * @return array CSS declarations with fallback border styles applied where needed.
  68   */
  69  function wp_get_state_declarations_with_fallback_border_styles( $declarations ) {
  70      if ( ! is_array( $declarations ) ) {
  71          return $declarations;
  72      }
  73  
  74      $has_border_style = isset( $declarations['border-style'] ) && '' !== $declarations['border-style'];
  75      $has_border_color = isset( $declarations['border-color'] ) && '' !== $declarations['border-color'];
  76      $has_border_width = isset( $declarations['border-width'] ) && '' !== $declarations['border-width'];
  77  
  78      if ( ! $has_border_style && ( $has_border_color || $has_border_width ) ) {
  79          $declarations['border-style'] = 'solid';
  80      }
  81  
  82      $sides = array( 'top', 'right', 'bottom', 'left' );
  83      foreach ( $sides as $side ) {
  84          $side_style_property = "border-$side-style";
  85          $side_color_property = "border-$side-color";
  86          $side_width_property = "border-$side-width";
  87  
  88          $has_side_style = isset( $declarations[ $side_style_property ] ) && '' !== $declarations[ $side_style_property ];
  89          $has_side_color = isset( $declarations[ $side_color_property ] ) && '' !== $declarations[ $side_color_property ];
  90          $has_side_width = isset( $declarations[ $side_width_property ] ) && '' !== $declarations[ $side_width_property ];
  91  
  92          if ( ! $has_border_style && ! $has_side_style && ( $has_side_color || $has_side_width ) ) {
  93              $declarations[ $side_style_property ] = 'solid';
  94          }
  95      }
  96  
  97      return $declarations;
  98  }
  99  
 100  /**
 101   * Adds background reset declarations to prevent gradient/solid color conflicts.
 102   *
 103   * When a state sets a solid background-color, any gradient applied to the
 104   * default state (via `background` shorthand or `background-image`) must be
 105   * explicitly cleared. Without this, the gradient image layer remains visible
 106   * on top of the solid hover color even when `!important` is used, because
 107   * `background-color` and `background-image` are separate CSS properties.
 108   *
 109   * @since 7.1.0
 110   *
 111   * @param array $declarations CSS declarations generated by the style engine.
 112   * @return array CSS declarations with background resets applied where needed.
 113   */
 114  function wp_get_state_declarations_with_background_resets( $declarations ) {
 115      if ( ! is_array( $declarations ) ) {
 116          return $declarations;
 117      }
 118  
 119      $has_background_color = isset( $declarations['background-color'] ) && '' !== $declarations['background-color'];
 120      $has_background       = isset( $declarations['background'] ) && '' !== $declarations['background'];
 121      $has_background_image = isset( $declarations['background-image'] ) && '' !== $declarations['background-image'];
 122  
 123      /*
 124       * When the state sets a solid background-color but no gradient of its own,
 125       * emit `background-image: unset !important` to clear any gradient (whether
 126       * stored as the `background` shorthand or as `background-image`) that was
 127       * applied to the default / normal state via an inline style attribute.
 128       */
 129      if ( $has_background_color && ! $has_background && ! $has_background_image ) {
 130          $declarations['background-image'] = 'unset !important';
 131      }
 132  
 133      return $declarations;
 134  }
 135  
 136  /**
 137   * Adds fallback dimension styles for aspectRatio and height block-support values.
 138   *
 139   * @since 7.1.0
 140   *
 141   * @param array $state_style State style object.
 142   * @return array State style object with fallback dimension styles applied where needed.
 143   */
 144  function wp_get_state_style_with_fallback_dimension_styles( $state_style ) {
 145      if ( ! is_array( $state_style ) ) {
 146          return $state_style;
 147      }
 148  
 149      $dimensions = isset( $state_style['dimensions'] ) && is_array( $state_style['dimensions'] )
 150          ? $state_style['dimensions']
 151          : array();
 152  
 153      if ( empty( $dimensions ) ) {
 154          return $state_style;
 155      }
 156  
 157      if ( wp_is_explicit_aspect_ratio_value( $dimensions['aspectRatio'] ?? null ) ) {
 158          return array_replace_recursive(
 159              $state_style,
 160              array(
 161                  'dimensions' => array(
 162                      'minHeight' => 'unset',
 163                      'height'    => 'unset',
 164                  ),
 165              )
 166          );
 167      }
 168  
 169      $has_min_height = isset( $dimensions['minHeight'] ) && ( is_string( $dimensions['minHeight'] ) || is_numeric( $dimensions['minHeight'] ) ) && '' !== trim( (string) $dimensions['minHeight'] );
 170      $has_height     = isset( $dimensions['height'] ) && ( is_string( $dimensions['height'] ) || is_numeric( $dimensions['height'] ) ) && '' !== trim( (string) $dimensions['height'] );
 171  
 172      if ( $has_min_height || $has_height ) {
 173          return array_replace_recursive(
 174              $state_style,
 175              array(
 176                  'dimensions' => array(
 177                      'aspectRatio' => 'unset',
 178                  ),
 179              )
 180          );
 181      }
 182  
 183      return $state_style;
 184  }
 185  
 186  /**
 187   * Adds a style fragment to a selector-keyed state style group.
 188   *
 189   * @since 7.1.0
 190   *
 191   * @param array       $groups   Selector-keyed style groups.
 192   * @param string|null $selector Block or feature selector.
 193   * @param array       $style    Style fragment.
 194   */
 195  function wp_add_state_style_group( &$groups, $selector, $style ) {
 196      $key = is_string( $selector ) ? $selector : '';
 197  
 198      if ( ! isset( $groups[ $key ] ) ) {
 199          $groups[ $key ] = array(
 200              'selector' => $selector,
 201              'style'    => array(),
 202          );
 203      }
 204  
 205      $groups[ $key ]['style'] = array_replace_recursive( $groups[ $key ]['style'], $style );
 206  }
 207  
 208  /**
 209   * Splits a state style object into groups based on block feature selectors.
 210   *
 211   * @since 7.1.0
 212   *
 213   * @param array $state_style     State style object.
 214   * @param array $block_selectors Block selectors metadata.
 215   * @return array[] Selector/style groups.
 216   */
 217  function wp_get_state_style_groups( $state_style, $block_selectors ) {
 218      $groups = array();
 219  
 220      foreach ( $state_style as $feature => $feature_styles ) {
 221          $feature_selectors = $block_selectors[ $feature ] ?? null;
 222  
 223          if ( is_string( $feature_selectors ) ) {
 224              wp_add_state_style_group(
 225                  $groups,
 226                  $feature_selectors,
 227                  array( $feature => $feature_styles )
 228              );
 229              continue;
 230          }
 231  
 232          if ( is_array( $feature_selectors ) && is_array( $feature_styles ) ) {
 233              $remaining_styles = $feature_styles;
 234  
 235              foreach ( $feature_selectors as $subfeature => $subfeature_selector ) {
 236                  if (
 237                      'root' === $subfeature ||
 238                      ! is_string( $subfeature_selector ) ||
 239                      ! array_key_exists( $subfeature, $feature_styles )
 240                  ) {
 241                      continue;
 242                  }
 243  
 244                  wp_add_state_style_group(
 245                      $groups,
 246                      $subfeature_selector,
 247                      array(
 248                          $feature => array(
 249                              $subfeature => $feature_styles[ $subfeature ],
 250                          ),
 251                      )
 252                  );
 253                  unset( $remaining_styles[ $subfeature ] );
 254              }
 255  
 256              if ( array() !== $remaining_styles ) {
 257                  wp_add_state_style_group(
 258                      $groups,
 259                      $feature_selectors['root'] ?? ( $block_selectors['root'] ?? null ),
 260                      array( $feature => $remaining_styles )
 261                  );
 262              }
 263              continue;
 264          }
 265  
 266          wp_add_state_style_group(
 267              $groups,
 268              $block_selectors['root'] ?? null,
 269              array( $feature => $feature_styles )
 270          );
 271      }
 272  
 273      return array_values( $groups );
 274  }
 275  
 276  /**
 277   * Returns a style object with nested state keys removed.
 278   *
 279   * @since 7.1.0
 280   *
 281   * @param array $state_style State style object.
 282   * @param array $nested_keys Keys to remove from the root style object.
 283   * @return array Root-only style object.
 284   */
 285  function wp_get_root_state_style( $state_style, $nested_keys ) {
 286      if ( ! is_array( $state_style ) ) {
 287          return $state_style;
 288      }
 289  
 290      $root_style = $state_style;
 291      foreach ( $nested_keys as $key ) {
 292          unset( $root_style[ $key ] );
 293      }
 294  
 295      return $root_style;
 296  }
 297  
 298  /**
 299   * Generates all element selectors for a block root selector.
 300   *
 301   * @since 7.1.0
 302   *
 303   * @param string $root_selector The block root CSS selector.
 304   * @return string[] Element selectors keyed by element name.
 305   */
 306  function wp_get_block_state_element_selectors( $root_selector ) {
 307      if ( ! is_string( $root_selector ) || '' === trim( $root_selector ) ) {
 308          return array();
 309      }
 310  
 311      $block_selectors   = wp_split_selector_list( $root_selector );
 312      $element_selectors = array();
 313  
 314      foreach ( WP_Theme_JSON::ELEMENTS as $element_name => $element_selector ) {
 315          $selectors = array();
 316  
 317          foreach ( $block_selectors as $block_selector ) {
 318              $block_selector = trim( $block_selector );
 319              if ( '' === $block_selector ) {
 320                  continue;
 321              }
 322  
 323              if ( $block_selector === $element_selector ) {
 324                  $selectors = array( $element_selector );
 325                  break;
 326              }
 327  
 328              $selector_prefix = "$block_selector ";
 329              if ( ! str_contains( $element_selector, ',' ) ) {
 330                  $selectors[] = $selector_prefix . $element_selector;
 331                  continue;
 332              }
 333  
 334              $prepended_selectors = array();
 335              foreach ( wp_split_selector_list( $element_selector ) as $selector ) {
 336                  $prepended_selectors[] = $selector_prefix . $selector;
 337              }
 338              $selectors[] = implode( ',', $prepended_selectors );
 339          }
 340  
 341          if ( ! empty( $selectors ) ) {
 342              $element_selectors[ $element_name ] = implode( ',', $selectors );
 343          }
 344      }
 345  
 346      return $element_selectors;
 347  }
 348  
 349  /**
 350   * Adds a compiled state style rule to a rule list.
 351   *
 352   * @since 7.1.0
 353   *
 354   * @param array       $css_rules   Style rules.
 355   * @param string      $state       Pseudo-state selector.
 356   * @param string|null $selector    Block, feature, or element selector.
 357   * @param array       $style       Style object.
 358   * @param string|null $rules_group Optional CSS grouping rule, e.g. a media query.
 359   */
 360  function wp_add_block_state_style_rule( &$css_rules, $state, $selector, $style, $rules_group = null ) {
 361      if ( empty( $style ) || ! is_array( $style ) ) {
 362          return;
 363      }
 364  
 365      $compiled = wp_style_engine_get_styles(
 366          wp_normalize_state_style_for_css_output( $style )
 367      );
 368  
 369      if ( empty( $compiled['declarations'] ) ) {
 370          return;
 371      }
 372  
 373      $css_rules[] = array(
 374          'state'        => $state,
 375          'selector'     => $selector,
 376          'declarations' => $compiled['declarations'],
 377      );
 378      if ( ! empty( $rules_group ) ) {
 379          $css_rules[ count( $css_rules ) - 1 ]['rules_group'] = $rules_group;
 380      }
 381  }
 382  
 383  /**
 384   * Builds compiled state style rules, preserving the selector each rule targets.
 385   *
 386   * @since 7.1.0
 387   *
 388   * @param array         $state_styles Map of state to style array.
 389   * @param WP_Block_Type $block_type   Block type.
 390   * @param string|null   $rules_group  Optional CSS grouping rule, e.g. a media query.
 391   * @return array[] State style rules.
 392   */
 393  function wp_get_block_state_style_rules( $state_styles, $block_type, $rules_group = null ) {
 394      $css_rules       = array();
 395      $block_selectors = isset( $block_type->selectors ) && is_array( $block_type->selectors )
 396          ? $block_type->selectors
 397          : array();
 398  
 399      foreach ( $state_styles as $state => $state_style ) {
 400          if ( empty( $state_style ) || ! is_array( $state_style ) ) {
 401              continue;
 402          }
 403  
 404          foreach ( wp_get_state_style_groups( $state_style, $block_selectors ) as $group ) {
 405              wp_add_block_state_style_rule(
 406                  $css_rules,
 407                  $state,
 408                  $group['selector'],
 409                  $group['style'],
 410                  $rules_group
 411              );
 412          }
 413      }
 414  
 415      return $css_rules;
 416  }
 417  
 418  /**
 419   * Returns a unique class for a set of state style rules.
 420   *
 421   * @since 7.1.0
 422   *
 423   * @param string $block_name Block name.
 424   * @param array  $css_rules  State style rules.
 425   * @return string Unique class name.
 426   */
 427  function wp_get_block_state_unique_class( $block_name, $css_rules ) {
 428      return 'wp-states-' . substr(
 429          md5(
 430              wp_json_encode(
 431                  array(
 432                      'blockName' => $block_name,
 433                      'rules'     => $css_rules,
 434                  )
 435              )
 436          ),
 437          0,
 438          8
 439      );
 440  }
 441  
 442  /**
 443   * Splits a selector list by top-level commas.
 444   *
 445   * @since 7.1.0
 446   *
 447   * @param string $selector CSS selector list.
 448   * @return string[] Selectors.
 449   */
 450  function wp_split_selector_list( $selector ) {
 451      if ( ! str_contains( $selector, ',' ) ) {
 452          return array( $selector );
 453      }
 454  
 455      $selectors         = array();
 456      $current_selector  = '';
 457      $parentheses_depth = 0;
 458      $selector_length   = strlen( $selector );
 459  
 460      for ( $i = 0; $i < $selector_length; $i++ ) {
 461          $char = $selector[ $i ];
 462  
 463          if ( '(' === $char ) {
 464              ++$parentheses_depth;
 465          } elseif ( ')' === $char && $parentheses_depth > 0 ) {
 466              --$parentheses_depth;
 467          } elseif ( ',' === $char && 0 === $parentheses_depth ) {
 468              $selectors[]      = $current_selector;
 469              $current_selector = '';
 470              continue;
 471          }
 472  
 473          $current_selector .= $char;
 474      }
 475  
 476      $selectors[] = $current_selector;
 477  
 478      return $selectors;
 479  }
 480  
 481  /**
 482   * Builds a scoped selector from a block selector and optional pseudo-state.
 483   *
 484   * @since 7.1.0
 485   *
 486   * @param string      $base_selector  Block-instance scoping selector.
 487   * @param string|null $block_selector Block or feature selector from metadata.
 488   * @param string      $state          Pseudo-state selector.
 489   * @return string Scoped selector.
 490   */
 491  function wp_build_state_selector( $base_selector, $block_selector, $state ) {
 492      if ( ! is_string( $block_selector ) || '' === trim( $block_selector ) ) {
 493          return $base_selector . $state;
 494      }
 495  
 496      $selectors        = wp_split_selector_list( $block_selector );
 497      $scoped_selectors = array();
 498  
 499      foreach ( $selectors as $selector ) {
 500          $selector = trim( $selector );
 501          if ( '' === $selector ) {
 502              continue;
 503          }
 504  
 505          /*
 506           * Replace only the leading block selector part (e.g. class name,
 507           * attribute selector, ID, or tag name) with the block instance selector.
 508           * Preserve anything after that prefix, including modifier classes on the
 509           * same element and combinators without spaces.
 510           */
 511          if ( preg_match( '/^([.#]?[-_a-zA-Z0-9]+|\[[^\]]+\])/', $selector, $matches ) ) {
 512              $scoped_selectors[] = $base_selector . substr( $selector, strlen( $matches[0] ) ) . $state;
 513              continue;
 514          }
 515  
 516          $scoped_selectors[] = $base_selector . $state;
 517      }
 518  
 519      return empty( $scoped_selectors )
 520          ? $base_selector . $state
 521          : implode( ', ', $scoped_selectors );
 522  }
 523  
 524  /**
 525   * Renders per-instance state styles on the frontend.
 526   *
 527   * @since 7.1.0
 528   *
 529   * @param string $block_content The block's rendered HTML.
 530   * @param array  $block         The block data including blockName and attrs.
 531   * @return string Modified block content with injected state styles.
 532   */
 533  function wp_render_block_states_support( $block_content, $block ) {
 534      if ( empty( $block['blockName'] ) || empty( $block_content ) ) {
 535          return $block_content;
 536      }
 537  
 538      $block_name = $block['blockName'];
 539      $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name );
 540      if ( ! $block_type ) {
 541          return $block_content;
 542      }
 543  
 544      $supported_pseudo_states = WP_Theme_JSON::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ?? array();
 545      $style                   = $block['attrs']['style'] ?? array();
 546      $css_rules               = array();
 547  
 548      foreach ( $supported_pseudo_states as $pseudo_state ) {
 549          if ( empty( $style[ $pseudo_state ] ) || ! is_array( $style[ $pseudo_state ] ) ) {
 550              continue;
 551          }
 552  
 553          $css_rules = array_merge(
 554              $css_rules,
 555              wp_get_block_state_style_rules(
 556                  array( $pseudo_state => $style[ $pseudo_state ] ),
 557                  $block_type
 558              )
 559          );
 560      }
 561  
 562      foreach ( WP_Theme_JSON::RESPONSIVE_BREAKPOINTS as $breakpoint => $media_query ) {
 563          if ( empty( $style[ $breakpoint ] ) || ! is_array( $style[ $breakpoint ] ) ) {
 564              continue;
 565          }
 566  
 567          $root_state_style = wp_get_root_state_style(
 568              $style[ $breakpoint ],
 569              array_merge( array( 'elements' ), $supported_pseudo_states )
 570          );
 571  
 572          if ( ! empty( $root_state_style ) ) {
 573              $css_rules = array_merge(
 574                  $css_rules,
 575                  wp_get_block_state_style_rules(
 576                      array( '' => $root_state_style ),
 577                      $block_type,
 578                      $media_query
 579                  )
 580              );
 581          }
 582  
 583          if (
 584              ! empty( $style[ $breakpoint ]['elements'] ) &&
 585              is_array( $style[ $breakpoint ]['elements'] )
 586          ) {
 587              $element_selectors = wp_get_block_state_element_selectors(
 588                  wp_get_block_css_selector( $block_type )
 589              );
 590  
 591              foreach ( $style[ $breakpoint ]['elements'] as $element_name => $element_style ) {
 592                  if (
 593                      empty( $element_style ) ||
 594                      ! is_array( $element_style ) ||
 595                      empty( $element_selectors[ $element_name ] )
 596                  ) {
 597                      continue;
 598                  }
 599  
 600                  $element_pseudo_states = WP_Theme_JSON::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ]
 601                      ?? array();
 602                  $root_element_style    = wp_get_root_state_style(
 603                      $element_style,
 604                      $element_pseudo_states
 605                  );
 606  
 607                  wp_add_block_state_style_rule(
 608                      $css_rules,
 609                      '',
 610                      $element_selectors[ $element_name ],
 611                      $root_element_style,
 612                      $media_query
 613                  );
 614  
 615                  foreach ( $element_pseudo_states as $pseudo_state ) {
 616                      if (
 617                          empty( $element_style[ $pseudo_state ] ) ||
 618                          ! is_array( $element_style[ $pseudo_state ] )
 619                      ) {
 620                          continue;
 621                      }
 622  
 623                      wp_add_block_state_style_rule(
 624                          $css_rules,
 625                          $pseudo_state,
 626                          $element_selectors[ $element_name ],
 627                          $element_style[ $pseudo_state ],
 628                          $media_query
 629                      );
 630                  }
 631              }
 632          }
 633  
 634          foreach ( $supported_pseudo_states as $pseudo_state ) {
 635              if ( empty( $style[ $breakpoint ][ $pseudo_state ] ) || ! is_array( $style[ $breakpoint ][ $pseudo_state ] ) ) {
 636                  continue;
 637              }
 638  
 639              $css_rules = array_merge(
 640                  $css_rules,
 641                  wp_get_block_state_style_rules(
 642                      array( $pseudo_state => $style[ $breakpoint ][ $pseudo_state ] ),
 643                      $block_type,
 644                      $media_query
 645                  )
 646              );
 647          }
 648      }
 649  
 650      if ( empty( $css_rules ) ) {
 651          return $block_content;
 652      }
 653  
 654      $unique_class = wp_get_block_state_unique_class( $block_name, $css_rules );
 655  
 656      /*
 657       * Register each state's CSS rules with the block-supports style engine store.
 658       * The store deduplicates rules by selector — two block instances with identical
 659       * state styles share the same hash class and therefore the same selector,
 660       * so only one CSS rule is emitted. The store is flushed to the page by
 661       * wp_enqueue_stored_styles() rather than injected inline here.
 662       *
 663       * State declarations need !important to apply reliably over inline styles and
 664       * preset utility classes such as .has-accent-3-background-color.
 665       *
 666       * Layout-driven state styles (responsive layout, blockGap, child layout) are
 667       * handled by wp_render_layout_support_flag() so they share a selector with
 668       * the base layout and target the correct (inner) wrapper element.
 669       */
 670      $style_rules = array();
 671      foreach ( $css_rules as $rule ) {
 672          $declarations = $rule['declarations'];
 673          foreach ( $declarations as $property => $value ) {
 674              $declarations[ $property ] = is_string( $value ) && str_contains( $value, '!important' )
 675                  ? $value
 676                  : $value . ' !important';
 677          }
 678          $declarations = wp_get_state_declarations_with_fallback_border_styles( $declarations );
 679          $declarations = wp_get_state_declarations_with_background_resets( $declarations );
 680          $style_rule   = array(
 681              'selector'     => wp_build_state_selector(
 682                  ".$unique_class",
 683                  $rule['selector'],
 684                  $rule['state']
 685              ),
 686              'declarations' => $declarations,
 687          );
 688          if ( ! empty( $rule['rules_group'] ) ) {
 689              $style_rule['rules_group'] = $rule['rules_group'];
 690          }
 691          $style_rules[] = $style_rule;
 692      }
 693  
 694      wp_style_engine_get_stylesheet_from_css_rules(
 695          $style_rules,
 696          array(
 697              'context'  => 'block-supports',
 698              'prettify' => false,
 699          )
 700      );
 701  
 702      $processor = new WP_HTML_Tag_Processor( $block_content );
 703      if ( $processor->next_tag() ) {
 704          $processor->add_class( $unique_class );
 705      }
 706      return $processor->get_updated_html();
 707  }
 708  add_filter( 'render_block', 'wp_render_block_states_support', 10, 2 );


Generated : Fri Jul 3 08:20:12 2026 Cross-referenced by PHPXref