Make WordPress Core


Ignore:
Timestamp:
09/14/2022 06:42:04 PM (4 years ago)
Author:
hellofromTonya
Message:

Editor: Backport foundation for Layout block support refactor (part 1).

Backports the following changes from the Gutenberg repository:

  • [WordPress/gutenberg/40875 gutenberg/40875] Layout: Use semantic classnames, centralize layout definitions, reduce duplication, and fix blockGap in theme.json
  • [WordPress/gutenberg/42544 gutenberg/42544] Layout: Add a disable-layout-styles theme supports flag to opt out of all layout styles gutenberg/42544
  • [WordPress/gutenberg/42087 gutenberg/42087] Theme.json: Add block support feature level selectors for blocks gutenberg/42087
  • [WordPress/gutenberg/43792 gutenberg/43792] Global Styles: Split root layout rules into a different function gutenberg/43792
  • [WordPress/gutenberg/42544 gutenberg/42544] Layout: Add a disable-layout-styles theme supports flag to opt out of all layout styles gutenberg/42544
  • [WordPress/gutenberg/42665 gutenberg/42665] Layout: Reduce specificity of fallback blockGap styles gutenberg/42665
  • [WordPress/gutenberg/42085 gutenberg/42085] Core CSS support for root padding and alignfull blocks gutenberg/42085

Notes:

  • It doesn't entirely port over PR 40875 — the remaining PHP changes for that PR will be explored in a separate PR targeting layout.php.
  • [54159] was reverted in [54160] due to PHPUnit test failures for tests added by the commit. Later, tests passed when applied on top of trunk. There were various outages today of upstream wp-env dependencies, which likely were the root cause of the earlier failures. For historical tracking and to make sure, recommitting [54159] but instead on top of current trunk. See PR 3205 for more details.
  • Giving additional props for those who did a deep dive investigation into the failed tests.

Follow-up to [54160], [54159].

