From 18200cc30ca695af37b434358b51d23ccf1cc33c Mon Sep 17 00:00:00 2001 From: jorgefilipecosta Date: Mon, 24 May 2021 17:39:57 +0000 Subject: [PATCH] Block Editor: Add Global Styles support using theme.json file. This is the second piece of landing the theme.json processing in WordPress core. It includes the mechanism that outputs the CSS styles of a theme.json file. Props nosolosw, youknowriad. See #53175. Built from https://develop.svn.wordpress.org/trunk@50973 git-svn-id: http://core.svn.wordpress.org/trunk@50582 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/block-editor.php | 11 +- wp-includes/class-wp-theme-json.php | 837 ++++++++++++++++++++++++++-- wp-includes/default-filters.php | 1 + wp-includes/script-loader.php | 46 ++ wp-includes/version.php | 2 +- 5 files changed, 856 insertions(+), 41 deletions(-) diff --git a/wp-includes/block-editor.php b/wp-includes/block-editor.php index 542fc99d2c..a1b6b10cc5 100644 --- a/wp-includes/block-editor.php +++ b/wp-includes/block-editor.php @@ -243,8 +243,17 @@ function get_block_editor_settings( $editor_name, $custom_settings = array() ) { $custom_settings ); - $editor_settings['__experimentalFeatures'] = WP_Theme_JSON_Resolver::get_merged_data( $editor_settings )->get_settings(); + $theme_json = WP_Theme_JSON_Resolver::get_merged_data( $editor_settings ); + if ( WP_Theme_JSON_Resolver::theme_has_support() ) { + $editor_settings['styles'][] = array( 'css' => $theme_json->get_stylesheet( 'block_styles' ) ); + $editor_settings['styles'][] = array( + 'css' => $theme_json->get_stylesheet( 'css_variables' ), + '__experimentalNoWrapper' => true, + ); + } + + $editor_settings['__experimentalFeatures'] = $theme_json->get_settings(); // These settings may need to be updated based on data coming from theme.json sources. if ( isset( $editor_settings['__experimentalFeatures']['color']['palette'] ) ) { $editor_settings['colors'] = $editor_settings['__experimentalFeatures']['color']['palette']; diff --git a/wp-includes/class-wp-theme-json.php b/wp-includes/class-wp-theme-json.php index 0cb5a845c5..983196d696 100644 --- a/wp-includes/class-wp-theme-json.php +++ b/wp-includes/class-wp-theme-json.php @@ -23,19 +23,149 @@ class WP_Theme_JSON { private $theme_json = null; /** - * Holds the allowed block names extracted from block.json. - * Shared among all instances so we only process it once. + * Holds block metadata extracted from block.json + * to be shared among all instances so we don't + * process it twice. * * @since 5.8.0 * @var array */ - private static $allowed_block_names = null; + private static $blocks_metadata = null; - const ALLOWED_TOP_LEVEL_KEYS = array( - 'version', - 'settings', + /** + * The CSS selector for the top-level styles. + * + * @since 5.8.0 + * @var string + */ + const ROOT_BLOCK_SELECTOR = 'body'; + + /** + * Presets are a set of values that serve + * to bootstrap some styles: colors, font sizes, etc. + * + * They are a unkeyed array of values such as: + * + * ```php + * array( + * array( + * 'slug' => 'unique-name-within-the-set', + * 'name' => 'Name for the UI', + * => 'value' + * ), + * ) + * ``` + * + * This contains the necessary metadata to process them: + * + * - path => where to find the preset within the settings section + * + * - value_key => the key that represents the value + * + * - css_var_infix => infix to use in generating the CSS Custom Property. Example: + * --wp--preset----: + * + * - classes => array containing a structure with the classes to + * generate for the presets. Each class should have + * the class suffix and the property name. Example: + * + * .has-- { + * : + * } + * + * @since 5.8.0 + * @var array + */ + const PRESETS_METADATA = array( + array( + 'path' => array( 'color', 'palette' ), + 'value_key' => 'color', + 'css_var_infix' => 'color', + 'classes' => array( + array( + 'class_suffix' => 'color', + 'property_name' => 'color', + ), + array( + 'class_suffix' => 'background-color', + 'property_name' => 'background-color', + ), + ), + ), + array( + 'path' => array( 'color', 'gradients' ), + 'value_key' => 'gradient', + 'css_var_infix' => 'gradient', + 'classes' => array( + array( + 'class_suffix' => 'gradient-background', + 'property_name' => 'background', + ), + ), + ), + array( + 'path' => array( 'typography', 'fontSizes' ), + 'value_key' => 'size', + 'css_var_infix' => 'font-size', + 'classes' => array( + array( + 'class_suffix' => 'font-size', + 'property_name' => 'font-size', + ), + ), + ), ); + /** + * Metadata for style properties. + * + * Each property declares: + * + * - 'value': path to the value in theme.json and block attributes. + * + * @since 5.8.0 + * @var array + */ + const PROPERTIES_METADATA = array( + 'background' => array( + 'value' => array( 'color', 'gradient' ), + ), + 'background-color' => array( + 'value' => array( 'color', 'background' ), + ), + 'color' => array( + 'value' => array( 'color', 'text' ), + ), + 'font-size' => array( + 'value' => array( 'typography', 'fontSize' ), + ), + 'line-height' => array( + 'value' => array( 'typography', 'lineHeight' ), + ), + 'margin' => array( + 'value' => array( 'spacing', 'margin' ), + 'properties' => array( 'top', 'right', 'bottom', 'left' ), + ), + 'padding' => array( + 'value' => array( 'spacing', 'padding' ), + 'properties' => array( 'top', 'right', 'bottom', 'left' ), + ), + ); + + /** + * @since 5.8.0 + * @var array + */ + const ALLOWED_TOP_LEVEL_KEYS = array( + 'settings', + 'styles', + 'version', + ); + + /** + * @since 5.8.0 + * @var array + */ const ALLOWED_SETTINGS = array( 'color' => array( 'custom' => null, @@ -60,6 +190,54 @@ class WP_Theme_JSON { ), ); + /** + * @since 5.8.0 + * @var array + */ + const ALLOWED_STYLES = array( + 'color' => array( + 'background' => null, + 'gradient' => null, + 'text' => null, + ), + 'spacing' => array( + 'margin' => array( + 'top' => null, + 'right' => null, + 'bottom' => null, + 'left' => null, + ), + 'padding' => array( + 'bottom' => null, + 'left' => null, + 'right' => null, + 'top' => null, + ), + ), + 'typography' => array( + 'fontSize' => null, + 'lineHeight' => null, + ), + ); + + /** + * @since 5.8.0 + * @var array + */ + const ELEMENTS = array( + 'link' => 'a', + 'h1' => 'h1', + 'h2' => 'h2', + 'h3' => 'h3', + 'h4' => 'h4', + 'h5' => 'h5', + 'h6' => 'h6', + ); + + /** + * @since 5.8.0 + * @var int + */ const LATEST_SCHEMA = 1; /** @@ -78,23 +256,6 @@ class WP_Theme_JSON { $this->theme_json = self::sanitize( $theme_json ); } - /** - * Returns the allowed block names. - * - * @since 5.8.0 - * - * @return array - */ - private static function get_allowed_block_names() { - if ( null !== self::$allowed_block_names ) { - return self::$allowed_block_names; - } - - self::$allowed_block_names = array_keys( WP_Block_Type_Registry::get_instance()->get_all_registered() ); - - return self::$allowed_block_names; - } - /** * Sanitizes the input according to the schemas. * @@ -110,21 +271,35 @@ class WP_Theme_JSON { return $output; } - $allowed_blocks = self::get_allowed_block_names(); + $allowed_top_level_keys = self::ALLOWED_TOP_LEVEL_KEYS; + $allowed_settings = self::ALLOWED_SETTINGS; + $allowed_styles = self::ALLOWED_STYLES; + $allowed_blocks = array_keys( self::get_blocks_metadata() ); + $allowed_elements = array_keys( self::ELEMENTS ); - $output = array_intersect_key( $input, array_flip( self::ALLOWED_TOP_LEVEL_KEYS ) ); + $output = array_intersect_key( $input, array_flip( $allowed_top_level_keys ) ); // Build the schema. $schema = array(); + $schema_styles_elements = array(); + foreach ( $allowed_elements as $element ) { + $schema_styles_elements[ $element ] = $allowed_styles; + } + $schema_styles_blocks = array(); $schema_settings_blocks = array(); foreach ( $allowed_blocks as $block ) { - $schema_settings_blocks[ $block ] = self::ALLOWED_SETTINGS; + $schema_settings_blocks[ $block ] = $allowed_settings; + $schema_styles_blocks[ $block ] = $allowed_styles; + $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; } - $schema['settings'] = self::ALLOWED_SETTINGS; + $schema['styles'] = $allowed_styles; + $schema['styles']['blocks'] = $schema_styles_blocks; + $schema['styles']['elements'] = $schema_styles_elements; + $schema['settings'] = $allowed_settings; $schema['settings']['blocks'] = $schema_settings_blocks; // Remove anything that's not present in the schema. - foreach ( array( 'settings' ) as $subtree ) { + foreach ( array( 'styles', 'settings' ) as $subtree ) { if ( ! isset( $input[ $subtree ] ) ) { continue; } @@ -146,6 +321,68 @@ class WP_Theme_JSON { return $output; } + /** + * Returns the metadata for each block. + * + * Example: + * + * { + * 'core/paragraph': { + * 'selector': 'p', + * 'elements': { + * 'link' => 'link selector', + * 'etc' => 'element selector' + * } + * }, + * 'core/heading': { + * 'selector': 'h1', + * 'elements': {} + * } + * 'core/group': { + * 'selector': '.wp-block-group', + * 'elements': {} + * } + * } + * + * @since 5.8.0 + * + * @return array Block metadata. + */ + private static function get_blocks_metadata() { + if ( null !== self::$blocks_metadata ) { + return self::$blocks_metadata; + } + + self::$blocks_metadata = array(); + + $registry = WP_Block_Type_Registry::get_instance(); + $blocks = $registry->get_all_registered(); + foreach ( $blocks as $block_name => $block_type ) { + if ( + isset( $block_type->supports['__experimentalSelector'] ) && + is_string( $block_type->supports['__experimentalSelector'] ) + ) { + self::$blocks_metadata[ $block_name ]['selector'] = $block_type->supports['__experimentalSelector']; + } else { + self::$blocks_metadata[ $block_name ]['selector'] = '.wp-block-' . str_replace( '/', '-', str_replace( 'core/', '', $block_name ) ); + } + + // Assign defaults, then overwrite those that the block sets by itself. + // If the block selector is compounded, will append the element to each + // individual block selector. + $block_selectors = explode( ',', self::$blocks_metadata[ $block_name ]['selector'] ); + foreach ( self::ELEMENTS as $el_name => $el_selector ) { + $element_selector = array(); + foreach ( $block_selectors as $selector ) { + $element_selector[] = $selector . ' ' . $el_selector; + } + self::$blocks_metadata[ $block_name ]['elements'][ $el_name ] = implode( ',', $element_selector ); + } + } + + return self::$blocks_metadata; + } + /** * Given a tree, removes the keys that are not present in the schema. * @@ -155,6 +392,7 @@ class WP_Theme_JSON { * * @param array $tree Input to process. * @param array $schema Schema to adhere to. + * * @return array Returns the modified $tree. */ private static function remove_keys_not_in_schema( $tree, $schema ) { @@ -209,24 +447,466 @@ class WP_Theme_JSON { } } + /** + * Returns the stylesheet that results of processing + * the theme.json structure this object represents. + * + * @since 5.8.0 + * + * @param string $type Type of stylesheet we want accepts 'all', 'block_styles', and 'css_variables'. + * + * @return string Stylesheet. + */ + public function get_stylesheet( $type = 'all' ) { + $blocks_metadata = self::get_blocks_metadata(); + $style_nodes = self::get_style_nodes( $this->theme_json, $blocks_metadata ); + $setting_nodes = self::get_setting_nodes( $this->theme_json, $blocks_metadata ); + + switch ( $type ) { + case 'block_styles': + return $this->get_block_styles( $style_nodes, $setting_nodes ); + case 'css_variables': + return $this->get_css_variables( $setting_nodes ); + default: + return $this->get_css_variables( $setting_nodes ) . $this->get_block_styles( $style_nodes, $setting_nodes ); + } + + } + + /** + * Converts each style section into a list of rulesets + * containing the block styles to be appended to the stylesheet. + * + * See glossary at https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax + * + * For each section this creates a new ruleset such as: + * + * block-selector { + * style-property-one: value; + * } + * + * Additionally, it'll also create new rulesets + * as classes for each preset value such as: + * + * .has-value-color { + * color: value; + * } + * + * .has-value-background-color { + * background-color: value; + * } + * + * .has-value-font-size { + * font-size: value; + * } + * + * .has-value-gradient-background { + * background: value; + * } + * + * p.has-value-gradient-background { + * background: value; + * } + * + * @since 5.8.0 + * + * @param array $style_nodes Nodes with styles. + * @param array $setting_nodes Nodes with settings. + * + * @return string The new stylesheet. + */ + private function get_block_styles( $style_nodes, $setting_nodes ) { + $block_rules = ''; + foreach ( $style_nodes as $metadata ) { + if ( null === $metadata['selector'] ) { + continue; + } + + $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); + $selector = $metadata['selector']; + $declarations = self::compute_style_properties( $node ); + $block_rules .= self::to_ruleset( $selector, $declarations ); + } + + $preset_rules = ''; + foreach ( $setting_nodes as $metadata ) { + if ( null === $metadata['selector'] ) { + continue; + } + + $selector = $metadata['selector']; + $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); + $preset_rules .= self::compute_preset_classes( $node, $selector ); + } + + return $block_rules . $preset_rules; + } + + /** + * Converts each styles section into a list of rulesets + * to be appended to the stylesheet. + * These rulesets contain all the css variables (custom variables and preset variables). + * + * See glossary at https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax + * + * For each section this creates a new ruleset such as: + * + * block-selector { + * --wp--preset--category--slug: value; + * --wp--custom--variable: value; + * } + * + * @since 5.8.0 + * + * @param array $nodes Nodes with settings. + * + * @return string The new stylesheet. + */ + private function get_css_variables( $nodes ) { + $stylesheet = ''; + foreach ( $nodes as $metadata ) { + if ( null === $metadata['selector'] ) { + continue; + } + + $selector = $metadata['selector']; + + $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); + $declarations = array_merge( self::compute_preset_vars( $node ), self::compute_theme_vars( $node ) ); + + $stylesheet .= self::to_ruleset( $selector, $declarations ); + } + + return $stylesheet; + } + + /** + * Given a selector and a declaration list, + * creates the corresponding ruleset. + * + * To help debugging, will add some space + * if SCRIPT_DEBUG is defined and true. + * + * @since 5.8.0 + * + * @param string $selector CSS selector. + * @param array $declarations List of declarations. + * + * @return string CSS ruleset. + */ + private static function to_ruleset( $selector, $declarations ) { + if ( empty( $declarations ) ) { + return ''; + } + + $declaration_block = array_reduce( + $declarations, + function ( $carry, $element ) { + return $carry .= $element['name'] . ': ' . $element['value'] . ';'; }, + '' + ); + + return $selector . '{' . $declaration_block . '}'; + } + + /** + * Given a settings array, it returns the generated rulesets + * for the preset classes. + * + * @since 5.8.0 + * + * @param array $settings Settings to process. + * @param string $selector Selector wrapping the classes. + * + * @return string The result of processing the presets. + */ + private static function compute_preset_classes( $settings, $selector ) { + if ( self::ROOT_BLOCK_SELECTOR === $selector ) { + // Classes at the global level do not need any CSS prefixed, + // and we don't want to increase its specificity. + $selector = ''; + } + + $stylesheet = ''; + foreach ( self::PRESETS_METADATA as $preset ) { + $values = _wp_array_get( $settings, $preset['path'], array() ); + foreach ( $values as $value ) { + foreach ( $preset['classes'] as $class ) { + $stylesheet .= self::to_ruleset( + $selector . '.has-' . $value['slug'] . '-' . $class['class_suffix'], + array( + array( + 'name' => $class['property_name'], + 'value' => $value[ $preset['value_key'] ] . ' !important', + ), + ) + ); + } + } + } + + return $stylesheet; + } + + /** + * Given the block settings, it extracts the CSS Custom Properties + * for the presets and adds them to the $declarations array + * following the format: + * + * ```php + * array( + * 'name' => 'property_name', + * 'value' => 'property_value, + * ) + * ``` + * + * @since 5.8.0 + * + * @param array $settings Settings to process. + * + * @return array Returns the modified $declarations. + */ + private static function compute_preset_vars( $settings ) { + $declarations = array(); + foreach ( self::PRESETS_METADATA as $preset ) { + $values = _wp_array_get( $settings, $preset['path'], array() ); + foreach ( $values as $value ) { + $declarations[] = array( + 'name' => '--wp--preset--' . $preset['css_var_infix'] . '--' . $value['slug'], + 'value' => $value[ $preset['value_key'] ], + ); + } + } + + return $declarations; + } + + /** + * Given an array of settings, it extracts the CSS Custom Properties + * for the custom values and adds them to the $declarations + * array following the format: + * + * ```php + * array( + * 'name' => 'property_name', + * 'value' => 'property_value, + * ) + * ``` + * + * @since 5.8.0 + * + * @param array $settings Settings to process. + * + * @return array Returns the modified $declarations. + */ + private static function compute_theme_vars( $settings ) { + $declarations = array(); + $custom_values = _wp_array_get( $settings, array( 'custom' ), array() ); + $css_vars = self::flatten_tree( $custom_values ); + foreach ( $css_vars as $key => $value ) { + $declarations[] = array( + 'name' => '--wp--custom--' . $key, + 'value' => $value, + ); + } + + return $declarations; + } + + /** + * Given a tree, it creates a flattened one + * by merging the keys and binding the leaf values + * to the new keys. + * + * It also transforms camelCase names into kebab-case + * and substitutes '/' by '-'. + * + * This is thought to be useful to generate + * CSS Custom Properties from a tree, + * although there's nothing in the implementation + * of this function that requires that format. + * + * For example, assuming the given prefix is '--wp' + * and the token is '--', for this input tree: + * + * { + * 'some/property': 'value', + * 'nestedProperty': { + * 'sub-property': 'value' + * } + * } + * + * it'll return this output: + * + * { + * '--wp--some-property': 'value', + * '--wp--nested-property--sub-property': 'value' + * } + * + * @since 5.8.0 + * + * @param array $tree Input tree to process. + * @param string $prefix Prefix to prepend to each variable. '' by default. + * @param string $token Token to use between levels. '--' by default. + * + * @return array The flattened tree. + */ + private static function flatten_tree( $tree, $prefix = '', $token = '--' ) { + $result = array(); + foreach ( $tree as $property => $value ) { + $new_key = $prefix . str_replace( + '/', + '-', + strtolower( preg_replace( '/(? 'property_name', + * 'value' => 'property_value, + * ) + * ``` + * + * @since 5.8.0 + * + * @param array $styles Styles to process. + * + * @return array Returns the modified $declarations. + */ + private static function compute_style_properties( $styles ) { + $declarations = array(); + if ( empty( $styles ) ) { + return $declarations; + } + + $properties = array(); + foreach ( self::PROPERTIES_METADATA as $name => $metadata ) { + // Some properties can be shorthand properties, meaning that + // they contain multiple values instead of a single one. + // An example of this is the padding property. + if ( self::has_properties( $metadata ) ) { + foreach ( $metadata['properties'] as $property ) { + $properties[] = array( + 'name' => $name . '-' . $property, + 'value' => array_merge( $metadata['value'], array( $property ) ), + ); + } + } else { + $properties[] = array( + 'name' => $name, + 'value' => $metadata['value'], + ); + } + } + + foreach ( $properties as $prop ) { + $value = self::get_property_value( $styles, $prop['value'] ); + if ( empty( $value ) ) { + continue; + } + + $declarations[] = array( + 'name' => $prop['name'], + 'value' => $value, + ); + } + + return $declarations; + } + + /** + * Whether the metadata contains a key named properties. + * + * @since 5.8.0 + * + * @param array $metadata Description of the style property. + * + * @return boolean True if properties exists, false otherwise. + */ + private static function has_properties( $metadata ) { + if ( array_key_exists( 'properties', $metadata ) ) { + return true; + } + + return false; + } + + /** + * Returns the style property for the given path. + * + * It also converts CSS Custom Property stored as + * "var:preset|color|secondary" to the form + * "--wp--preset--color--secondary". + * + * @since 5.8.0 + * + * @param array $styles Styles subtree. + * @param array $path Which property to process. + * + * @return string Style property value. + */ + private static function get_property_value( $styles, $path ) { + $value = _wp_array_get( $styles, $path, '' ); + + if ( '' === $value ) { + return $value; + } + + $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; + } + /** * Builds metadata for the setting nodes, which returns in the form of: * - * [ - * [ - * 'path' => ['path', 'to', 'some', 'node' ] - * ], - * [ - * 'path' => [ 'path', 'to', 'other', 'node' ] - * ], - * ] + * [ + * [ + * 'path' => ['path', 'to', 'some', 'node' ], + * 'selector' => 'CSS selector for some node' + * ], + * [ + * 'path' => [ 'path', 'to', 'other', 'node' ], + * 'selector' => 'CSS selector for other node' + * ], + * ] * * @since 5.8.0 * * @param array $theme_json The tree to extract setting nodes from. + * @param array $selectors List of selectors per block. + * * @return array */ - private static function get_setting_nodes( $theme_json ) { + private static function get_setting_nodes( $theme_json, $selectors = array() ) { $nodes = array(); if ( ! isset( $theme_json['settings'] ) ) { return $nodes; @@ -234,7 +914,8 @@ class WP_Theme_JSON { // Top-level. $nodes[] = array( - 'path' => array( 'settings' ), + 'path' => array( 'settings' ), + 'selector' => self::ROOT_BLOCK_SELECTOR, ); // Calculate paths for blocks. @@ -243,14 +924,92 @@ class WP_Theme_JSON { } foreach ( $theme_json['settings']['blocks'] as $name => $node ) { + $selector = null; + if ( isset( $selectors[ $name ]['selector'] ) ) { + $selector = $selectors[ $name ]['selector']; + } + $nodes[] = array( - 'path' => array( 'settings', 'blocks', $name ), + 'path' => array( 'settings', 'blocks', $name ), + 'selector' => $selector, ); } return $nodes; } + + /** + * Builds metadata for the style nodes, which returns in the form of: + * + * [ + * [ + * 'path' => [ 'path', 'to', 'some', 'node' ], + * 'selector' => 'CSS selector for some node' + * ], + * [ + * 'path' => ['path', 'to', 'other', 'node' ], + * 'selector' => 'CSS selector for other node' + * ], + * ] + * + * @since 5.8.0 + * + * @param array $theme_json The tree to extract style nodes from. + * @param array $selectors List of selectors per block. + * + * @return array + */ + private static function get_style_nodes( $theme_json, $selectors = array() ) { + $nodes = array(); + if ( ! isset( $theme_json['styles'] ) ) { + return $nodes; + } + + // Top-level. + $nodes[] = array( + 'path' => array( 'styles' ), + 'selector' => self::ROOT_BLOCK_SELECTOR, + ); + + if ( isset( $theme_json['styles']['elements'] ) ) { + foreach ( $theme_json['styles']['elements'] as $element => $node ) { + $nodes[] = array( + 'path' => array( 'styles', 'elements', $element ), + 'selector' => self::ELEMENTS[ $element ], + ); + } + } + + // Blocks. + if ( ! isset( $theme_json['styles']['blocks'] ) ) { + return $nodes; + } + + foreach ( $theme_json['styles']['blocks'] as $name => $node ) { + $selector = null; + if ( isset( $selectors[ $name ]['selector'] ) ) { + $selector = $selectors[ $name ]['selector']; + } + + $nodes[] = array( + 'path' => array( 'styles', 'blocks', $name ), + 'selector' => $selector, + ); + + if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) { + foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) { + $nodes[] = array( + 'path' => array( 'styles', 'blocks', $name, 'elements', $element ), + 'selector' => $selectors[ $name ]['elements'][ $element ], + ); + } + } + } + + return $nodes; + } + /** * Merge new incoming data. * diff --git a/wp-includes/default-filters.php b/wp-includes/default-filters.php index 7a7bfeab0f..d828a9fd17 100644 --- a/wp-includes/default-filters.php +++ b/wp-includes/default-filters.php @@ -535,6 +535,7 @@ add_action( 'wp_default_scripts', 'wp_default_packages' ); add_action( 'wp_enqueue_scripts', 'wp_localize_jquery_ui_datepicker', 1000 ); add_action( 'wp_enqueue_scripts', 'wp_common_block_scripts_and_styles' ); +add_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles' ); add_action( 'admin_enqueue_scripts', 'wp_localize_jquery_ui_datepicker', 1000 ); add_action( 'admin_enqueue_scripts', 'wp_common_block_scripts_and_styles' ); add_action( 'enqueue_block_assets', 'wp_enqueue_registered_block_scripts_and_styles' ); diff --git a/wp-includes/script-loader.php b/wp-includes/script-loader.php index ebb9a23359..6a288e813a 100644 --- a/wp-includes/script-loader.php +++ b/wp-includes/script-loader.php @@ -2232,6 +2232,52 @@ function wp_common_block_scripts_and_styles() { do_action( 'enqueue_block_assets' ); } +/** + * Enqueues the global styles defined via theme.json. + * + * @since 5.8.0 + * + * @return void + */ +function wp_enqueue_global_styles() { + if ( ! WP_Theme_JSON_Resolver::theme_has_support() ) { + return; + } + + $can_use_cache = ( + ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) && + ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) && + ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) && + ! is_admin() + ); + + $stylesheet = null; + if ( $can_use_cache ) { + $cache = get_transient( 'global_styles' ); + if ( $cache ) { + $stylesheet = $cache; + } + } + + if ( null === $stylesheet ) { + $settings = get_default_block_editor_settings(); + $theme_json = WP_Theme_JSON_Resolver::get_merged_data( $settings ); + $stylesheet = $theme_json->get_stylesheet(); + + if ( $can_use_cache ) { + set_transient( 'global_styles', $stylesheet, MINUTE_IN_SECONDS ); + } + } + + if ( empty( $stylesheet ) ) { + return; + } + + wp_register_style( 'global-styles', false, array(), true, true ); + wp_add_inline_style( 'global-styles', $stylesheet ); + wp_enqueue_style( 'global-styles' ); +} + /** * Checks if the editor scripts and styles for all registered block types * should be enqueued on the current screen. diff --git a/wp-includes/version.php b/wp-includes/version.php index f919bae2c1..9bd4888528 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -13,7 +13,7 @@ * * @global string $wp_version */ -$wp_version = '5.8-alpha-50972'; +$wp_version = '5.8-alpha-50973'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.