diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index 2c62af810d969b..ed3758fadbdb36 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -53,7 +53,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 cea9bd77cf17e6..85f0bded24ee5f 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 @@ -15,6 +15,116 @@ * @access private */ class WP_Theme_JSON_6_1 extends WP_Theme_JSON_6_0 { + /** + * Metadata for style properties. + * + * Each element is a direct mapping from the CSS property name to the + * 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' ), + '--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' ), + '--wp--style--block-gap' => array( 'spacing', 'blockGap' ), + 'text-decoration' => array( 'typography', 'textDecoration' ), + 'text-transform' => array( 'typography', 'textTransform' ), + 'filter' => array( 'filter', 'duotone' ), + ); + + /** + * The valid properties under the settings key. + * + * @var array + */ + const VALID_SETTINGS = array( + 'appearanceTools' => null, + 'useRootVariables' => null, + 'border' => array( + 'color' => null, + 'radius' => null, + 'style' => null, + 'width' => null, + ), + 'color' => array( + 'background' => null, + 'custom' => null, + 'customDuotone' => null, + 'customGradient' => null, + 'defaultDuotone' => null, + 'defaultGradients' => null, + 'defaultPalette' => null, + 'duotone' => null, + 'gradients' => null, + 'link' => null, + 'palette' => null, + 'text' => null, + ), + 'custom' => null, + 'layout' => array( + 'contentSize' => null, + 'wideSize' => null, + ), + 'spacing' => array( + 'blockGap' => null, + 'margin' => null, + 'padding' => null, + 'units' => null, + ), + 'typography' => array( + 'customFontSize' => null, + 'dropCap' => null, + 'fontFamilies' => null, + 'fontSizes' => null, + 'fontStyle' => null, + 'fontWeight' => null, + 'letterSpacing' => null, + 'lineHeight' => null, + 'textDecoration' => null, + 'textTransform' => null, + ), + ); + /** * Returns the metadata for each block. * @@ -87,4 +197,251 @@ protected static function get_blocks_metadata() { return static::$blocks_metadata; } + + /** + * 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; + * } + * + * @param array $style_nodes Nodes with styles. + * @return string The new stylesheet. + */ + protected function get_block_classes( $style_nodes ) { + $block_rules = ''; + + foreach ( $style_nodes as $metadata ) { + if ( null === $metadata['selector'] ) { + continue; + } + + $use_root_vars = _wp_array_get( $this->theme_json, array( 'settings', 'useRootVariables' ), false ); + $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); + $selector = $metadata['selector']; + $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); + $declarations = static::compute_style_properties( $node, $settings, null, $selector, $use_root_vars ); + + // 1. Separate the ones who use the general selector + // and the ones who use the duotone selector. + $declarations_duotone = array(); + foreach ( $declarations as $index => $declaration ) { + if ( 'filter' === $declaration['name'] ) { + unset( $declarations[ $index ] ); + $declarations_duotone[] = $declaration; + } + } + + /* + * Reset default browser margin on the root body element. + * This is set on the root selector **before** generating the ruleset + * from the `theme.json`. This is to ensure that if the `theme.json` declares + * `margin` in its `spacing` declaration for the `body` element then these + * user-generated values take precedence in the CSS cascade. + * @link https://github.com/WordPress/gutenberg/issues/36147. + */ + if ( static::ROOT_BLOCK_SELECTOR === $selector ) { + $block_rules .= 'body { margin: 0; }'; + } + + // 2. Generate the rules that use the general selector. + $block_rules .= static::to_ruleset( $selector, $declarations ); + + // 3. Generate the rules that use the duotone selector. + if ( isset( $metadata['duotone'] ) && ! empty( $declarations_duotone ) ) { + $selector_duotone = static::scope_selector( $metadata['selector'], $metadata['duotone'] ); + $block_rules .= static::to_ruleset( $selector_duotone, $declarations_duotone ); + } + + // 4. Generate additional rules for the root block. + $block_rules .= static::additional_root_selector_rules( $selector, $this->theme_json, $use_root_vars ); + } + + return $block_rules; + } + + /** + * Generate additional rules for the root selector + * + * @param string $selector The selector to generate rules for. + * @param object $theme_json A copy of $this->theme_json. + * @param bool $use_root_vars whether or not the theme has opted in to root padding variables. + * @return string The rules for the root selector. + */ + protected static function additional_root_selector_rules( $selector, $theme_json, $use_root_vars ) { + $block_rules = ''; + if ( static::ROOT_BLOCK_SELECTOR === $selector ) { + if ( $use_root_vars ) { + // These rules are duplicated in use-global-styles-output.js. + $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); width: unset; }'; + + // Alignfull blocks in the block editor that are direct children of post content should also get negative margins. + if ( is_callable( 'get_current_screen' ) && get_current_screen()->is_block_editor() ) { + $block_rules .= 'body > .is-root-container, + .edit-post-visual-editor__post-title-wrapper + { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + + $block_rules .= '.is-root-container .wp-block.alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); max-width: unset; width: unset; }'; + } + } + + $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; }'; + + $has_block_gap_support = _wp_array_get( $theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null; + if ( $has_block_gap_support ) { + $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }'; + $block_rules .= '.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }'; + } + } + + return $block_rules; + } + + /** + * Given a styles array, it extracts the style properties + * and adds them to the $declarations array following the format: + * + * ```php + * array( + * 'name' => 'property_name', + * 'value' => 'property_value, + * ) + * ``` + * + * @param array $styles Styles to process. + * @param array $settings Theme settings. + * @param array $properties Properties metadata. + * @param string $selector Selector for styles. + * @param boolean $use_root_vars Whether to use root variables. + * @return array Returns the modified $declarations. + */ + protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $selector = null, $use_root_vars = null ) { + if ( null === $properties ) { + $properties = static::PROPERTIES_METADATA; + } + + $declarations = array(); + $root_variable_duplicates = array(); + + if ( empty( $styles ) ) { + return $declarations; + } + + foreach ( $properties as $css_property => $value_path ) { + $value = static::get_property_value( $styles, $value_path ); + + if ( strpos( $css_property, '--wp--style--root--' ) === 0 && ( ! $use_root_vars || static::ROOT_BLOCK_SELECTOR !== $selector ) ) { + continue; + } + + if ( strpos( $css_property, '--wp--style--root--' ) === 0 && $use_root_vars ) { + $root_variable_duplicates[] = substr( $css_property, strlen( '--wp--style--root--' ) ); + } + + // Root padding requires special logic to split shorthand values. + if ( '--wp--style--root--padding' === $css_property && is_string( $value ) ) { + if ( empty( $value ) ) { + $value = '0 0 0 0'; + } + + $shorthand_top = '0'; + $shorthand_right = '0'; + $shorthand_bottom = '0'; + $shorthand_left = '0'; + + $separate_values = explode( ' ', $value ); + + switch ( count( $separate_values ) ) { + case 1: + $shorthand_top = $separate_values[0]; + $shorthand_right = $separate_values[0]; + $shorthand_bottom = $separate_values[0]; + $shorthand_left = $separate_values[0]; + break; + case 2: + $shorthand_top = $separate_values[0]; + $shorthand_right = $separate_values[1]; + $shorthand_bottom = $separate_values[0]; + $shorthand_left = $separate_values[1]; + break; + case 3: + $shorthand_top = $separate_values[0]; + $shorthand_right = $separate_values[1]; + $shorthand_bottom = $separate_values[2]; + $shorthand_left = $separate_values[1]; + break; + case 4: + $shorthand_top = $separate_values[0]; + $shorthand_right = $separate_values[1]; + $shorthand_bottom = $separate_values[2]; + $shorthand_left = $separate_values[3]; + break; + } + + $all_properties = array( + array( + 'name' => '--wp--style--root--padding-top', + 'value' => $shorthand_top, + ), + array( + 'name' => '--wp--style--root--padding-right', + 'value' => $shorthand_right, + ), + array( + 'name' => '--wp--style--root--padding-bottom', + 'value' => $shorthand_bottom, + ), + array( + 'name' => '--wp--style--root--padding-left', + 'value' => $shorthand_left, + ), + ); + + $declarations = array_merge( $declarations, $all_properties ); + continue; + } + + // Look up protected properties, keyed by value path. + // Skip protected properties that are explicitly set to `null`. + if ( is_array( $value_path ) ) { + $path_string = implode( '.', $value_path ); + if ( + array_key_exists( $path_string, static::PROTECTED_PROPERTIES ) && + _wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null + ) { + continue; + } + } + + // Skip if empty and not "0" or value represents array of longhand values. + $has_missing_value = empty( $value ) && ! is_numeric( $value ); + if ( $has_missing_value || is_array( $value ) ) { + continue; + } + + $declarations[] = array( + 'name' => $css_property, + 'value' => $value, + ); + } + + // 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 ( false !== $discard ) { + array_splice( $declarations, $discard, 1 ); + } + } + + return $declarations; + } } diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php new file mode 100644 index 00000000000000..58abcc27c04ec5 --- /dev/null +++ b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php @@ -0,0 +1,35 @@ + { + 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 82851cb0150756..764cee2001dd3e 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/block-library/src/columns/style.scss b/packages/block-library/src/columns/style.scss index eb7e7d18072044..95654f2d027dd1 100644 --- a/packages/block-library/src/columns/style.scss +++ b/packages/block-library/src/columns/style.scss @@ -79,6 +79,10 @@ padding: $block-bg-padding--v $block-bg-padding--h; } +.wp-block-columns.alignfull.has-background { + padding-right: var(--wp--style--root--padding-right); + padding-left: var(--wp--style--root--padding-left); +} .wp-block-column { flex-grow: 1; diff --git a/packages/block-library/src/cover/style.scss b/packages/block-library/src/cover/style.scss index 8e137633f4e358..f545d7594c2f2c 100644 --- a/packages/block-library/src/cover/style.scss +++ b/packages/block-library/src/cover/style.scss @@ -12,6 +12,11 @@ // This block has customizable padding, border-box makes that more predictable. box-sizing: border-box; + &.alignfull { + padding-right: var(--wp--style--root--padding-right, 1em); + padding-left: var(--wp--style--root--padding-left, 1em); + } + &.has-parallax { background-attachment: fixed; diff --git a/packages/block-library/src/group/theme.scss b/packages/block-library/src/group/theme.scss index 5a76fc43bd6d93..a478880abb9def 100644 --- a/packages/block-library/src/group/theme.scss +++ b/packages/block-library/src/group/theme.scss @@ -3,3 +3,8 @@ // Matches paragraph block padding. padding: $block-bg-padding--v $block-bg-padding--h; } + +.wp-block-group.alignfull { + padding-right: var(--wp--style--root--padding-right); + padding-left: var(--wp--style--root--padding-left); +} diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index a5ed771d72d2fa..05119b0149e6df 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -179,6 +179,17 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { value: [ 'spacing', 'blockGap' ], support: [ 'spacing', 'blockGap' ], }, + '--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 d105ad7a673936..33ae1f8e55ae83 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 @@ -378,7 +378,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;}.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;}' + + '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;}.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;}' + '.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 0be47a7000b539..e5e0ea55eeed05 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 @@ -170,14 +170,21 @@ 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; @@ -194,14 +201,63 @@ function getStylesDeclarations( blockStyles = {} ) { // for sub-properties that don't have any value. return; } - - const cssProperty = kebabCase( name ); + const cssProperty = name.startsWith( '--' ) + ? name + : kebabCase( name ); declarations.push( `${ cssProperty }: ${ compileStyleValue( get( styleValue, [ prop ] ) ) }` ); } ); + } else if ( !! properties && isString( styleValue ) && rootOnly ) { + const separateValues = styleValue.split( ' ' ); + + const sortedBoxValues = { + top: '0', + right: '0', + bottom: '0', + left: '0', + }; + + switch ( separateValues.length ) { + case 1: + sortedBoxValues.top = separateValues[ 0 ]; + sortedBoxValues.right = separateValues[ 0 ]; + sortedBoxValues.bottom = separateValues[ 0 ]; + sortedBoxValues.left = separateValues[ 0 ]; + break; + case 2: + sortedBoxValues.top = separateValues[ 0 ]; + sortedBoxValues.right = separateValues[ 1 ]; + sortedBoxValues.bottom = separateValues[ 0 ]; + sortedBoxValues.left = separateValues[ 1 ]; + break; + case 3: + sortedBoxValues.top = separateValues[ 0 ]; + sortedBoxValues.right = separateValues[ 1 ]; + sortedBoxValues.bottom = separateValues[ 2 ]; + sortedBoxValues.left = separateValues[ 1 ]; + break; + case 4: + sortedBoxValues.top = separateValues[ 0 ]; + sortedBoxValues.right = separateValues[ 1 ]; + sortedBoxValues.bottom = separateValues[ 2 ]; + sortedBoxValues.left = separateValues[ 3 ]; + break; + } + + Object.entries( properties ).forEach( ( entry ) => { + const [ name, prop ] = entry; + const cssProperty = name.startsWith( '--' ) + ? name + : kebabCase( name ); + declarations.push( + `${ cssProperty }: ${ compileStyleValue( + get( sortedBoxValues, [ prop ] ) + ) }` + ); + } ); } else if ( get( blockStyles, pathToValue, false ) ) { const cssProperty = key.startsWith( '--' ) ? key @@ -218,6 +274,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 ); @@ -360,6 +420,7 @@ export const toCustomProperties = ( tree, blockSelectors ) => { export const toStyles = ( tree, blockSelectors, hasBlockGapSupport ) => { const nodesWithStyles = getNodesWithStyles( tree, blockSelectors ); const nodesWithSettings = getNodesWithSettings( tree, blockSelectors ); + const useRootVars = tree?.settings?.useRootVariables; /* * Reset default browser margin on the root body element. @@ -370,6 +431,18 @@ export const toStyles = ( tree, blockSelectors, hasBlockGapSupport ) => { * @link https://github.com/WordPress/gutenberg/issues/36147. */ let ruleset = 'body {margin: 0;}'; + + // Add alignment rules. + // These are also implemented in class-wp-theme-json.php + if ( useRootVars ) { + ruleset += + '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; + ruleset += + '.wp-site-blocks,.wp-block-group.alignfull,.wp-block-group.has-background,.wp-block-columns.alignfull.has-background,.wp-block-cover.alignfull { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + ruleset += + '.wp-site-blocks .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); width: unset; }'; + } + nodesWithStyles.forEach( ( { selector, duotoneSelector, styles } ) => { const duotoneStyles = {}; if ( styles?.filter ) { @@ -389,7 +462,11 @@ export const toStyles = ( tree, blockSelectors, hasBlockGapSupport ) => { } // Process the remaning block styles (they use either normal block class or __experimentalSelector). - const declarations = getStylesDeclarations( styles ); + const declarations = getStylesDeclarations( + styles, + selector, + useRootVars + ); if ( declarations.length === 0 ) { return; } diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 89240870399dc9..d8326f912a69f1 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -594,9 +594,9 @@ function test_get_stylesheet_preset_rules_come_after_block_rules() { ) ); + $variables = '.wp-block-group{--wp--preset--color--grey: grey;}'; $styles = 'body { margin: 0; }.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; }.wp-block-group{color: red;}'; $presets = '.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}'; - $variables = '.wp-block-group{--wp--preset--color--grey: grey;}'; $all = $variables . $styles . $presets; $this->assertEquals( $all, $theme_json->get_stylesheet() ); $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );