Commit f0f8edd1b9 for wordpress.org

commit f0f8edd1b9f3c3a49bf0323d69c25265ac510ebd
Author: ramonopoly <ramonopoly@git.wordpress.org>
Date:   Thu Oct 22 00:57:57 2026 +0000

    Editor: Generate custom CSS for blocks rendered by their parent block

    This commit generates per-block custom, "Additional CSS" for blocks that a parent block parses and renders itself, such as the links in a navigation menu.

    Previously, the frontend did not render generate CSS for these blocks, the major case being Navigation Link blocks.

    Developed in: https://github.com/WordPress/wordpress-develop/pull/14538

    Props mikachan, mrleemon, scruffian.

    Fixes #66345.


    Built from https://develop.svn.wordpress.org/trunk@64676


    git-svn-id: http://core.svn.wordpress.org/trunk@63826 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/block-supports/custom-css.php b/wp-includes/block-supports/custom-css.php
index f5318d075f..1c9a6a95d0 100644
--- a/wp-includes/block-supports/custom-css.php
+++ b/wp-includes/block-supports/custom-css.php
@@ -124,12 +124,13 @@ function wp_process_custom_css_state_entries( $state_entries, $selector ) {
 }

 /**
- * Render the custom CSS stylesheet and add class name to block as required.
+ * Generates the custom CSS class name for a block and registers its stylesheet.
  *
- * @since 7.0.0
+ * @since 7.2.0
+ * @access private
  *
  * @param array $parsed_block The parsed block.
- * @return array The same parsed block with custom CSS class name added if appropriate.
+ * @return string|null The generated class name, or null if the block has no custom CSS to render.
  *
  * @phpstan-param array{
  *     blockName: string|null,
@@ -143,51 +144,32 @@ function wp_process_custom_css_state_entries( $state_entries, $selector ) {
  *     },
  *     ...
  * } $parsed_block
- * @phpstan-return array{
- *     blockName: string|null,
- *     attrs: array{
- *         className?: string,
- *         style?: array{
- *             css?: string,
- *             ...
- *         },
- *         ...
- *     },
- *     ...
- * }
  */