Props andrewserong, aaronrobertshaw, isabel_brison, bernhard-reiter, hellofromTonya.
See #56467.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-theme-json.php

    r54160 r54162  
    178178         *              `text-decoration`, `text-transform`, and `filter` properties,
    179179         *              simplified the metadata structure.
     180         * @since 6.1.0 Added the `border-*-color`, `border-*-width`, `border-*-style`,
     181         *              `--wp--style--root--padding-*`, and `box-shadow` properties,
     182         *              removed the `--wp--style--block-gap` property.
    180183         * @var array
    181184         */
    182185        const PROPERTIES_METADATA = array(
    183                 'background'                 => array( 'color', 'gradient' ),
    184                 'background-color'           => array( 'color', 'background' ),
    185                 'border-radius'              => array( 'border', 'radius' ),
    186                 'border-top-left-radius'     => array( 'border', 'radius', 'topLeft' ),
    187                 'border-top-right-radius'    => array( 'border', 'radius', 'topRight' ),
    188                 'border-bottom-left-radius'  => array( 'border', 'radius', 'bottomLeft' ),
    189                 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ),
    190                 'border-color'               => array( 'border', 'color' ),
    191                 'border-width'               => array( 'border', 'width' ),
    192                 'border-style'               => array( 'border', 'style' ),
    193                 'color'                      => array( 'color', 'text' ),
    194                 'font-family'                => array( 'typography', 'fontFamily' ),
    195                 'font-size'                  => array( 'typography', 'fontSize' ),
    196                 'font-style'                 => array( 'typography', 'fontStyle' ),
    197                 'font-weight'                => array( 'typography', 'fontWeight' ),
    198                 'letter-spacing'             => array( 'typography', 'letterSpacing' ),
    199                 'line-height'                => array( 'typography', 'lineHeight' ),
    200                 'margin'                     => array( 'spacing', 'margin' ),
    201                 'margin-top'                 => array( 'spacing', 'margin', 'top' ),
    202                 'margin-right'               => array( 'spacing', 'margin', 'right' ),
    203                 'margin-bottom'              => array( 'spacing', 'margin', 'bottom' ),
    204                 'margin-left'                => array( 'spacing', 'margin', 'left' ),
    205                 'padding'                    => array( 'spacing', 'padding' ),
    206                 'padding-top'                => array( 'spacing', 'padding', 'top' ),
    207                 'padding-right'              => array( 'spacing', 'padding', 'right' ),
    208                 'padding-bottom'             => array( 'spacing', 'padding', 'bottom' ),
    209                 'padding-left'               => array( 'spacing', 'padding', 'left' ),
    210                 '--wp--style--block-gap'     => array( 'spacing', 'blockGap' ),
    211                 'text-decoration'            => array( 'typography', 'textDecoration' ),
    212                 'text-transform'             => array( 'typography', 'textTransform' ),
    213                 'filter'                     => array( 'filter', 'duotone' ),
     186                'background'                        => array( 'color', 'gradient' ),
     187                'background-color'                  => array( 'color', 'background' ),
     188                'border-radius'                     => array( 'border', 'radius' ),
     189                'border-top-left-radius'            => array( 'border', 'radius', 'topLeft' ),
     190                'border-top-right-radius'           => array( 'border', 'radius', 'topRight' ),
     191                'border-bottom-left-radius'         => array( 'border', 'radius', 'bottomLeft' ),
     192                'border-bottom-right-radius'        => array( 'border', 'radius', 'bottomRight' ),
     193                'border-color'                      => array( 'border', 'color' ),
     194                'border-width'                      => array( 'border', 'width' ),
     195                'border-style'                      => array( 'border', 'style' ),
     196                'border-top-color'                  => array( 'border', 'top', 'color' ),
     197                'border-top-width'                  => array( 'border', 'top', 'width' ),
     198                'border-top-style'                  => array( 'border', 'top', 'style' ),
     199                'border-right-color'                => array( 'border', 'right', 'color' ),
     200                'border-right-width'                => array( 'border', 'right', 'width' ),
     201                'border-right-style'                => array( 'border', 'right', 'style' ),
     202                'border-bottom-color'               => array( 'border', 'bottom', 'color' ),
     203                'border-bottom-width'               => array( 'border', 'bottom', 'width' ),
     204                'border-bottom-style'               => array( 'border', 'bottom', 'style' ),
     205                'border-left-color'                 => array( 'border', 'left', 'color' ),
     206                'border-left-width'                 => array( 'border', 'left', 'width' ),
     207                'border-left-style'                 => array( 'border', 'left', 'style' ),
     208                'color'                             => array( 'color', 'text' ),
     209                'font-family'                       => array( 'typography', 'fontFamily' ),
     210                'font-size'                         => array( 'typography', 'fontSize' ),
     211                'font-style'                        => array( 'typography', 'fontStyle' ),
     212                'font-weight'                       => array( 'typography', 'fontWeight' ),
     213                'letter-spacing'                    => array( 'typography', 'letterSpacing' ),
     214                'line-height'                       => array( 'typography', 'lineHeight' ),
     215                'margin'                            => array( 'spacing', 'margin' ),
     216                'margin-top'                        => array( 'spacing', 'margin', 'top' ),
     217                'margin-right'                      => array( 'spacing', 'margin', 'right' ),
     218                'margin-bottom'                     => array( 'spacing', 'margin', 'bottom' ),
     219                'margin-left'                       => array( 'spacing', 'margin', 'left' ),
     220                'padding'                           => array( 'spacing', 'padding' ),
     221                'padding-top'                       => array( 'spacing', 'padding', 'top' ),
     222                'padding-right'                     => array( 'spacing', 'padding', 'right' ),
     223                'padding-bottom'                    => array( 'spacing', 'padding', 'bottom' ),
     224                'padding-left'                      => array( 'spacing', 'padding', 'left' ),
     225                '--wp--style--root--padding'        => array( 'spacing', 'padding' ),
     226                '--wp--style--root--padding-top'    => array( 'spacing', 'padding', 'top' ),
     227                '--wp--style--root--padding-right'  => array( 'spacing', 'padding', 'right' ),
     228                '--wp--style--root--padding-bottom' => array( 'spacing', 'padding', 'bottom' ),
     229                '--wp--style--root--padding-left'   => array( 'spacing', 'padding', 'left' ),
     230                'text-decoration'                   => array( 'typography', 'textDecoration' ),
     231                'text-transform'                    => array( 'typography', 'textTransform' ),
     232                'filter'                            => array( 'filter', 'duotone' ),
     233                'box-shadow'                        => array( 'shadow' ),
    214234        );
    215235
    … …  
    255275         *              and `typography`, and renamed others according to the new schema.
    256276         * @since 6.0.0 Added `color.defaultDuotone`.
     277         * @since 6.1.0 Added `layout.definitions` and `useRootPaddingAwareAlignments`.
    257278         * @var array
    258279         */
    259280        const VALID_SETTINGS = array(
    260                 'appearanceTools' => null,
    261                 'border'          => array(
     281                'appearanceTools'               => null,
     282                'useRootPaddingAwareAlignments' => null,
     283                'border'                        => array(
    262284                        'color'  => null,
    263285                        'radius' => null,
    … …  
    265287                        'width'  => null,
    266288                ),
    267                 'color'           => array(
     289                'color'                         => array(
    268290                        'background'       => null,
    269291                        'custom'           => null,
    … …  
    279301                        'text'             => null,
    280302                ),
    281                 'custom'          => null,
    282                 'layout'          => array(
     303                'custom'                        => null,
     304                'layout'                        => array(
    283305                        'contentSize' => null,
     306                        'definitions' => null,
    284307                        'wideSize'    => null,
    285308                ),
    286                 'spacing'         => array(
     309                'spacing'                       => array(
    287310                        'blockGap' => null,
    288311                        'margin'   => null,
    … …  
    290313                        'units'    => null,
    291314                ),
    292                 'typography'      => array(
     315                'typography'                    => array(
    293316                        'customFontSize' => null,
    294317                        'dropCap'        => null,
    … …  
    311334         *              added new properties for `border`, `filter`, `spacing`,
    312335         *              and `typography`.
     336         * @since 6.1.0 Added new side properties for `border`,
     337         *              updated `blockGap` to be allowed at any level.
    313338         * @var array
    314339         */
    … …  
    319344                        'style'  => null,
    320345                        'width'  => null,
     346                        'top'    => null,
     347                        'right'  => null,
     348                        'bottom' => null,
     349                        'left'   => null,
    321350                ),
    322351                'color'      => array(
    … …  
    331360                        'margin'   => null,
    332361                        'padding'  => null,
    333                         'blockGap' => 'top',
     362                        'blockGap' => null,
    334363                ),
    335364                'typography' => array(
    … …  
    382411                'button'  => 'wp-element-button',
    383412                'caption' => 'wp-element-caption',
     413        );
     414
     415        /**
     416         * List of block support features that can have their related styles
     417         * generated under their own feature level selector rather than the block's.
     418         *
     419         * @since 6.1.0
     420         * @var string[]
     421         */
     422        const BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS = array(
     423                '__experimentalBorder' => 'border',
     424                'color'                => 'color',
     425                'spacing'              => 'spacing',
     426                'typography'           => 'typography',
    384427        );
    385428
    … …  
    657700         * @since 5.8.0
    658701         * @since 5.9.0 Added `duotone` key with CSS selector.
     702         * @since 6.1.0 Added `features` key with block support feature level selectors.
    659703         *
    660704         * @return array Block metadata.
    … …  
    684728                        ) {
    685729                                static::$blocks_metadata[ $block_name ]['duotone'] = $block_type->supports['color']['__experimentalDuotone'];
     730                        }
     731
     732                        // Generate block support feature level selectors if opted into
     733                        // for the current block.
     734                        $features = array();
     735                        foreach ( static::BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS as $key => $feature ) {
     736                                if (
     737                                        isset( $block_type->supports[ $key ]['__experimentalSelector'] ) &&
     738                                        $block_type->supports[ $key ]['__experimentalSelector']
     739                                ) {
     740                                        $features[ $feature ] = static::scope_selector(
     741                                                static::$blocks_metadata[ $block_name ]['selector'],
     742                                                $block_type->supports[ $key ]['__experimentalSelector']
     743                                        );
     744                                }
     745                        }
     746
     747                        if ( ! empty( $features ) ) {
     748                                static::$blocks_metadata[ $block_name ]['features'] = $features;
    686749                        }
    687750
    … …  
    811874
    812875                if ( in_array( 'styles', $types, true ) ) {
     876                        $root_block_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $style_nodes, 'selector' ), true );
     877
     878                        if ( false !== $root_block_key ) {
     879                                $stylesheet .= $this->get_root_layout_rules( static::ROOT_BLOCK_SELECTOR, $style_nodes[ $root_block_key ] );
     880                        }
    813881                        $stylesheet .= $this->get_block_classes( $style_nodes );
     882                } elseif ( in_array( 'base-layout-styles', $types, true ) ) {
     883                        // Base layout styles are provided as part of `styles`, so only output separately if explicitly requested.
     884                        // For backwards compatibility, the Columns block is explicitly included, to support a different default gap value.
     885                        $base_styles_nodes = array(
     886                                array(
     887                                        'path'     => array( 'styles' ),
     888                                        'selector' => static::ROOT_BLOCK_SELECTOR,
     889                                ),
     890                                array(
     891                                        'path'     => array( 'styles', 'blocks', 'core/columns' ),
     892                                        'selector' => '.wp-block-columns',
     893                                        'name'     => 'core/columns',
     894                                ),
     895                        );
     896
     897                        foreach ( $base_styles_nodes as $base_style_node ) {
     898                                $stylesheet .= $this->get_layout_styles( $base_style_node );
     899                        }
    814900                }
    815901
    … …  
    885971         *              and no longer returns preset classes.
    886972         *              Removed the `$setting_nodes` parameter.
     973         * @since 6.1.0 Moved most internal logic to `get_styles_for_block()`.
    887974         *
    888975         * @param array $style_nodes Nodes with styles.
    … …  
    899986                }
    900987
     988                return $block_rules;
     989        }
     990
     991        /**
     992         * Get the CSS layout rules for a particular block from theme.json layout definitions.
     993         *
     994         * @since 6.1.0
     995         *
     996         * @param array $block_metadata Metadata about the block to get styles for.
     997         *
     998         * @return string Layout styles for the block.
     999         */
     1000        protected function get_layout_styles( $block_metadata ) {
     1001                $block_rules = '';
     1002                $block_type  = null;
     1003
     1004                // Skip outputting layout styles if explicitly disabled.
     1005                if ( current_theme_supports( 'disable-layout-styles' ) ) {
     1006                        return $block_rules;
     1007                }
     1008
     1009                if ( isset( $block_metadata['name'] ) ) {
     1010                        $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_metadata['name'] );
     1011                        if ( ! block_has_support( $block_type, array( '__experimentalLayout' ), false ) ) {
     1012                                return $block_rules;
     1013                        }
     1014                }
     1015
     1016                $selector                 = isset( $block_metadata['selector'] ) ? $block_metadata['selector'] : '';
     1017                $has_block_gap_support    = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
     1018                $has_fallback_gap_support = ! $has_block_gap_support; // This setting isn't useful yet: it exists as a placeholder for a future explicit fallback gap styles support.
     1019                $node                     = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );
     1020                $layout_definitions       = _wp_array_get( $this->theme_json, array( 'settings', 'layout', 'definitions' ), array() );
     1021                $layout_selector_pattern  = '/^[a-zA-Z0-9\-\.\ *+>:\(\)]*$/'; // Allow alphanumeric classnames, spaces, wildcard, sibling, child combinator and pseudo class selectors.
     1022
     1023                // Gap styles will only be output if the theme has block gap support, or supports a fallback gap.
     1024                // Default layout gap styles will be skipped for themes that do not explicitly opt-in to blockGap with a `true` or `false` value.
     1025                if ( $has_block_gap_support || $has_fallback_gap_support ) {
     1026                        $block_gap_value = null;
     1027                        // Use a fallback gap value if block gap support is not available.
     1028                        if ( ! $has_block_gap_support ) {
     1029                                $block_gap_value = static::ROOT_BLOCK_SELECTOR === $selector ? '0.5em' : null;
     1030                                if ( ! empty( $block_type ) ) {
     1031                                        $block_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), null );
     1032                                }
     1033                        } else {
     1034                                $block_gap_value = static::get_property_value( $node, array( 'spacing', 'blockGap' ) );
     1035                        }
     1036
     1037                        // Support split row / column values and concatenate to a shorthand value.
     1038                        if ( is_array( $block_gap_value ) ) {
     1039                                if ( isset( $block_gap_value['top'] ) && isset( $block_gap_value['left'] ) ) {
     1040                                        $gap_row         = static::get_property_value( $node, array( 'spacing', 'blockGap', 'top' ) );
     1041                                        $gap_column      = static::get_property_value( $node, array( 'spacing', 'blockGap', 'left' ) );
     1042                                        $block_gap_value = $gap_row === $gap_column ? $gap_row : $gap_row . ' ' . $gap_column;
     1043                                } else {
     1044                                        // Skip outputting gap value if not all sides are provided.
     1045                                        $block_gap_value = null;
     1046                                }
     1047                        }
     1048
     1049                        // If the block should have custom gap, add the gap styles.
     1050                        if ( null !== $block_gap_value && false !== $block_gap_value && '' !== $block_gap_value ) {
     1051                                foreach ( $layout_definitions as $layout_definition_key => $layout_definition ) {
     1052                                        // Allow outputting fallback gap styles for flex layout type when block gap support isn't available.
     1053                                        if ( ! $has_block_gap_support && 'flex' !== $layout_definition_key ) {
     1054                                                continue;
     1055                                        }
     1056
     1057                                        $class_name    = sanitize_title( _wp_array_get( $layout_definition, array( 'className' ), false ) );
     1058                                        $spacing_rules = _wp_array_get( $layout_definition, array( 'spacingStyles' ), array() );
     1059
     1060                                        if (
     1061                                                ! empty( $class_name ) &&
     1062                                                ! empty( $spacing_rules )
     1063                                        ) {
     1064                                                foreach ( $spacing_rules as $spacing_rule ) {
     1065                                                        $declarations = array();
     1066                                                        if (
     1067                                                                isset( $spacing_rule['selector'] ) &&
     1068                                                                preg_match( $layout_selector_pattern, $spacing_rule['selector'] ) &&
     1069                                                                ! empty( $spacing_rule['rules'] )
     1070                                                        ) {
     1071                                                                // Iterate over each of the styling rules and substitute non-string values such as `null` with the real `blockGap` value.
     1072                                                                foreach ( $spacing_rule['rules'] as $css_property => $css_value ) {
     1073                                                                        $current_css_value = is_string( $css_value ) ? $css_value : $block_gap_value;
     1074                                                                        if ( static::is_safe_css_declaration( $css_property, $current_css_value ) ) {
     1075                                                                                $declarations[] = array(
     1076                                                                                        'name'  => $css_property,
     1077                                                                                        'value' => $current_css_value,
     1078                                                                                );
     1079                                                                        }
     1080                                                                }
     1081
     1082                                                                if ( ! $has_block_gap_support ) {
     1083                                                                        // For fallback gap styles, use lower specificity, to ensure styles do not unintentionally override theme styles.
     1084                                                                        $format          = static::ROOT_BLOCK_SELECTOR === $selector ? ':where(.%2$s%3$s)' : ':where(%1$s.%2$s%3$s)';
     1085                                                                        $layout_selector = sprintf(
     1086                                                                                $format,
     1087                                                                                $selector,
     1088                                                                                $class_name,
     1089                                                                                $spacing_rule['selector']
     1090                                                                        );
     1091                                                                } else {
     1092                                                                        $format          = static::ROOT_BLOCK_SELECTOR === $selector ? '%s .%s%s' : '%s.%s%s';
     1093                                                                        $layout_selector = sprintf(
     1094                                                                                $format,
     1095                                                                                $selector,
     1096                                                                                $class_name,
     1097                                                                                $spacing_rule['selector']
     1098                                                                        );
     1099                                                                }
     1100                                                                $block_rules .= static::to_ruleset( $layout_selector, $declarations );
     1101                                                        }
     1102                                                }
     1103                                        }
     1104                                }
     1105                        }
     1106                }
     1107
     1108                // Output base styles.
     1109                if (
     1110                        static::ROOT_BLOCK_SELECTOR === $selector
     1111                ) {
     1112                        $valid_display_modes = array( 'block', 'flex', 'grid' );
     1113                        foreach ( $layout_definitions as $layout_definition ) {
     1114                                $class_name       = sanitize_title( _wp_array_get( $layout_definition, array( 'className' ), false ) );
     1115                                $base_style_rules = _wp_array_get( $layout_definition, array( 'baseStyles' ), array() );
     1116
     1117                                if (
     1118                                        ! empty( $class_name ) &&
     1119                                        ! empty( $base_style_rules )
     1120                                ) {
     1121                                        // Output display mode. This requires special handling as `display` is not exposed in `safe_style_css_filter`.
     1122                                        if (
     1123                                                ! empty( $layout_definition['displayMode'] ) &&
     1124                                                is_string( $layout_definition['displayMode'] ) &&
     1125                                                in_array( $layout_definition['displayMode'], $valid_display_modes, true )
     1126                                        ) {
     1127                                                $layout_selector = sprintf(
     1128                                                        '%s .%s',
     1129                                                        $selector,
     1130                                                        $class_name
     1131                                                );
     1132                                                $block_rules    .= static::to_ruleset(
     1133                                                        $layout_selector,
     1134                                                        array(
     1135                                                                array(
     1136                                                                        'name'  => 'display',
     1137                                                                        'value' => $layout_definition['displayMode'],
     1138                                                                ),
     1139                                                        )
     1140                                                );
     1141                                        }
     1142
     1143                                        foreach ( $base_style_rules as $base_style_rule ) {
     1144                                                $declarations = array();
     1145
     1146                                                if (
     1147                                                        isset( $base_style_rule['selector'] ) &&
     1148                                                        preg_match( $layout_selector_pattern, $base_style_rule['selector'] ) &&
     1149                                                        ! empty( $base_style_rule['rules'] )
     1150                                                ) {
     1151                                                        foreach ( $base_style_rule['rules'] as $css_property => $css_value ) {
     1152                                                                if ( static::is_safe_css_declaration( $css_property, $css_value ) ) {
     1153                                                                        $declarations[] = array(
     1154                                                                                'name'  => $css_property,
     1155                                                                                'value' => $css_value,
     1156                                                                        );
     1157                                                                }
     1158                                                        }
     1159
     1160                                                        $layout_selector = sprintf(
     1161                                                                '%s .%s%s',
     1162                                                                $selector,
     1163                                                                $class_name,
     1164                                                                $base_style_rule['selector']
     1165                                                        );
     1166                                                        $block_rules    .= static::to_ruleset( $layout_selector, $declarations );
     1167                                                }
     1168                                        }
     1169                                }
     1170                        }
     1171                }
    9011172                return $block_rules;
    9021173        }
    … …  
    13281599         * @since 5.8.0
    13291600         * @since 5.9.0 Added the `$settings` and `$properties` parameters.
    1330          * @since 6.1.0 Added the `$theme_json` parameter.
    1331          *
    1332          * @param array $styles    Styles to process.
    1333          * @param array $settings  Theme settings.
    1334          * @param array $properties Properties metadata.
    1335          * @param array $theme_json Theme JSON array.
    1336          * @return array Returns the modified $declarations.
    1337          */
    1338         protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null ) {
     1601         * @since 6.1.0 Added `$theme_json`, `$selector`, and `$use_root_padding` parameters.
     1602         *
     1603         * @param array   $styles Styles to process.
     1604         * @param array   $settings Theme settings.
     1605         * @param array   $properties Properties metadata.
     1606         * @param array   $theme_json Theme JSON array.
     1607         * @param string  $selector The style block selector.
     1608         * @param boolean $use_root_padding Whether to add custom properties at root level.
     1609         * @return array  Returns the modified $declarations.
     1610         */
     1611        protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) {
    13391612                if ( null === $properties ) {
    13401613                        $properties = static::PROPERTIES_METADATA;
    … …  
    13461619                }
    13471620
     1621                $root_variable_duplicates = array();
     1622
    13481623                foreach ( $properties as $css_property => $value_path ) {
    13491624                        $value = static::get_property_value( $styles, $value_path, $theme_json );
     1625
     1626                        if ( str_starts_with( $css_property, '--wp--style--root--' ) && ( static::ROOT_BLOCK_SELECTOR !== $selector || ! $use_root_padding ) ) {
     1627                                continue;
     1628                        }
     1629                        // Root-level padding styles don't currently support strings with CSS shorthand values.
     1630                        // This may change: https://github.com/WordPress/gutenberg/issues/40132.
     1631                        if ( '--wp--style--root--padding' === $css_property && is_string( $value ) ) {
     1632                                continue;
     1633                        }
     1634
     1635                        if ( str_starts_with( $css_property, '--wp--style--root--' ) && $use_root_padding ) {
     1636                                $root_variable_duplicates[] = substr( $css_property, strlen( '--wp--style--root--' ) );
     1637                        }
    13501638
    13511639                        // Look up protected properties, keyed by value path.
    … …  
    13731661                }
    13741662
     1663                // If a variable value is added to the root, the corresponding property should be removed.
     1664                foreach ( $root_variable_duplicates as $duplicate ) {
     1665                        $discard = array_search( $duplicate, array_column( $declarations, 'name' ), true );
     1666                        if ( is_numeric( $discard ) ) {
     1667                                array_splice( $declarations, $discard, 1 );
     1668                        }
     1669                }
     1670
    13751671                return $declarations;
    13761672        }
    … …  
    13971693         */
    13981694        protected static function get_property_value( $styles, $path, $theme_json = null ) {
    1399                 $value = _wp_array_get( $styles, $path, '' );
     1695                $value = _wp_array_get( $styles, $path );
    14001696
    14011697                /*
    … …  
    14301726                }
    14311727
    1432                 if ( '' === $value || is_array( $value ) ) {
     1728                if ( is_array( $value ) ) {
    14331729                        return $value;
    14341730                }
    … …  
    16231919                        }
    16241920
     1921                        $feature_selectors = null;
     1922                        if ( isset( $selectors[ $name ]['features'] ) ) {
     1923                                $feature_selectors = $selectors[ $name ]['features'];
     1924                        }
     1925
    16251926                        $nodes[] = array(
    16261927                                'name'     => $name,
    … …  
    16281929                                'selector' => $selector,
    16291930                                'duotone'  => $duotone_selector,
     1931                                'features' => $feature_selectors,
    16301932                        );
    16311933
    … …  
    16601962         * @since 6.1.0
    16611963         *
    1662          * @param array $block_metadata Meta data about the block to get styles for.
    1663          * @return array Styles for the block.
     1964         * @param array $block_metadata Metadata about the block to get styles for.
     1965         *
     1966         * @return string Styles for the block.
    16641967         */
    16651968        public function get_styles_for_block( $block_metadata ) {
    1666 
    1667                 $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );
    1668 
    1669                 $selector = $block_metadata['selector'];
    1670                 $settings = _wp_array_get( $this->theme_json, array( 'settings' ) );
     1969                $node             = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );
     1970                $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments'];
     1971                $selector         = $block_metadata['selector'];
     1972                $settings         = _wp_array_get( $this->theme_json, array( 'settings' ) );
     1973
     1974                /*
     1975                 * Process style declarations for block support features the current
     1976                 * block contains selectors for. Values for a feature with a custom
     1977                 * selector are filtered from the theme.json node before it is
     1978                 * processed as normal.
     1979                */
     1980                $feature_declarations = array();
     1981
     1982                if ( ! empty( $block_metadata['features'] ) ) {
     1983                        foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) {
     1984                                if ( ! empty( $node[ $feature_name ] ) ) {
     1985                                        // Create temporary node containing only the feature data
     1986                                        // to leverage existing `compute_style_properties` function.
     1987                                        $feature = array( $feature_name => $node[ $feature_name ] );
     1988                                        // Generate the feature's declarations only.
     1989                                        $new_feature_declarations = static::compute_style_properties( $feature, $settings, null, $this->theme_json );
     1990
     1991                                        // Merge new declarations with any that already exist for
     1992                                        // the feature selector. This may occur when multiple block
     1993                                        // support features use the same custom selector.
     1994                                        if ( isset( $feature_declarations[ $feature_selector ] ) ) {
     1995                                                $feature_declarations[ $feature_selector ] = array_merge( $feature_declarations[ $feature_selector ], $new_feature_declarations );
     1996                                        } else {
     1997                                                $feature_declarations[ $feature_selector ] = $new_feature_declarations;
     1998                                        }
     1999
     2000                                        // Remove the feature from the block's node now the
     2001                                        // styles will be included under the feature level selector.
     2002                                        unset( $node[ $feature_name ] );
     2003                                }
     2004                        }
     2005                }
    16712006
    16722007                /*
    … …  
    17102045                        && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true )
    17112046                ) {
    1712                         $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json );
     2047                        $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding );
    17132048                } else {
    1714                         $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json );
     2049                        $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding );
    17152050                }
    17162051
    … …  
    17292064                }
    17302065
    1731                 /*
    1732                  * Reset default browser margin on the root body element.
    1733                  * This is set on the root selector **before** generating the ruleset
    1734                  * from the `theme.json`. This is to ensure that if the `theme.json` declares
    1735                  * `margin` in its `spacing` declaration for the `body` element then these
    1736                  * user-generated values take precedence in the CSS cascade.
    1737                  * @link https://github.com/WordPress/gutenberg/issues/36147.
    1738                  */
    1739                 if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
    1740                         $block_rules .= 'body { margin: 0; }';
    1741                 }
    1742 
    17432066                // 2. Generate and append the rules that use the general selector.
    17442067                $block_rules .= static::to_ruleset( $selector, $declarations );
    … …  
    17502073                }
    17512074
    1752                 if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
    1753                         $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }';
    1754                         $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }';
    1755                         $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
    1756 
    1757                         $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
    1758                         if ( $has_block_gap_support ) {
    1759                                 $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }';
    1760                                 $block_rules .= '.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }';
    1761                         }
     2075                // 4. Generate Layout block gap styles.
     2076                if (
     2077                        static::ROOT_BLOCK_SELECTOR !== $selector &&
     2078                        ! empty( $block_metadata['name'] )
     2079                ) {
     2080                        $block_rules .= $this->get_layout_styles( $block_metadata );
     2081                }
     2082
     2083                // 5. Generate and append the feature level rulesets.
     2084                foreach ( $feature_declarations as $feature_selector => $individual_feature_declarations ) {
     2085                        $block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations );
    17622086                }
    17632087
    17642088                return $block_rules;
     2089        }
     2090
     2091        /**
     2092         * Outputs the CSS for layout rules on the root.
     2093         *
     2094         * @since 6.1.0
     2095         *
     2096         * @param string $selector The root node selector.
     2097         * @param array  $block_metadata The metadata for the root block.
     2098         * @return string The additional root rules CSS.
     2099         */
     2100        public function get_root_layout_rules( $selector, $block_metadata ) {
     2101                $css              = '';
     2102                $settings         = _wp_array_get( $this->theme_json, array( 'settings' ) );
     2103                $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments'];
     2104
     2105                /*
     2106                * Reset default browser margin on the root body element.
     2107                * This is set on the root selector **before** generating the ruleset
     2108                * from the `theme.json`. This is to ensure that if the `theme.json` declares
     2109                * `margin` in its `spacing` declaration for the `body` element then these
     2110                * user-generated values take precedence in the CSS cascade.
     2111                * @link https://github.com/WordPress/gutenberg/issues/36147.
     2112                */
     2113                $css .= 'body { margin: 0;';
     2114
     2115                /*
     2116                * If there are content and wide widths in theme.json, output them
     2117                * as custom properties on the body element so all blocks can use them.
     2118                */
     2119                if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) {
     2120                        $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize'];
     2121                        $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial';
     2122                        $wide_size    = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize'];
     2123                        $wide_size    = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial';
     2124                        $css         .= '--wp--style--global--content-size: ' . $content_size . ';';
     2125                        $css         .= '--wp--style--global--wide-size: ' . $wide_size . ';';
     2126                }
     2127
     2128                $css .= ' }';
     2129
     2130                if ( $use_root_padding ) {
     2131                        // Top and bottom padding are applied to the outer block container.
     2132                        $css .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }';
     2133                        // Right and left padding are applied to the first container with `.has-global-padding` class.
     2134                        $css .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }';
     2135                        // Nested containers with `.has-global-padding` class do not get padding.
     2136                        $css .= '.has-global-padding :where(.has-global-padding) { padding-right: 0; padding-left: 0; }';
     2137                        // Alignfull children of the container with left and right padding have negative margins so they can still be full width.
     2138                        $css .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }';
     2139                        // The above rule is negated for alignfull children of nested containers.
     2140                        $css .= '.has-global-padding :where(.has-global-padding) > .alignfull { margin-right: 0; margin-left: 0; }';
     2141                        // Some of the children of alignfull blocks without content width should also get padding: text blocks and non-alignfull container blocks.
     2142                        $css .= '.has-global-padding > .alignfull:where(:not(.has-global-padding)) > :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); }';
     2143                        // The above rule also has to be negated for blocks inside nested `.has-global-padding` blocks.
     2144                        $css .= '.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }';
     2145                }
     2146
     2147                $css .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }';
     2148                $css .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }';
     2149                $css .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
     2150
     2151                $block_gap_value       = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' );
     2152                $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
     2153                if ( $has_block_gap_support ) {
     2154                        $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) );
     2155                        $css            .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }';
     2156                        $css            .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }";
     2157
     2158                        // For backwards compatibility, ensure the legacy block gap CSS variable is still available.
     2159                        $css .= "$selector { --wp--style--block-gap: $block_gap_value; }";
     2160                }
     2161                $css .= $this->get_layout_styles( $block_metadata );
     2162
     2163                return $css;
    17652164        }
    17662165
Note: See TracChangeset for help on using the changeset viewer.