diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index fbbf9a05d1c81..6f72ef2c403ce 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -795,7 +795,7 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n if ( empty( $result ) ) { unset( $output[ $subtree ] ); } else { - $output[ $subtree ] = $result; + $output[ $subtree ] = static::resolve_custom_css_format( $result ); } } @@ -1989,20 +1989,6 @@ protected static function get_property_value( $styles, $path, $theme_json = null return $value; } - // Convert custom CSS properties. - $prefix = 'var:'; - $prefix_len = strlen( $prefix ); - $token_in = '|'; - $token_out = '--'; - if ( 0 === strncmp( $value, $prefix, $prefix_len ) ) { - $unwrapped_name = str_replace( - $token_in, - $token_out, - substr( $value, $prefix_len ) - ); - $value = "var(--wp--$unwrapped_name)"; - } - return $value; } @@ -3578,4 +3564,51 @@ protected function get_feature_declarations_for_node( $metadata, &$node ) { return $declarations; } + + /** + * This is used to convert the internal representation of variables to the CSS representation. + * For example, `var:preset|color|vivid-green-cyan` becomes `var(--wp--preset--color--vivid-green-cyan)`. + * + * @since 6.3.0 + * @param string $value The variable such as var:preset|color|vivid-green-cyan to convert. + * @return string The converted variable. + */ + private static function convert_custom_properties( $value ) { + $prefix = 'var:'; + $prefix_len = strlen( $prefix ); + $token_in = '|'; + $token_out = '--'; + if ( 0 === strpos( $value, $prefix ) ) { + $unwrapped_name = str_replace( + $token_in, + $token_out, + substr( $value, $prefix_len ) + ); + $value = "var(--wp--$unwrapped_name)"; + } + + return $value; + } + + /** + * Given a tree, converts the internal representation of variables to the CSS representation. + * It is recursive and modifies the input in-place. + * + * @since 6.3.0 + * @param array $tree Input to process. + * @return array The modified $tree. + */ + private static function resolve_custom_css_format( $tree ) { + $prefix = 'var:'; + + foreach ( $tree as $key => $data ) { + if ( is_string( $data ) && 0 === strpos( $data, $prefix ) ) { + $tree[ $key ] = self::convert_custom_properties( $data ); + } elseif ( is_array( $data ) ) { + $tree[ $key ] = self::resolve_custom_css_format( $data ); + } + } + + return $tree; + } } diff --git a/lib/compat/wordpress-6.3/get-global-styles-and-settings.php b/lib/compat/wordpress-6.3/get-global-styles-and-settings.php index bb489664e1eea..609d9b3de38a1 100644 --- a/lib/compat/wordpress-6.3/get-global-styles-and-settings.php +++ b/lib/compat/wordpress-6.3/get-global-styles-and-settings.php @@ -124,3 +124,37 @@ function wp_get_block_css_selector( $block_type, $target = 'root', $fallback = f function gutenberg_get_remote_theme_patterns() { return WP_Theme_JSON_Resolver_Gutenberg::get_theme_data( array(), array( 'with_supports' => false ) )->get_patterns(); } + +/** + * Gets the styles resulting of merging core, theme, and user data. + * + * @since 5.9.0 + * @since 6.3.0 the internal link format "var:preset|color|secondary" is resolved + * to "var(--wp--preset--font-size--small)" so consumers don't have to. + * + * @param array $path Path to the specific style to retrieve. Optional. + * If empty, will return all styles. + * @param array $context { + * Metadata to know where to retrieve the $path from. Optional. + * + * @type string $block_name Which block to retrieve the styles from. + * If empty, it'll return the styles for the global context. + * @type string $origin Which origin to take data from. + * Valid values are 'all' (core, theme, and user) or 'base' (core and theme). + * If empty or unknown, 'all' is used. + * } + * @return array The styles to retrieve. + */ +function gutenberg_get_global_styles( $path = array(), $context = array() ) { + if ( ! empty( $context['block_name'] ) ) { + $path = array_merge( array( 'blocks', $context['block_name'] ), $path ); + } + + $origin = 'custom'; + if ( isset( $context['origin'] ) && 'base' === $context['origin'] ) { + $origin = 'theme'; + } + $styles = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $origin )->get_raw_data()['styles']; + + return _wp_array_get( $styles, $path, $styles ); +} diff --git a/lib/experimental/block-editor-settings-mobile.php b/lib/experimental/block-editor-settings-mobile.php index b9d1147d60f96..91e3694c199f8 100644 --- a/lib/experimental/block-editor-settings-mobile.php +++ b/lib/experimental/block-editor-settings-mobile.php @@ -23,7 +23,7 @@ function gutenberg_get_block_editor_settings_mobile( $settings ) { 'mobile' === $_GET['context'] ) { if ( wp_theme_has_theme_json() ) { - $settings['__experimentalStyles'] = wp_get_global_styles(); + $settings['__experimentalStyles'] = gutenberg_get_global_styles(); } // To tell mobile that the site uses quote v2 (inner blocks). diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index d7a28e2d6e238..7d63f62a2c1f0 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -2071,4 +2071,69 @@ public function data_process_blocks_custom_css() { ), ); } + + public function test_internal_syntax_is_converted_to_css_variables() { + $result = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'color' => array( + 'background' => 'var:preset|color|primary', + 'text' => 'var(--wp--preset--color--secondary)', + ), + 'elements' => array( + 'link' => array( + 'color' => array( + 'background' => 'var:preset|color|pri', + 'text' => 'var(--wp--preset--color--sec)', + ), + ), + ), + 'blocks' => array( + 'core/post-terms' => array( + 'typography' => array( 'fontSize' => 'var(--wp--preset--font-size--small)' ), + 'color' => array( 'background' => 'var:preset|color|secondary' ), + ), + 'core/navigation' => array( + 'elements' => array( + 'link' => array( + 'color' => array( + 'background' => 'var:preset|color|p', + 'text' => 'var(--wp--preset--color--s)', + ), + ), + ), + ), + 'core/quote' => array( + 'typography' => array( 'fontSize' => 'var(--wp--preset--font-size--d)' ), + 'color' => array( 'background' => 'var:preset|color|d' ), + 'variations' => array( + 'plain' => array( + 'typography' => array( 'fontSize' => 'var(--wp--preset--font-size--s)' ), + 'color' => array( 'background' => 'var:preset|color|s' ), + ), + ), + ), + ), + ), + ) + ); + $styles = $result->get_raw_data()['styles']; + + $this->assertEquals( 'var(--wp--preset--color--primary)', $styles['color']['background'], 'Top level: Assert the originally correct values are still correct.' ); + $this->assertEquals( 'var(--wp--preset--color--secondary)', $styles['color']['text'], 'Top level: Assert the originally correct values are still correct.' ); + + $this->assertEquals( 'var(--wp--preset--color--pri)', $styles['elements']['link']['color']['background'], 'Element top level: Assert the originally correct values are still correct.' ); + $this->assertEquals( 'var(--wp--preset--color--sec)', $styles['elements']['link']['color']['text'], 'Element top level: Assert the originally correct values are still correct.' ); + + $this->assertEquals( 'var(--wp--preset--font-size--small)', $styles['blocks']['core/post-terms']['typography']['fontSize'], 'Top block level: Assert the originally correct values are still correct.' ); + $this->assertEquals( 'var(--wp--preset--color--secondary)', $styles['blocks']['core/post-terms']['color']['background'], 'Top block level: Assert the internal variables are convert to CSS custom variables.' ); + + $this->assertEquals( 'var(--wp--preset--color--p)', $styles['blocks']['core/navigation']['elements']['link']['color']['background'], 'Elements block level: Assert the originally correct values are still correct.' ); + $this->assertEquals( 'var(--wp--preset--color--s)', $styles['blocks']['core/navigation']['elements']['link']['color']['text'], 'Elements block level: Assert the originally correct values are still correct.' ); + + $this->assertEquals( 'var(--wp--preset--font-size--s)', $styles['blocks']['core/quote']['variations']['plain']['typography']['fontSize'], 'Style variations: Assert the originally correct values are still correct.' ); + $this->assertEquals( 'var(--wp--preset--color--s)', $styles['blocks']['core/quote']['variations']['plain']['color']['background'], 'Style variations: Assert the internal variables are convert to CSS custom variables.' ); + + } }