diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index fa63beef0710b3..466c8a9cf159d1 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -54,7 +54,7 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support $wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] ); if ( $content_size || $wide_size ) { - $style = "$selector > :where(:not(.alignleft):not(.alignright)) {"; + $style = "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull)) {"; $style .= 'max-width: ' . esc_html( $all_max_width_value ) . ';'; $style .= 'margin-left: auto !important;'; $style .= 'margin-right: auto !important;'; diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php index 5bb0c4fa944231..cb59b2d632989c 100644 --- a/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php +++ b/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php @@ -32,48 +32,53 @@ class WP_Theme_JSON_6_1 extends WP_Theme_JSON_6_0 { * path to the value in theme.json & block attributes. */ const PROPERTIES_METADATA = array( - 'background' => array( 'color', 'gradient' ), - 'background-color' => array( 'color', 'background' ), - 'border-radius' => array( 'border', 'radius' ), - 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ), - 'border-top-right-radius' => array( 'border', 'radius', 'topRight' ), - 'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ), - 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ), - 'border-color' => array( 'border', 'color' ), - 'border-width' => array( 'border', 'width' ), - 'border-style' => array( 'border', 'style' ), - 'border-top-color' => array( 'border', 'top', 'color' ), - 'border-top-width' => array( 'border', 'top', 'width' ), - 'border-top-style' => array( 'border', 'top', 'style' ), - 'border-right-color' => array( 'border', 'right', 'color' ), - 'border-right-width' => array( 'border', 'right', 'width' ), - 'border-right-style' => array( 'border', 'right', 'style' ), - 'border-bottom-color' => array( 'border', 'bottom', 'color' ), - 'border-bottom-width' => array( 'border', 'bottom', 'width' ), - 'border-bottom-style' => array( 'border', 'bottom', 'style' ), - 'border-left-color' => array( 'border', 'left', 'color' ), - 'border-left-width' => array( 'border', 'left', 'width' ), - 'border-left-style' => array( 'border', 'left', 'style' ), - 'color' => array( 'color', 'text' ), - 'font-family' => array( 'typography', 'fontFamily' ), - 'font-size' => array( 'typography', 'fontSize' ), - 'font-style' => array( 'typography', 'fontStyle' ), - 'font-weight' => array( 'typography', 'fontWeight' ), - 'letter-spacing' => array( 'typography', 'letterSpacing' ), - 'line-height' => array( 'typography', 'lineHeight' ), - 'margin' => array( 'spacing', 'margin' ), - 'margin-top' => array( 'spacing', 'margin', 'top' ), - 'margin-right' => array( 'spacing', 'margin', 'right' ), - 'margin-bottom' => array( 'spacing', 'margin', 'bottom' ), - 'margin-left' => array( 'spacing', 'margin', 'left' ), - 'padding' => array( 'spacing', 'padding' ), - 'padding-top' => array( 'spacing', 'padding', 'top' ), - 'padding-right' => array( 'spacing', 'padding', 'right' ), - 'padding-bottom' => array( 'spacing', 'padding', 'bottom' ), - 'padding-left' => array( 'spacing', 'padding', 'left' ), - 'text-decoration' => array( 'typography', 'textDecoration' ), - 'text-transform' => array( 'typography', 'textTransform' ), - 'filter' => array( 'filter', 'duotone' ), + 'background' => array( 'color', 'gradient' ), + 'background-color' => array( 'color', 'background' ), + 'border-radius' => array( 'border', 'radius' ), + 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ), + 'border-top-right-radius' => array( 'border', 'radius', 'topRight' ), + 'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ), + 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ), + 'border-color' => array( 'border', 'color' ), + 'border-width' => array( 'border', 'width' ), + 'border-style' => array( 'border', 'style' ), + 'border-top-color' => array( 'border', 'top', 'color' ), + 'border-top-width' => array( 'border', 'top', 'width' ), + 'border-top-style' => array( 'border', 'top', 'style' ), + 'border-right-color' => array( 'border', 'right', 'color' ), + 'border-right-width' => array( 'border', 'right', 'width' ), + 'border-right-style' => array( 'border', 'right', 'style' ), + 'border-bottom-color' => array( 'border', 'bottom', 'color' ), + 'border-bottom-width' => array( 'border', 'bottom', 'width' ), + 'border-bottom-style' => array( 'border', 'bottom', 'style' ), + 'border-left-color' => array( 'border', 'left', 'color' ), + 'border-left-width' => array( 'border', 'left', 'width' ), + 'border-left-style' => array( 'border', 'left', 'style' ), + 'color' => array( 'color', 'text' ), + 'font-family' => array( 'typography', 'fontFamily' ), + 'font-size' => array( 'typography', 'fontSize' ), + 'font-style' => array( 'typography', 'fontStyle' ), + 'font-weight' => array( 'typography', 'fontWeight' ), + 'letter-spacing' => array( 'typography', 'letterSpacing' ), + 'line-height' => array( 'typography', 'lineHeight' ), + 'margin' => array( 'spacing', 'margin' ), + 'margin-top' => array( 'spacing', 'margin', 'top' ), + 'margin-right' => array( 'spacing', 'margin', 'right' ), + 'margin-bottom' => array( 'spacing', 'margin', 'bottom' ), + 'margin-left' => array( 'spacing', 'margin', 'left' ), + 'padding' => array( 'spacing', 'padding' ), + 'padding-top' => array( 'spacing', 'padding', 'top' ), + 'padding-right' => array( 'spacing', 'padding', 'right' ), + 'padding-bottom' => array( 'spacing', 'padding', 'bottom' ), + 'padding-left' => array( 'spacing', 'padding', 'left' ), + '--wp--style--root--padding' => array( 'spacing', 'padding' ), + '--wp--style--root--padding-top' => array( 'spacing', 'padding', 'top' ), + '--wp--style--root--padding-right' => array( 'spacing', 'padding', 'right' ), + '--wp--style--root--padding-bottom' => array( 'spacing', 'padding', 'bottom' ), + '--wp--style--root--padding-left' => array( 'spacing', 'padding', 'left' ), + 'text-decoration' => array( 'typography', 'textDecoration' ), + 'text-transform' => array( 'typography', 'textTransform' ), + 'filter' => array( 'filter', 'duotone' ), ); /** @@ -617,10 +622,10 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' * @return string Styles for the block. */ public function get_styles_for_block( $block_metadata ) { - $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); - - $selector = $block_metadata['selector']; - $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); + $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); + $use_root_vars = _wp_array_get( $this->theme_json, array( 'settings', 'useRootVariables' ), array() ); + $selector = $block_metadata['selector']; + $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); // Get a reference to element name from path. // $block_metadata['path'] = array('styles','elements','link'); @@ -649,9 +654,9 @@ function( $pseudo_selector ) use ( $selector ) { // element then compute the style properties for it. // Otherwise just compute the styles for the default selector as normal. if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] ) && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { - $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json ); + $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_vars ); } else { - $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json ); + $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_vars ); } $block_rules = ''; @@ -698,6 +703,13 @@ function( $pseudo_selector ) use ( $selector ) { if ( static::ROOT_BLOCK_SELECTOR === $selector ) { $block_gap_value = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' ); + if ( $use_root_vars ) { + $block_rules .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; + $block_rules .= '.wp-site-blocks > * { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + $block_rules .= '.wp-site-blocks > * > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }'; + $block_rules .= '.alignfull > :where([class*="wp-block-"]:not(.alignfull),p,h1,h2,h3,h4,h5,h6, ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + } + $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; @@ -754,13 +766,15 @@ protected function get_block_classes( $style_nodes ) { * ) * ``` * - * @param array $styles Styles to process. - * @param array $settings Theme settings. - * @param array $properties Properties metadata. - * @param array $theme_json Theme JSON array. - * @return array Returns the modified $declarations. + * @param array $styles Styles to process. + * @param array $settings Theme settings. + * @param array $properties Properties metadata. + * @param array $theme_json Theme JSON array. + * @param string $selector The style block selector. + * @param boolean $use_root_vars Whether to add custom properties at root level. + * @return array Returns the modified $declarations. */ - protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null ) { + protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_vars = null ) { if ( null === $properties ) { $properties = static::PROPERTIES_METADATA; } @@ -770,9 +784,23 @@ protected static function compute_style_properties( $styles, $settings = array() return $declarations; } + $root_variable_duplicates = array(); + foreach ( $properties as $css_property => $value_path ) { $value = static::get_property_value( $styles, $value_path, $theme_json ); + if ( strpos( $css_property, '--wp--style--root--' ) === 0 && static::ROOT_BLOCK_SELECTOR !== $selector ) { + continue; + } + // We don't support string values. + if ( '--wp--style--root--padding' === $css_property && is_string( $value ) ) { + continue; + } + + if ( strpos( $css_property, '--wp--style--root--' ) === 0 && $use_root_vars ) { + $root_variable_duplicates[] = substr( $css_property, strlen( '--wp--style--root--' ) ); + } + // Look up protected properties, keyed by value path. // Skip protected properties that are explicitly set to `null`. if ( is_array( $value_path ) ) { @@ -797,6 +825,14 @@ protected static function compute_style_properties( $styles, $settings = array() ); } + // If a variable value is added to the root, the corresponding property should be removed. + foreach ( $root_variable_duplicates as $duplicate ) { + $discard = array_search( $duplicate, array_column( $declarations, 'name' ), true ); + if ( $discard ) { + array_splice( $declarations, $discard, 1 ); + } + } + return $declarations; } @@ -985,6 +1021,7 @@ protected static function get_property_value( $styles, $path, $theme_json = null */ const VALID_SETTINGS = array( 'appearanceTools' => null, + 'useRootVariables' => null, 'border' => array( 'color' => null, 'radius' => null, diff --git a/lib/compat/wordpress-6.1/theme.json b/lib/compat/wordpress-6.1/theme.json index 426adc6195085d..b1d7724b22abba 100644 --- a/lib/compat/wordpress-6.1/theme.json +++ b/lib/compat/wordpress-6.1/theme.json @@ -2,6 +2,7 @@ "version": 2, "settings": { "appearanceTools": false, + "useRootVariables": false, "border": { "color": false, "radius": false, @@ -349,6 +350,14 @@ } } }, - "spacing": { "blockGap": "24px" } + "spacing": { + "blockGap": "24px", + "padding": { + "top": "0px", + "right": "0px", + "bottom": "0px", + "left": "0px" + } + } } } diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index 400ebd0a84263f..fd4c4fdc8b9fa7 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -68,6 +68,9 @@ export function getInlineStyles( styles = {} ) { const ignoredStyles = [ 'spacing.blockGap' ]; const output = {}; Object.keys( STYLE_PROPERTY ).forEach( ( propKey ) => { + if ( STYLE_PROPERTY[ propKey ].rootOnly ) { + return; + } const path = STYLE_PROPERTY[ propKey ].value; const subPaths = STYLE_PROPERTY[ propKey ].properties; // Ignore styles on elements because they are handled on the server. diff --git a/packages/block-editor/src/layouts/flow.js b/packages/block-editor/src/layouts/flow.js index 1d8ea7d7a6aea2..d267fd609f2447 100644 --- a/packages/block-editor/src/layouts/flow.js +++ b/packages/block-editor/src/layouts/flow.js @@ -132,7 +132,7 @@ export default { ? ` ${ appendSelectors( selector, - '> :where(:not(.alignleft):not(.alignright))' + '> :where(:not(.alignleft):not(.alignright):not(.alignfull))' ) } { max-width: ${ contentSize ?? wideSize }; margin-left: auto !important; diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index fb8580ef30ef0a..b852fe3932fa05 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -199,6 +199,17 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { support: [ 'typography', '__experimentalLetterSpacing' ], useEngine: true, }, + '--wp--style--root--padding': { + value: [ 'spacing', 'padding' ], + support: [ 'spacing', 'padding' ], + properties: { + '--wp--style--root--padding-top': 'top', + '--wp--style--root--padding-right': 'right', + '--wp--style--root--padding-bottom': 'bottom', + '--wp--style--root--padding-left': 'left', + }, + rootOnly: true, + }, }; export const __EXPERIMENTAL_ELEMENTS = { diff --git a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js index 8014c015dc44c9..d926805542bed3 100644 --- a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js +++ b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js @@ -445,7 +445,7 @@ describe( 'global styles renderer', () => { expect( toStyles( tree, blockSelectors ) ).toEqual( 'body {margin: 0;}' + - 'body{background-color: red;margin: 10px;padding: 10px;}h1{font-size: 42px;}a{color: blue;}a:hover{color: orange;}a:focus{color: orange;}.wp-block-group{margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}h1,h2,h3,h4,h5,h6{color: orange;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: hotpink;}h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover{color: red;}h1 a:focus,h2 a:focus,h3 a:focus,h4 a:focus,h5 a:focus,h6 a:focus{color: red;}' + + 'body{background-color: red;--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 10px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 10px;margin: 10px;padding: 10px;}h1{font-size: 42px;}a{color: blue;}a:hover{color: orange;}a:focus{color: orange;}.wp-block-group{margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}h1,h2,h3,h4,h5,h6{color: orange;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: hotpink;}h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover{color: red;}h1 a:focus,h2 a:focus,h3 a:focus,h4 a:focus,h5 a:focus,h6 a:focus{color: red;}' + '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }' + '.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}h1.has-blue-color,h2.has-blue-color,h3.has-blue-color,h4.has-blue-color,h5.has-blue-color,h6.has-blue-color{color: var(--wp--preset--color--blue) !important;}h1.has-blue-background-color,h2.has-blue-background-color,h3.has-blue-background-color,h4.has-blue-background-color,h5.has-blue-background-color,h6.has-blue-background-color{background-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-color,h2.has-blue-border-color,h3.has-blue-border-color,h4.has-blue-border-color,h5.has-blue-border-color,h6.has-blue-border-color{border-color: var(--wp--preset--color--blue) !important;}' ); diff --git a/packages/edit-site/src/components/global-styles/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/use-global-styles-output.js index 3f533c49f82e34..ab0cf87351575f 100644 --- a/packages/edit-site/src/components/global-styles/use-global-styles-output.js +++ b/packages/edit-site/src/components/global-styles/use-global-styles-output.js @@ -173,14 +173,22 @@ function flattenTree( input = {}, prefix, token ) { /** * Transform given style tree into a set of style declarations. * - * @param {Object} blockStyles Block styles. + * @param {Object} blockStyles Block styles. + * + * @param {string} selector The selector these declarations should attach to. + * + * @param {boolean} useRootVars Whether to use CSS custom properties in root selector. * * @return {Array} An array of style declarations. */ -function getStylesDeclarations( blockStyles = {} ) { +function getStylesDeclarations( blockStyles = {}, selector = '', useRootVars ) { + const isRoot = ROOT_BLOCK_SELECTOR === selector; const output = reduce( STYLE_PROPERTY, - ( declarations, { value, properties, useEngine }, key ) => { + ( declarations, { value, properties, useEngine, rootOnly }, key ) => { + if ( rootOnly && ! isRoot ) { + return declarations; + } const pathToValue = value; if ( first( pathToValue ) === 'elements' || useEngine ) { return declarations; @@ -198,7 +206,9 @@ function getStylesDeclarations( blockStyles = {} ) { return; } - const cssProperty = kebabCase( name ); + const cssProperty = name.startsWith( '--' ) + ? name + : kebabCase( name ); declarations.push( `${ cssProperty }: ${ compileStyleValue( get( styleValue, [ prop ] ) @@ -221,6 +231,10 @@ function getStylesDeclarations( blockStyles = {} ) { [] ); + if ( isRoot && useRootVars ) { + return output; + } + // The goal is to move everything to server side generated engine styles // This is temporary as we absorb more and more styles into the engine. const extraRules = getCSSRules( blockStyles ); @@ -503,6 +517,7 @@ export const toStyles = ( ) => { const nodesWithStyles = getNodesWithStyles( tree, blockSelectors ); const nodesWithSettings = getNodesWithSettings( tree, blockSelectors ); + const useRootVars = tree?.settings?.useRootVariables; /* * Reset default browser margin on the root body element. @@ -513,6 +528,12 @@ export const toStyles = ( * @link https://github.com/WordPress/gutenberg/issues/36147. */ let ruleset = 'body {margin: 0;}'; + + if ( useRootVars ) { + ruleset = + 'body { margin: 0; padding-right: 0; padding-left: 0; padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom) } .wp-site-blocks > * { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); } .wp-site-blocks > * > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); } .alignfull > :where([class*="wp-block-"]:not(.alignfull),p,h1,h2,h3,h4,h5,h6, ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + } + nodesWithStyles.forEach( ( { selector, @@ -554,7 +575,11 @@ export const toStyles = ( } // Process the remaining block styles (they use either normal block class or __experimentalSelector). - const declarations = getStylesDeclarations( styles ); + const declarations = getStylesDeclarations( + styles, + selector, + useRootVars + ); if ( declarations?.length ) { ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`;