-function wp_render_custom_css_support_styles( $parsed_block ) {
+function wp_generate_custom_css_class_name( $parsed_block ) {
 	$style = $parsed_block['attrs']['style'] ?? null;
 	if ( empty( $style ) || ! is_array( $style ) ) {
-		return $parsed_block;
+		return null;
 	}

 	$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );
 	if ( ! block_has_support( $block_type, 'customCSS', true ) ) {
-		return $parsed_block;
+		return null;
 	}

 	$viewport_settings        = wp_get_global_settings( array( 'viewport' ) );
 	$responsive_media_queries = WP_Theme_JSON::get_viewport_media_queries( $viewport_settings );
 	$state_entries            = wp_get_custom_css_state_entries( $style, $parsed_block['blockName'], $responsive_media_queries );
 	if ( empty( $state_entries ) ) {
-		return $parsed_block;
+		return null;
 	}

 	// Generate a unique class name for this block instance.
 	$class_name    = wp_unique_id_from_values( $parsed_block, 'wp-custom-css-' );
 	$processed_css = wp_process_custom_css_state_entries( $state_entries, '.' . $class_name );
 	if ( '' === $processed_css ) {
-		return $parsed_block;
+		return null;
 	}

-	$existing_class_name = $parsed_block['attrs']['className'] ?? null;
-	$updated_class_name  = is_string( $existing_class_name )
-		? "$existing_class_name $class_name"
-		: $class_name;
-
-	$parsed_block['attrs']['className'] = $updated_class_name;
-
 	/**
 	 * Reuse one handle so identical custom CSS is enqueued only once via
 	 * {@see wp_unique_id_from_values()}. Explicitly declare the `wp-block-library`
@@ -206,6 +188,55 @@ function wp_render_custom_css_support_styles( $parsed_block ) {
 		wp_add_inline_style( $handle, $processed_css );
 	}

+	return $class_name;
+}
+
+/**
+ * Render the custom CSS stylesheet and add class name to block as required.
+ *
+ * @since 7.0.0
+ *
+ * @param array $parsed_block The parsed block.
+ * @return array The same parsed block with custom CSS class name added if appropriate.
+ *
+ * @phpstan-param array{
+ *     blockName: string|null,
+ *     attrs: array{
+ *         className?: string,
+ *         style?: array{
+ *             css?: string,
+ *             ...
+ *         },
+ *         ...
+ *     },
+ *     ...
+ * } $parsed_block
+ * @phpstan-return array{
+ *     blockName: string|null,
+ *     attrs: array{
+ *         className?: string,
+ *         style?: array{
+ *             css?: string,
+ *             ...
+ *         },
+ *         ...
+ *     },
+ *     ...
+ * }
+ */
+function wp_render_custom_css_support_styles( $parsed_block ) {
+	$class_name = wp_generate_custom_css_class_name( $parsed_block );
+	if ( null === $class_name ) {
+		return $parsed_block;
+	}
+
+	$existing_class_name = $parsed_block['attrs']['className'] ?? null;
+	$updated_class_name  = is_string( $existing_class_name )
+		? "$existing_class_name $class_name"
+		: $class_name;
+
+	$parsed_block['attrs']['className'] = $updated_class_name;
+
 	return $parsed_block;
 }

@@ -221,41 +252,54 @@ function wp_enqueue_block_custom_css() {
 /**
  * Applies the custom CSS class name to the block's rendered HTML.
  *
- * The class name is generated in {@see wp_render_custom_css_support_styles()}
- * and stored in block attributes. This filter adds it to the actual markup.
+ * Uses the class name that {@see wp_render_custom_css_support_styles()}
+ * stored in the block attributes. Blocks that a parent parsed and rendered
+ * itself never reach that filter, so the class name is generated here instead
+ * via {@see wp_generate_custom_css_class_name()}.
  *
  * @since 7.0.0
+ * @since 7.2.0 Generates the class name when the block attributes lack one.
  *
  * @param string $block_content Rendered block content.
  * @param array  $block         Block object.
  * @return string Filtered block content.
  *
  * @phpstan-param array{
+ *     blockName: string|null,
  *     attrs: array{
  *         className?: string,
+ *         style?: array{
+ *             css?: string,
+ *             ...
+ *         },
  *         ...
  *     },
  *     ...
  * } $block
  */
 function wp_render_custom_css_class_name( $block_content, $block ) {
-	$class_name_attr   = $block['attrs']['className'] ?? null;
-	$class_name_prefix = 'wp-custom-css-';
-	if ( ! is_string( $class_name_attr ) || ! str_contains( $class_name_attr, $class_name_prefix ) ) {
-		return $block_content;
-	}
+	$class_name_attr    = $block['attrs']['className'] ?? null;
+	$class_name_prefix  = 'wp-custom-css-';
+	$matched_class_name = null;

 	// Parse out the 'wp-custom-css-*' class name added by wp_render_custom_css_support_styles().
-	$matched_class_name = null;
-	$token_delimiter    = " \t\f\r\n";
-	$class_token        = strtok( $class_name_attr, $token_delimiter );
-	while ( false !== $class_token ) {
-		if ( str_starts_with( $class_token, $class_name_prefix ) ) {
-			$matched_class_name = $class_token;
-			break;
+	if ( is_string( $class_name_attr ) && str_contains( $class_name_attr, $class_name_prefix ) ) {
+		$token_delimiter = " \t\f\r\n";
+		$class_token     = strtok( $class_name_attr, $token_delimiter );
+		while ( false !== $class_token ) {
+			if ( str_starts_with( $class_token, $class_name_prefix ) ) {
+				$matched_class_name = $class_token;
+				break;
+			}
+			$class_token = strtok( $token_delimiter );
 		}
-		$class_token = strtok( $token_delimiter );
 	}
+
+	// Direct WP_Block::render() calls skip render_block_data for the block itself.
+	if ( null === $matched_class_name ) {
+		$matched_class_name = wp_generate_custom_css_class_name( $block );
+	}
+
 	if ( null === $matched_class_name ) {
 		return $block_content;
 	}
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 41919f67b8..80011fc421 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-beta1-64675';
+$wp_version = '7.2-beta1-64676';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.