| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Thu Aug 20 08:20:25 2026 | Cross-referenced by PHPXref |