diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index e520166fa4bb34..267089cdb6d195 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -302,7 +302,7 @@ Gather blocks in a layout container. ([Source](https://github.com/WordPress/gute - **Name:** core/group - **Category:** design -- **Supports:** align (full, wide), anchor, ariaLabel, color (background, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~ +- **Supports:** align (full, wide), anchor, ariaLabel, color (background, button, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** allowedBlocks, tagName, templateLock ## Heading diff --git a/lib/block-supports/colors.php b/lib/block-supports/colors.php index 7ec32bf14d7537..88e13abfc9cc9b 100644 --- a/lib/block-supports/colors.php +++ b/lib/block-supports/colors.php @@ -16,10 +16,14 @@ function gutenberg_register_colors_support( $block_type ) { $has_background_colors_support = true === $color_support || ( is_array( $color_support ) && _wp_array_get( $color_support, array( 'background' ), true ) ); $has_gradients_support = _wp_array_get( $color_support, array( 'gradients' ), false ); $has_link_colors_support = _wp_array_get( $color_support, array( 'link' ), false ); + $has_button_colors_support = _wp_array_get( $color_support, array( 'button' ), false ); + $has_heading_colors_support = _wp_array_get( $color_support, array( 'heading' ), false ); $has_color_support = $has_text_colors_support || $has_background_colors_support || $has_gradients_support || - $has_link_colors_support; + $has_link_colors_support || + $has_button_colors_support || + $has_heading_colors_support; if ( ! $block_type->attributes ) { $block_type->attributes = array(); diff --git a/lib/block-supports/elements.php b/lib/block-supports/elements.php index d4502aaa2e4785..4029c91aa40a35 100644 --- a/lib/block-supports/elements.php +++ b/lib/block-supports/elements.php @@ -23,34 +23,79 @@ function gutenberg_get_elements_class_name( $block ) { * @return string Filtered block content. */ function gutenberg_render_elements_support( $block_content, $block ) { - if ( ! $block_content ) { + if ( ! $block_content || empty( $block['attrs'] ) ) { return $block_content; } - $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); - $skip_link_color_serialization = wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' ); + $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); + + $element_color_properties = array( + 'button' => array( + 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' ), + 'paths' => array( + 'style.elements.button.color.text', + 'style.elements.button.color.background', + 'style.elements.button.color.gradient', + ), + ), + 'link' => array( + 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' ), + 'paths' => array( + 'style.elements.link.color.text', + 'style.elements.link.:hover.color.text', + ), + ), + 'heading' => array( + 'skip' => wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' ), + 'paths' => array( + 'style.elements.heading.color.text', + 'style.elements.heading.color.background', + 'style.elements.heading.color.gradient', + 'style.elements.h1.color.text', + 'style.elements.h1.color.background', + 'style.elements.h1.color.gradient', + 'style.elements.h2.color.text', + 'style.elements.h2.color.background', + 'style.elements.h2.color.gradient', + 'style.elements.h3.color.text', + 'style.elements.h3.color.background', + 'style.elements.h3.color.gradient', + 'style.elements.h4.color.text', + 'style.elements.h4.color.background', + 'style.elements.h4.color.gradient', + 'style.elements.h5.color.text', + 'style.elements.h5.color.background', + 'style.elements.h5.color.gradient', + 'style.elements.h6.color.text', + 'style.elements.h6.color.background', + 'style.elements.h6.color.gradient', + ), + ), + ); + + $skip_all_element_color_serialization = $element_color_properties['button']['skip'] && + $element_color_properties['link']['skip'] && + $element_color_properties['heading']['skip']; - if ( $skip_link_color_serialization ) { + if ( $skip_all_element_color_serialization ) { return $block_content; } - $link_color = null; - if ( ! empty( $block['attrs'] ) ) { - $link_color = _wp_array_get( $block['attrs'], array( 'style', 'elements', 'link', 'color', 'text' ), null ); - } + $element_colors_set = 0; + + foreach ( $element_color_properties as $element_config ) { + if ( $element_config['skip'] ) { + continue; + } - $hover_link_color = null; - if ( ! empty( $block['attrs'] ) ) { - $hover_link_color = _wp_array_get( $block['attrs'], array( 'style', 'elements', 'link', ':hover', 'color', 'text' ), null ); + foreach ( $element_config['paths'] as $path ) { + if ( null !== _wp_array_get( $block['attrs'], explode( '.', $path ), null ) ) { + $element_colors_set++; + } + } } - /* - * For now we only care about link colors. - * This code in the future when we have a public API - * should take advantage of WP_Theme_JSON_Gutenberg::compute_style_properties - * and work for any element and style. - */ - if ( null === $link_color && null === $hover_link_color ) { + if ( ! $element_colors_set ) { return $block_content; } @@ -80,33 +125,84 @@ function gutenberg_render_elements_support_styles( $pre_render, $block ) { $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); $element_block_styles = isset( $block['attrs']['style']['elements'] ) ? $block['attrs']['style']['elements'] : null; - /* - * For now we only care about link color. - */ - $skip_link_color_serialization = wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' ); + if ( ! $element_block_styles ) { + return null; + } - if ( $skip_link_color_serialization ) { + $skip_link_color_serialization = wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' ); + $skip_heading_color_serialization = wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' ); + $skip_button_color_serialization = wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' ); + $skips_all_element_color_serialization = $skip_link_color_serialization && + $skip_heading_color_serialization && + $skip_button_color_serialization; + + if ( $skips_all_element_color_serialization ) { return null; } - $class_name = gutenberg_get_elements_class_name( $block ); - $link_block_styles = isset( $element_block_styles['link'] ) ? $element_block_styles['link'] : null; - - gutenberg_style_engine_get_styles( - $link_block_styles, - array( - 'selector' => ".$class_name a", - 'context' => 'block-supports', - ) + + $class_name = gutenberg_get_elements_class_name( $block ); + + $element_types = array( + 'button' => array( + 'selector' => ".$class_name .wp-element-button, .$class_name .wp-block-button__link", + 'skip' => $skip_button_color_serialization, + ), + 'link' => array( + 'selector' => ".$class_name a", + 'hover_selector' => ".$class_name a:hover", + 'skip' => $skip_link_color_serialization, + ), + 'heading' => array( + 'selector' => ".$class_name h1, .$class_name h2, .$class_name h3, .$class_name h4, .$class_name h5, .$class_name h6", + 'skip' => $skip_heading_color_serialization, + 'elements' => array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ), + ), ); - if ( isset( $link_block_styles[':hover'] ) ) { - gutenberg_style_engine_get_styles( - $link_block_styles[':hover'], - array( - 'selector' => ".$class_name a:hover", - 'context' => 'block-supports', - ) - ); + foreach ( $element_types as $element_type => $element_config ) { + if ( $element_config['skip'] ) { + continue; + } + + $element_style_object = _wp_array_get( $element_block_styles, array( $element_type ), null ); + + // Process primary element type styles. + if ( $element_style_object ) { + gutenberg_style_engine_get_styles( + $element_style_object, + array( + 'selector' => $element_config['selector'], + 'context' => 'block-supports', + ) + ); + + if ( isset( $element_style_object[':hover'] ) ) { + gutenberg_style_engine_get_styles( + $element_style_object[':hover'], + array( + 'selector' => $element_config['hover_selector'], + 'context' => 'block-supports', + ) + ); + } + } + + // Process related elements e.g. h1-h6 for headings. + if ( isset( $element_config['elements'] ) ) { + foreach ( $element_config['elements'] as $element ) { + $element_style_object = _wp_array_get( $element_block_styles, array( $element ), null ); + + if ( $element_style_object ) { + gutenberg_style_engine_get_styles( + $element_style_object, + array( + 'selector' => ".$class_name $element", + 'context' => 'block-supports', + ) + ); + } + } + } } return null; diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index b416c86405e512..e165a888822948 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -34,6 +34,7 @@ import { } from './dimensions'; import useDisplayBlockControls from '../components/use-display-block-controls'; import { shouldSkipSerialization } from './utils'; +import { scopeSelector } from '../components/global-styles/utils'; import { useBlockEditingMode } from '../components/block-editing-mode'; const styleSupportKeys = [ @@ -371,6 +372,18 @@ export const withBlockControls = createHigherOrderComponent( 'withToolbarControls' ); +// Defines which element types are supported, including their hover styles or +// any other elements that have been included under a single element type +// e.g. heading and h1-h6. +const elementTypes = [ + { elementType: 'button' }, + { elementType: 'link', pseudo: [ ':hover' ] }, + { + elementType: 'heading', + elements: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ], + }, +]; + /** * Override the default block element to include elements styles. * @@ -383,47 +396,84 @@ const withElementsStyles = createHigherOrderComponent( BlockListBlock ) }`; - const skipLinkColorSerialization = shouldSkipSerialization( - props.name, - COLOR_SUPPORT_KEY, - 'link' - ); + // The .editor-styles-wrapper selector is required on elements styles. As it is + // added to all other editor styles, not providing it causes reset and global + // styles to override element styles because of higher specificity. + const baseElementSelector = `.editor-styles-wrapper .${ blockElementsContainerIdentifier }`; + const blockElementStyles = props.attributes.style?.elements; const styles = useMemo( () => { - // The .editor-styles-wrapper selector is required on elements styles. As it is - // added to all other editor styles, not providing it causes reset and global - // styles to override element styles because of higher specificity. - const elements = [ - { - styles: ! skipLinkColorSerialization - ? props.attributes.style?.elements?.link - : undefined, - selector: `.editor-styles-wrapper .${ blockElementsContainerIdentifier } ${ ELEMENTS.link }`, - }, - { - styles: ! skipLinkColorSerialization - ? props.attributes.style?.elements?.link?.[ ':hover' ] - : undefined, - selector: `.editor-styles-wrapper .${ blockElementsContainerIdentifier } ${ ELEMENTS.link }:hover`, - }, - ]; - const elementCssRules = []; - for ( const { styles: elementStyles, selector } of elements ) { + if ( ! blockElementStyles ) { + return; + } + + const elementCSSRules = []; + + elementTypes.forEach( ( { elementType, pseudo, elements } ) => { + const skipSerialization = shouldSkipSerialization( + props.name, + COLOR_SUPPORT_KEY, + elementType + ); + + if ( skipSerialization ) { + return; + } + + const elementStyles = blockElementStyles?.[ elementType ]; + + // Process primary element type styles. if ( elementStyles ) { - const cssRule = compileCSS( elementStyles, { - selector, + const selector = scopeSelector( + baseElementSelector, + ELEMENTS[ elementType ] + ); + + elementCSSRules.push( + compileCSS( elementStyles, { selector } ) + ); + + // Process any interactive states for the element type. + if ( pseudo ) { + pseudo.forEach( ( pseudoSelector ) => { + if ( elementStyles[ pseudoSelector ] ) { + elementCSSRules.push( + compileCSS( + elementStyles[ pseudoSelector ], + { + selector: scopeSelector( + baseElementSelector, + `${ ELEMENTS[ elementType ] }${ pseudoSelector }` + ), + } + ) + ); + } + } ); + } + } + + // Process related elements e.g. h1-h6 for headings + if ( elements ) { + elements.forEach( ( element ) => { + if ( blockElementStyles[ element ] ) { + elementCSSRules.push( + compileCSS( blockElementStyles[ element ], { + selector: scopeSelector( + baseElementSelector, + ELEMENTS[ element ] + ), + } ) + ); + } } ); - elementCssRules.push( cssRule ); } - } - return elementCssRules.length > 0 - ? elementCssRules.join( '' ) + } ); + + return elementCSSRules.length > 0 + ? elementCSSRules.join( '' ) : undefined; - }, [ - props.attributes.style?.elements, - blockElementsContainerIdentifier, - skipLinkColorSerialization, - ] ); + }, [ baseElementSelector, blockElementStyles, props.name ] ); const element = useContext( BlockList.__unstableElementContext ); diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index 9c6bf957d61c51..f81fc118ea84bb 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -166,6 +166,8 @@ export function useBlockSettings( name, parentLayout ) { const isBackgroundEnabled = useSetting( 'color.background' ); const isLinkEnabled = useSetting( 'color.link' ); const isTextEnabled = useSetting( 'color.text' ); + const isHeadingEnabled = useSetting( 'color.heading' ); + const isButtonEnabled = useSetting( 'color.button' ); const rawSettings = useMemo( () => { return { @@ -193,6 +195,8 @@ export function useBlockSettings( name, parentLayout ) { customDuotone, background: isBackgroundEnabled, link: isLinkEnabled, + heading: isHeadingEnabled, + button: isButtonEnabled, text: isTextEnabled, }, typography: { diff --git a/packages/block-library/src/group/block.json b/packages/block-library/src/group/block.json index 4f8de8802ea70c..9c2d012620c3c1 100644 --- a/packages/block-library/src/group/block.json +++ b/packages/block-library/src/group/block.json @@ -31,6 +31,7 @@ "color": { "gradients": true, "heading": true, + "button": true, "link": true, "__experimentalDefaultControls": { "background": true, diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index ec66168bc1a9a6..52cb3515c342e5 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -58,6 +58,9 @@ final class WP_Style_Engine { 'default' => 'background-color', ), 'path' => array( 'color', 'background' ), + 'css_vars' => array( + 'color' => '--wp--preset--color--$slug', + ), 'classnames' => array( 'has-background' => true, 'has-$slug-background-color' => 'color', @@ -67,6 +70,9 @@ final class WP_Style_Engine { 'property_keys' => array( 'default' => 'background', ), + 'css_vars' => array( + 'gradient' => '--wp--preset--gradient--$slug', + ), 'path' => array( 'color', 'gradient' ), 'classnames' => array( 'has-background' => true,