From aebead14831e505bb28bc2f5a0377b757a62c8db Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 31 Mar 2022 17:33:25 +1100 Subject: [PATCH] Try using variable for root padding value Root vars shouldn't apply to block global styles Fix issue with shorthand padding on server side. Temp fix for file path error Fix site editor top padding not updating Support shorthand properties in site editor. Fix full widths in the post editor Check selector inside function. Fix kebab-casing of CSS variables. Fix borked merge conflict solving Move post editor full width styles to edit-post package. Set default padding value to 0. Update test string. Fix PHP unit tests Fix PHP lint Fix failing PHP tests. Use block gap variable as default root padding value. Fix linting errors. Add opt-in setting via package.json Generate correct block editor styles Fix tests and add prop to core theme.json Fix unit tests properly this time Merge remote-tracking branch 'origin/trunk' into try/root-padding-fix Added missing doc comment for parameter "$use_root_vars" fix rebase --- lib/block-supports/layout.php | 2 +- .../wordpress-6.0/class-wp-theme-json-6-0.php | 262 ++++++++++++++---- .../class-wp-theme-json-resolver-6-0.php | 1 + lib/compat/wordpress-6.0/theme.json | 11 +- .../src/components/block-list/style.scss | 16 ++ packages/block-editor/src/hooks/style.js | 3 + packages/block-editor/src/layouts/flow.js | 2 +- packages/blocks/src/api/constants.js | 11 + .../test/use-global-styles-output.js | 5 +- .../global-styles/use-global-styles-output.js | 83 +++++- phpunit/class-wp-theme-json-test.php | 12 +- 11 files changed, 335 insertions(+), 73 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index 871cad2c20022a..fbf3676b9d6b2b 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -66,7 +66,7 @@ function gutenberg_get_layout_style( $selector, $layout, $padding, $has_block_ga isset( $padding['left'] ) ? $padding['left'] : 0 ); $style .= '}'; - $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.0/class-wp-theme-json-6-0.php b/lib/compat/wordpress-6.0/class-wp-theme-json-6-0.php index cb8015beec604d..27bce4e80d20e4 100644 --- a/lib/compat/wordpress-6.0/class-wp-theme-json-6-0.php +++ b/lib/compat/wordpress-6.0/class-wp-theme-json-6-0.php @@ -22,49 +22,54 @@ class WP_Theme_JSON_6_0 extends WP_Theme_JSON_5_9 { * 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--block-gap' => array( 'spacing', 'blockGap' ), - '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' ), + '--wp--style--block-gap' => array( 'spacing', 'blockGap' ), + 'text-decoration' => array( 'typography', 'textDecoration' ), + 'text-transform' => array( 'typography', 'textTransform' ), + 'filter' => array( 'filter', 'duotone' ), ); /** @@ -202,14 +207,15 @@ class WP_Theme_JSON_6_0 extends WP_Theme_JSON_5_9 { * @var array */ const VALID_SETTINGS = array( - 'appearanceTools' => null, - 'border' => array( + 'appearanceTools' => null, + 'useRootVariables' => null, + 'border' => array( 'color' => null, 'radius' => null, 'style' => null, 'width' => null, ), - 'color' => array( + 'color' => array( 'background' => null, 'custom' => null, 'customDuotone' => null, @@ -223,19 +229,19 @@ class WP_Theme_JSON_6_0 extends WP_Theme_JSON_5_9 { 'palette' => null, 'text' => null, ), - 'custom' => null, - 'layout' => array( + 'custom' => null, + 'layout' => array( 'contentSize' => null, 'wideSize' => null, 'padding' => null, ), - 'spacing' => array( + 'spacing' => array( 'blockGap' => null, 'margin' => null, 'padding' => null, 'units' => null, ), - 'typography' => array( + 'typography' => array( 'customFontSize' => null, 'dropCap' => null, 'fontFamilies' => null, @@ -326,10 +332,11 @@ protected function get_block_classes( $style_nodes ) { continue; } - $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 ); + $use_root_vars = _wp_array_get( $this->theme_json, array( 'settings', 'useRootVariables' ), array() ); + $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. @@ -363,6 +370,16 @@ protected function get_block_classes( $style_nodes ) { } if ( static::ROOT_BLOCK_SELECTOR === $selector ) { + 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); }'; + + // 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 .= '.is-root-container > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }'; + } + } $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; }'; @@ -530,6 +547,143 @@ protected static function get_metadata_boolean( $data, $path, $default = false ) return $default; } + /** + * 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 && 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 ) ) { + + $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 ( $discard ) { + array_splice( $declarations, $discard, 1 ); + } + } + + return $declarations; + } + /** * Returns a valid theme.json as provided by a theme. * diff --git a/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php b/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php index cc6aa688493dea..11c20951d7faf6 100644 --- a/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php +++ b/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php @@ -16,6 +16,7 @@ * @access private */ class WP_Theme_JSON_Resolver_6_0 extends WP_Theme_JSON_Resolver_5_9 { + /** * Given a theme.json structure modifies it in place * to update certain values by its translated strings diff --git a/lib/compat/wordpress-6.0/theme.json b/lib/compat/wordpress-6.0/theme.json index 7691aa4a64e6a9..631201a70063c5 100644 --- a/lib/compat/wordpress-6.0/theme.json +++ b/lib/compat/wordpress-6.0/theme.json @@ -2,6 +2,7 @@ "version": 2, "settings": { "appearanceTools": false, + "useRootVariables": false, "border": { "color": false, "radius": false, @@ -240,6 +241,14 @@ } }, "styles": { - "spacing": { "blockGap": "24px" } + "spacing": { + "blockGap": "24px", + "padding": { + "top": "var(--wp--style--block-gap)", + "right": "var(--wp--style--block-gap)", + "bottom": "var(--wp--style--block-gap)", + "left": "var(--wp--style--block-gap)" + } + } } } diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index 7b78148328aecf..51156e4b3af90a 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -308,6 +308,22 @@ } } +// Active entity spotlight. +// Disable if focus mode is active. +.is-root-container:not(.is-focus-mode) .block-editor-block-list__block.has-active-entity { + opacity: 0.5; + transition: opacity 0.1s linear; + @include reduce-motion("transition"); + + &.is-active-entity, + &.has-child-selected, + &:not(.has-child-selected) .block-editor-block-list__block, + &.is-active-entity .block-editor-block-list__block, + .is-active-entity .block-editor-block-list__block { + opacity: 1; + } +} + .wp-block[data-align="left"] > *, .wp-block[data-align="right"] > *, .wp-block.alignleft, 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 84c3a5ec2fbb2d..4c263ea4daf218 100644 --- a/packages/block-editor/src/layouts/flow.js +++ b/packages/block-editor/src/layouts/flow.js @@ -140,7 +140,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 b7e9d11524ac77..ee2e1f8a0d8cb5 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -203,6 +203,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 10e1d533e025ba..1ea6261e6d3d1e 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 @@ -443,10 +443,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;}' + - '.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;}' + '.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }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;}.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 9a8ad4f54282b7..4b1a1df822393a 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 ); @@ -367,6 +427,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. @@ -377,6 +438,11 @@ export const toStyles = ( tree, blockSelectors, hasBlockGapSupport ) => { * @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); } }'; + } + nodesWithStyles.forEach( ( { selector, duotoneSelector, styles } ) => { const duotoneStyles = {}; if ( styles?.filter ) { @@ -396,7 +462,12 @@ 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 ) { ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`; } diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index ed5b867e775345..6129acc0df2583 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -351,7 +351,7 @@ function test_get_stylesheet_support_for_shorthand_and_longhand_values() { ) ); - $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{border-radius: 10px;margin: 1em;padding: 24px;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;padding-top: 15px;}'; + $styles = 'body { margin: 0; }body{--wp--style--root--padding-top: ;--wp--style--root--padding-right: ;--wp--style--root--padding-bottom: ;--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; }.wp-block-group{border-radius: 10px;margin: 1em;padding: 24px;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;padding-top: 15px;}'; $this->assertEquals( $styles, $theme_json->get_stylesheet() ); $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) ); } @@ -380,7 +380,7 @@ function test_get_stylesheet_skips_disabled_protected_properties() { ) ); - $expected = '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; }'; + $expected = 'body { margin: 0; }body{--wp--style--root--padding-top: ;--wp--style--root--padding-right: ;--wp--style--root--padding-bottom: ;--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_stylesheet() ); $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) ); } @@ -402,7 +402,7 @@ function test_get_stylesheet_renders_enabled_protected_properties() { ) ); - $expected = 'body { margin: 0; }body{--wp--style--block-gap: 1em;}.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-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }'; + $expected = 'body { margin: 0; }body{--wp--style--root--padding-top: ;--wp--style--root--padding-right: ;--wp--style--root--padding-bottom: ;--wp--style--root--padding-left: ;--wp--style--block-gap: 1em;}.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-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }'; $this->assertEquals( $expected, $theme_json->get_stylesheet() ); $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) ); } @@ -528,7 +528,7 @@ function test_get_stylesheet() { ); $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}'; - $styles = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);}.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-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}'; + $styles = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);--wp--style--root--padding-top: ;--wp--style--root--padding-right: ;--wp--style--root--padding-bottom: ;--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; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}'; $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}'; $all = $variables . $styles . $presets; $this->assertEquals( $all, $theme_json->get_stylesheet() ); @@ -594,7 +594,7 @@ function test_get_stylesheet_preset_rules_come_after_block_rules() { ) ); - $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;}'; + $styles = 'body { margin: 0; }body{--wp--style--root--padding-top: ;--wp--style--root--padding-right: ;--wp--style--root--padding-bottom: ;--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; }.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; @@ -680,7 +680,7 @@ public function test_get_stylesheet_preset_values_are_marked_as_important() { ); $this->assertEquals( - 'body{--wp--preset--color--grey: grey;}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; }p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', + 'body{--wp--preset--color--grey: grey;}body { margin: 0; }body{--wp--style--root--padding-top: ;--wp--style--root--padding-right: ;--wp--style--root--padding-bottom: ;--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; }p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', $theme_json->get_stylesheet() ); }