diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index fa63beef0710b..b26ed9df659c2 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -34,10 +34,11 @@ function gutenberg_register_layout_support( $block_type ) { * @param string $gap_value The block gap value to apply. * @param boolean $should_skip_gap_serialization Whether to skip applying the user-defined value set in the editor. * @param string $fallback_gap_value The block gap value to apply. + * @param array $block_spacing Custom spacing set on the block. * * @return string CSS style. */ -function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null, $should_skip_gap_serialization = false, $fallback_gap_value = '0.5em' ) { +function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null, $should_skip_gap_serialization = false, $fallback_gap_value = '0.5em', $block_spacing = null ) { $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; $style = ''; @@ -54,7 +55,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;'; @@ -62,6 +63,25 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support $style .= "$selector > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}'; $style .= "$selector .alignfull { max-width: none; }"; + + if ( isset( $block_spacing ) ) { + $block_spacing_values = gutenberg_style_engine_get_block_supports_styles( + array( + 'spacing' => $block_spacing, + ) + ); + + // Handle negative margins for alignfull children of blocks with custom padding set. + // They're added separately because padding might only be set on one side. + if ( isset( $block_spacing_values['declarations']['padding-right'] ) ) { + $padding_right = $block_spacing_values['declarations']['padding-right']; + $style .= "$selector > .alignfull { margin-right:calc($padding_right * -1); }"; + } + if ( isset( $block_spacing_values['declarations']['padding-left'] ) ) { + $padding_left = $block_spacing_values['declarations']['padding-left']; + $style .= "$selector > .alignfull { margin-left: calc($padding_left * -1); }"; + } + } } if ( $has_block_gap_support ) { @@ -173,6 +193,11 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { $block_classname = wp_get_block_default_classname( $block['blockName'] ); $container_class = wp_unique_id( 'wp-container-' ); $layout_classname = ''; + $use_global_padding = gutenberg_get_global_settings( array( 'useRootPaddingAwareAlignments' ) ) && ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] || isset( $used_layout['contentSize'] ) && $used_layout['contentSize'] ); + + if ( $use_global_padding ) { + $class_names[] = 'has-global-padding'; + } // The following section was added to reintroduce a small set of layout classnames that were // removed in the 5.9 release (https://github.com/WordPress/gutenberg/issues/38719). It is @@ -214,11 +239,12 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { } $fallback_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), '0.5em' ); + $block_spacing = _wp_array_get( $block, array( 'attrs', 'style', 'spacing' ), null ); // If a block's block.json skips serialization for spacing or spacing.blockGap, // don't apply the user-defined value to the styles. $should_skip_gap_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' ); - $style = gutenberg_get_layout_style( ".$block_classname.$container_class", $used_layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value ); + $style = gutenberg_get_layout_style( ".$block_classname.$container_class", $used_layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value, $block_spacing ); // Only add container class and enqueue block support styles if unique styles were generated. if ( ! empty( $style ) ) { 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 5bb0c4fa94423..3916cc8159a2b 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_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments']; + $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_padding ); } 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_padding ); } $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_padding ) { + $block_rules .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; + $block_rules .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + $block_rules .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }'; + $block_rules .= '.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),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_padding 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_padding = null ) { if ( null === $properties ) { $properties = static::PROPERTIES_METADATA; } @@ -770,9 +784,24 @@ 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 || ! $use_root_padding ) ) { + continue; + } + // Root-level padding styles don't currently support strings with CSS shorthand values. + // This may change: https://github.com/WordPress/gutenberg/issues/40132. + if ( '--wp--style--root--padding' === $css_property && is_string( $value ) ) { + continue; + } + + if ( strpos( $css_property, '--wp--style--root--' ) === 0 && $use_root_padding ) { + $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 +826,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 ( is_numeric( $discard ) ) { + array_splice( $declarations, $discard, 1 ); + } + } + return $declarations; } @@ -984,14 +1021,15 @@ protected static function get_property_value( $styles, $path, $theme_json = null * @var array */ const VALID_SETTINGS = array( - 'appearanceTools' => null, - 'border' => array( + 'appearanceTools' => null, + 'useRootPaddingAwareAlignments' => null, + 'border' => array( 'color' => null, 'radius' => null, 'style' => null, 'width' => null, ), - 'color' => array( + 'color' => array( 'background' => null, 'custom' => null, 'customDuotone' => null, @@ -1005,13 +1043,13 @@ protected static function get_property_value( $styles, $path, $theme_json = null 'palette' => null, 'text' => null, ), - 'custom' => null, - 'layout' => array( + 'custom' => null, + 'layout' => array( 'contentSize' => null, 'definitions' => null, 'wideSize' => null, ), - 'spacing' => array( + 'spacing' => array( 'customSpacingSize' => null, 'spacingSizes' => null, 'spacingScale' => null, @@ -1020,7 +1058,7 @@ protected static function get_property_value( $styles, $path, $theme_json = null 'padding' => null, 'units' => null, ), - 'typography' => array( + 'typography' => array( 'customFontSize' => null, 'dropCap' => null, 'fontFamilies' => null, diff --git a/lib/compat/wordpress-6.1/theme.json b/lib/compat/wordpress-6.1/theme.json index 426adc6195085..b5358e56e24b9 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, + "useRootPaddingAwareAlignments": 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/layout.js b/packages/block-editor/src/hooks/layout.js index 65a44b6b8df54..4db7ac3b6ef05 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -49,7 +49,12 @@ const layoutBlockSupportKey = '__experimentalLayout'; * * @return { Array } Array of CSS classname strings. */ -function getLayoutClasses( layout, layoutDefinitions ) { +function useLayoutClasses( layout, layoutDefinitions ) { + const rootPaddingAlignment = useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + return getSettings().__experimentalFeatures + ?.useRootPaddingAwareAlignments; + }, [] ); const layoutClassnames = []; if ( layoutDefinitions?.[ layout?.type || 'default' ]?.className ) { @@ -58,6 +63,10 @@ function getLayoutClasses( layout, layoutDefinitions ) { ); } + if ( ( layout?.inherit || layout?.contentSize ) && rootPaddingAlignment ) { + layoutClassnames.push( 'has-global-padding' ); + } + if ( layout?.orientation ) { layoutClassnames.push( `is-${ kebabCase( layout.orientation ) }` ); } @@ -136,7 +145,9 @@ function LayoutPanel( { setAttributes, attributes, name: blockName } ) { checked={ ! inherit } onChange={ () => setAttributes( { - layout: { inherit: ! inherit }, + layout: { + inherit: ! inherit, + }, } ) } /> @@ -267,7 +278,7 @@ export const withLayoutStyles = createHigherOrderComponent( ? defaultThemeLayout : layout || defaultBlockLayout || {}; const layoutClasses = shouldRenderLayoutStyles - ? getLayoutClasses( usedLayout, defaultThemeLayout?.definitions ) + ? useLayoutClasses( usedLayout, defaultThemeLayout?.definitions ) : null; const selector = `.${ getBlockDefaultClassName( name diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index 400ebd0a84263..fd4c4fdc8b9fa 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 1d8ea7d7a6aea..f0e084d623b17 100644 --- a/packages/block-editor/src/layouts/flow.js +++ b/packages/block-editor/src/layouts/flow.js @@ -8,6 +8,7 @@ import { } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { Icon, positionCenter, stretchWide } from '@wordpress/icons'; +import { getCSSRules } from '@wordpress/style-engine'; /** * Internal dependencies @@ -132,7 +133,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; @@ -147,6 +148,27 @@ export default { ` : ''; + // If there is custom padding, add negative margins for alignfull blocks. + if ( style?.spacing?.padding ) { + // The style object might be storing a preset so we need to make sure we get a usable value. + const paddingValues = getCSSRules( style ); + paddingValues.forEach( ( rule ) => { + if ( rule.key === 'paddingRight' ) { + output += ` + ${ appendSelectors( selector, '> .alignfull' ) } { + margin-right: calc(${ rule.value } * -1); + } + `; + } else if ( rule.key === 'paddingLeft' ) { + output += ` + ${ appendSelectors( selector, '> .alignfull' ) } { + margin-left: calc(${ rule.value } * -1); + } + `; + } + } ); + } + // Output blockGap styles based on rules contained in layout definitions in theme.json. if ( hasBlockGapSupport && blockGapValue ) { output += getBlockGapCSS( diff --git a/packages/block-library/src/cover/style.scss b/packages/block-library/src/cover/style.scss index 957cfc5d4e23a..619cad46c1b5a 100644 --- a/packages/block-library/src/cover/style.scss +++ b/packages/block-library/src/cover/style.scss @@ -4,7 +4,6 @@ background-size: cover; background-position: center center; min-height: 430px; - width: 100%; display: flex; justify-content: center; align-items: center; diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index fb8580ef30ef0..b852fe3932fa0 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/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/use-global-styles-output.js index 7654ebb7fed9c..74c66e652f2c2 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 @@ -172,14 +172,26 @@ 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} useRootPaddingAlign Whether to use CSS custom properties in root selector. * * @return {Array} An array of style declarations. */ -function getStylesDeclarations( blockStyles = {} ) { +function getStylesDeclarations( + blockStyles = {}, + selector = '', + useRootPaddingAlign +) { + 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; @@ -197,13 +209,22 @@ function getStylesDeclarations( blockStyles = {} ) { return; } - const cssProperty = kebabCase( name ); + const cssProperty = name.startsWith( '--' ) + ? name + : kebabCase( name ); declarations.push( `${ cssProperty }: ${ compileStyleValue( get( styleValue, [ prop ] ) ) }` ); } ); + } else if ( + key === '--wp--style--root--padding' && + typeof styleValue === 'string' + ) { + // Root-level padding styles don't currently support strings with CSS shorthand values. + // This may change: https://github.com/WordPress/gutenberg/issues/40132. + return declarations; } else if ( get( blockStyles, pathToValue, false ) ) { const cssProperty = key.startsWith( '--' ) ? key @@ -220,6 +241,10 @@ function getStylesDeclarations( blockStyles = {} ) { [] ); + if ( isRoot && useRootPaddingAlign ) { + 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 ); @@ -505,6 +530,7 @@ export const toStyles = ( ) => { const nodesWithStyles = getNodesWithStyles( tree, blockSelectors ); const nodesWithSettings = getNodesWithSettings( tree, blockSelectors ); + const useRootPaddingAlign = tree?.settings?.useRootPaddingAwareAlignments; /* * Reset default browser margin on the root body element. @@ -515,6 +541,12 @@ export const toStyles = ( * @link https://github.com/WordPress/gutenberg/issues/36147. */ let ruleset = 'body {margin: 0;}'; + + if ( useRootPaddingAlign ) { + 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) } .has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); } .has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); } .has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),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, @@ -556,7 +588,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, + useRootPaddingAlign + ); if ( declarations?.length ) { ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`; diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 0c830d8de9a9c..88d2b9a2b5870 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -913,4 +913,63 @@ function data_generate_spacing_scale_fixtures() { ), ); } + + function test_get_styles_for_block_with_padding_aware_alignments() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 2, + 'styles' => array( + 'spacing' => array( + 'padding' => array( + 'top' => '10px', + 'right' => '12px', + 'bottom' => '10px', + 'left' => '12px', + ), + ), + ), + 'settings' => array( + 'useRootPaddingAwareAlignments' => true, + ), + ) + ); + + $metadata = array( + 'path' => array( + '0' => 'styles', + ), + 'selector' => 'body', + ); + + $expected = 'body { margin: 0; }body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),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); }.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; }'; + $this->assertEquals( $expected, $theme_json->get_styles_for_block( $metadata ) ); + } + + function test_get_styles_for_block_without_padding_aware_alignments() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 2, + 'styles' => array( + 'spacing' => array( + 'padding' => array( + 'top' => '10px', + 'right' => '12px', + 'bottom' => '10px', + 'left' => '12px', + ), + ), + ), + ) + ); + + $metadata = array( + 'path' => array( + '0' => 'styles', + ), + 'selector' => 'body', + ); + + $expected = 'body { margin: 0; }body{padding-top: 10px;padding-right: 12px;padding-bottom: 10px;padding-left: 12px;}.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; }'; + $this->assertEquals( $expected, $theme_json->get_styles_for_block( $metadata ) ); + } }