Make WordPress Core


Ignore:
Timestamp:
09/14/2022 02:19:16 PM (2 years ago)
Author:
audrasjb
Message:

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

This change backports the following changes from 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

Note that it doesn't entirely port over PR40875 — the remaining PHP changes for that PR will be explored in a separate PR targeting layout.php.

Props andrewserong, aaronrobertshaw, isabel_brison.
See #56467.

File:
1 edited

Legend:

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

    r54133 r54159  
    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,
    284306            'wideSize'    => null,
    285307        ),
    286         'spacing'         => array(
     308        'spacing'                       => array(
    287309            'blockGap' => null,
    288310            'margin'   => null,
     
    290312            'units'    => null,
    291313        ),
    292         'typography'      => array(
     314        'typography'                    => array(
    293315            'customFontSize' => null,
    294316            'dropCap'        => null,
     
    311333     *              added new properties for `border`, `filter`, `spacing`,
    312334     *              and `typography`.
     335     * @since 6.1.0 Added new side properties for `border`,
     336     *              updated `blockGap` to be allowed at any level.
    313337     * @var array
    314338     */
     
    319343            'style'  => null,
    320344            'width'  => null,
     345            'top'    => null,
     346            'right'  => null,
     347            'bottom' => null,
     348            'left'   => null,
    321349        ),
    322350        'color'      => array(
     
    331359            'margin'   => null,
    332360            'padding'  => null,
    333             'blockGap' => 'top',
     361            'blockGap' => null,
    334362        ),
    335363        'typography' => array(
     
    382410        'button'  => 'wp-element-button',
    383411        'caption' => 'wp-element-caption',
     412    );
     413
     414    /**
     415     * List of block support features that can have their related styles
     416     * generated under their own feature level selector rather than the block's.
     417     *
     418     * @since 6.1.0
     419     * @var string[]
     420     */
     421    const BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS = array(
     422        '__experimentalBorder' => 'border',
     423        'color'                => 'color',
     424        'spacing'              => 'spacing',
     425        'typography'           => 'typography',
    384426    );
    385427
     
    657699     * @since 5.8.0
    658700     * @since 5.9.0 Added `duotone` key with CSS selector.
     701     * @since 6.1.0 Added `features` key with block support feature level selectors.
    659702     *
    660703     * @return array Block metadata.
     
    684727            ) {
    685728                static::$blocks_metadata[ $block_name ]['duotone'] = $block_type->supports['color']['__experimentalDuotone'];
     729            }
     730
     731            // Generate block support feature level selectors if opted into
     732            // for the current block.
     733            $features = array();
     734            foreach ( static::BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS as $key => $feature ) {
     735                if (
     736                    isset( $block_type->supports[ $key ]['__experimentalSelector'] ) &&
     737                    $block_type->supports[ $key ]['__experimentalSelector']
     738                ) {
     739                    $features[ $feature ] = static::scope_selector(
     740                        static::$blocks_metadata[ $block_name ]['selector'],
     741                        $block_type->supports[ $key ]['__experimentalSelector']
     742                    );
     743                }
     744            }
     745
     746            if ( ! empty( $features ) ) {
     747                static::$blocks_metadata[ $block_name ]['features'] = $features;
    686748            }
    687749
     
    811873
    812874        if ( in_array( 'styles', $types, true ) ) {
     875            $root_block_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $style_nodes, 'selector' ), true );
     876
     877            if ( false !== $root_block_key ) {
     878                $stylesheet .= $this->get_root_layout_rules( static::ROOT_BLOCK_SELECTOR, $style_nodes[ $root_block_key ] );
     879            }
    813880            $stylesheet .= $this->get_block_classes( $style_nodes );
     881        } elseif ( in_array( 'base-layout-styles', $types, true ) ) {
     882            // Base layout styles are provided as part of `styles`, so only output separately if explicitly requested.
     883            // For backwards compatibility, the Columns block is explicitly included, to support a different default gap value.
     884            $base_styles_nodes = array(
     885                array(
     886                    'path'     => array( 'styles' ),
     887                    'selector' => static::ROOT_BLOCK_SELECTOR,
     888                ),
     889                array(
     890                    'path'     => array( 'styles', 'blocks', 'core/columns' ),
     891                    'selector' => '.wp-block-columns',
     892                    'name'     => 'core/columns',
     893                ),
     894            );
     895
     896            foreach ( $base_styles_nodes as $base_style_node ) {
     897                $stylesheet .= $this->get_layout_styles( $base_style_node );
     898            }
    814899        }
    815900
     
    885970     *              and no longer returns preset classes.
    886971     *              Removed the `$setting_nodes` parameter.
     972     * @since 6.1.0 Moved most internal logic to `get_styles_for_block()`.
    887973     *
    888974     * @param array $style_nodes Nodes with styles.
     
    899985        }
    900986
     987        return $block_rules;
     988    }
     989
     990    /**
     991     * Get the CSS layout rules for a particular block from theme.json layout definitions.
     992     *
     993     * @since 6.1.0
     994     *
     995     * @param array $block_metadata Metadata about the block to get styles for.
     996     *
     997     * @return string Layout styles for the block.
     998     */
     999    protected function get_layout_styles( $block_metadata ) {
     1000        $block_rules = '';
     1001        $block_type  = null;
     1002
     1003        // Skip outputting layout styles if explicitly disabled.
     1004        if ( current_theme_supports( 'disable-layout-styles' ) ) {
     1005            return $block_rules;
     1006        }
     1007
     1008        if ( isset( $block_metadata['name'] ) ) {
     1009            $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_metadata['name'] );
     1010            if ( ! block_has_support( $block_type, array( '__experimentalLayout' ), false ) ) {
     1011                return $block_rules;
     1012            }
     1013        }
     1014
     1015        $selector                 = isset( $block_metadata['selector'] ) ? $block_metadata['selector'] : '';
     1016        $has_block_gap_support    = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
     1017        $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.
     1018        $node                     = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );
     1019        $layout_definitions       = _wp_array_get( $this->theme_json, array( 'settings', 'layout', 'definitions' ), array() );
     1020        $layout_selector_pattern  = '/^[a-zA-Z0-9\-\.\ *+>:\(\)]*$/'; // Allow alphanumeric classnames, spaces, wildcard, sibling, child combinator and pseudo class selectors.
     1021
     1022        // Gap styles will only be output if the theme has block gap support, or supports a fallback gap.
     1023        // Default layout gap styles will be skipped for themes that do not explicitly opt-in to blockGap with a `true` or `false` value.
     1024        if ( $has_block_gap_support || $has_fallback_gap_support ) {
     1025            $block_gap_value = null;
     1026            // Use a fallback gap value if block gap support is not available.
     1027            if ( ! $has_block_gap_support ) {
     1028                $block_gap_value = static::ROOT_BLOCK_SELECTOR === $selector ? '0.5em' : null;
     1029                if ( ! empty( $block_type ) ) {
     1030                    $block_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), null );
     1031                }
     1032            } else {
     1033                $block_gap_value = static::get_property_value( $node, array( 'spacing', 'blockGap' ) );
     1034            }
     1035
     1036            // Support split row / column values and concatenate to a shorthand value.
     1037            if ( is_array( $block_gap_value ) ) {
     1038                if ( isset( $block_gap_value['top'] ) && isset( $block_gap_value['left'] ) ) {
     1039                    $gap_row         = static::get_property_value( $node, array( 'spacing', 'blockGap', 'top' ) );
     1040                    $gap_column      = static::get_property_value( $node, array( 'spacing', 'blockGap', 'left' ) );
     1041                    $block_gap_value = $gap_row === $gap_column ? $gap_row : $gap_row . ' ' . $gap_column;
     1042                } else {
     1043                    // Skip outputting gap value if not all sides are provided.
     1044                    $block_gap_value = null;
     1045                }
     1046            }
     1047
     1048            // If the block should have custom gap, add the gap styles.
     1049            if ( null !== $block_gap_value && false !== $block_gap_value && '' !== $block_gap_value ) {
     1050                foreach ( $layout_definitions as $layout_definition_key => $layout_definition ) {
     1051                    // Allow outputting fallback gap styles for flex layout type when block gap support isn't available.
     1052                    if ( ! $has_block_gap_support && 'flex' !== $layout_definition_key ) {
     1053                        continue;
     1054                    }
     1055
     1056                    $class_name    = sanitize_title( _wp_array_get( $layout_definition, array( 'className' ), false ) );
     1057                    $spacing_rules = _wp_array_get( $layout_definition, array( 'spacingStyles' ), array() );
     1058
     1059                    if (
     1060                        ! empty( $class_name ) &&
     1061                        ! empty( $spacing_rules )
     1062                    ) {
     1063                        foreach ( $spacing_rules as $spacing_rule ) {
     1064                            $declarations = array();
     1065                            if (
     1066                                isset( $spacing_rule['selector'] ) &&
     1067                                preg_match( $layout_selector_pattern, $spacing_rule['selector'] ) &&
     1068                                ! empty( $spacing_rule['rules'] )
     1069                            ) {
     1070                                // Iterate over each of the styling rules and substitute non-string values such as `null` with the real `blockGap` value.
     1071                                foreach ( $spacing_rule['rules'] as $css_property => $css_value ) {
     1072                                    $current_css_value = is_string( $css_value ) ? $css_value : $block_gap_value;
     1073                                    if ( static::is_safe_css_declaration( $css_property, $current_css_value ) ) {
     1074                                        $declarations[] = array(
     1075                                            'name'  => $css_property,
     1076                                            'value' => $current_css_value,
     1077                                        );
     1078                                    }
     1079                                }
     1080
     1081                                if ( ! $has_block_gap_support ) {
     1082                                    // For fallback gap styles, use lower specificity, to ensure styles do not unintentionally override theme styles.
     1083                                    $format          = static::ROOT_BLOCK_SELECTOR === $selector ? ':where(.%2$s%3$s)' : ':where(%1$s.%2$s%3$s)';
     1084                                    $layout_selector = sprintf(
     1085                                        $format,
     1086                                        $selector,
     1087                                        $class_name,
     1088                                        $spacing_rule['selector']
     1089                                    );
     1090                                } else {
     1091                                    $format          = static::ROOT_BLOCK_SELECTOR === $selector ? '%s .%s%s' : '%s.%s%s';
     1092                                    $layout_selector = sprintf(
     1093                                        $format,
     1094                                        $selector,
     1095                                        $class_name,
     1096                                        $spacing_rule['selector']
     1097                                    );
     1098                                }
     1099                                $block_rules .= static::to_ruleset( $layout_selector, $declarations );
     1100                            }
     1101                        }
     1102                    }
     1103                }
     1104            }
     1105        }
     1106
     1107        // Output base styles.
     1108        if (
     1109            static::ROOT_BLOCK_SELECTOR === $selector
     1110        ) {
     1111            $valid_display_modes = array( 'block', 'flex', 'grid' );
     1112            foreach ( $layout_definitions as $layout_definition ) {
     1113                $class_name       = sanitize_title( _wp_array_get( $layout_definition, array( 'className' ), false ) );
     1114                $base_style_rules = _wp_array_get( $layout_definition, array( 'baseStyles' ), array() );
     1115
     1116                if (
     1117                    ! empty( $class_name ) &&
     1118                    ! empty( $base_style_rules )
     1119                ) {
     1120                    // Output display mode. This requires special handling as `display` is not exposed in `safe_style_css_filter`.
     1121                    if (
     1122                        ! empty( $layout_definition['displayMode'] ) &&
     1123                        is_string( $layout_definition['displayMode'] ) &&
     1124                        in_array( $layout_definition['displayMode'], $valid_display_modes, true )
     1125                    ) {
     1126                        $layout_selector = sprintf(
     1127                            '%s .%s',
     1128                            $selector,
     1129                            $class_name
     1130                        );
     1131                        $block_rules    .= static::to_ruleset(
     1132                            $layout_selector,
     1133                            array(
     1134                                array(
     1135                                    'name'  => 'display',
     1136                                    'value' => $layout_definition['displayMode'],
     1137                                ),
     1138                            )
     1139                        );
     1140                    }
     1141
     1142                    foreach ( $base_style_rules as $base_style_rule ) {
     1143                        $declarations = array();
     1144
     1145                        if (
     1146                            isset( $base_style_rule['selector'] ) &&
     1147                            preg_match( $layout_selector_pattern, $base_style_rule['selector'] ) &&
     1148                            ! empty( $base_style_rule['rules'] )
     1149                        ) {
     1150                            foreach ( $base_style_rule['rules'] as $css_property => $css_value ) {
     1151                                if ( static::is_safe_css_declaration( $css_property, $css_value ) ) {
     1152                                    $declarations[] = array(
     1153                                        'name'  => $css_property,
     1154                                        'value' => $css_value,
     1155                                    );
     1156                                }
     1157                            }
     1158
     1159                            $layout_selector = sprintf(
     1160                                '%s .%s%s',
     1161                                $selector,
     1162                                $class_name,
     1163                                $base_style_rule['selector']
     1164                            );
     1165                            $block_rules    .= static::to_ruleset( $layout_selector, $declarations );
     1166                        }
     1167                    }
     1168                }
     1169            }
     1170        }
    9011171        return $block_rules;
    9021172    }
     
    13281598     * @since 5.8.0
    13291599     * @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 ) {
     1600     * @since 6.1.0 Added `$theme_json`, `$selector`, and `$use_root_padding` parameters.
     1601     *
     1602     * @param array   $styles Styles to process.
     1603     * @param array   $settings Theme settings.
     1604     * @param array   $properties Properties metadata.
     1605     * @param array   $theme_json Theme JSON array.
     1606     * @param string  $selector The style block selector.
     1607     * @param boolean $use_root_padding Whether to add custom properties at root level.
     1608     * @return array  Returns the modified $declarations.
     1609     */
     1610    protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) {
    13391611        if ( null === $properties ) {
    13401612            $properties = static::PROPERTIES_METADATA;
     
    13461618        }
    13471619
     1620        $root_variable_duplicates = array();
     1621
    13481622        foreach ( $properties as $css_property => $value_path ) {
    13491623            $value = static::get_property_value( $styles, $value_path, $theme_json );
     1624
     1625            if ( str_starts_with( $css_property, '--wp--style--root--' ) && ( static::ROOT_BLOCK_SELECTOR !== $selector || ! $use_root_padding ) ) {
     1626                continue;
     1627            }
     1628            // Root-level padding styles don't currently support strings with CSS shorthand values.
     1629            // This may change: https://github.com/WordPress/gutenberg/issues/40132.
     1630            if ( '--wp--style--root--padding' === $css_property && is_string( $value ) ) {
     1631                continue;
     1632            }
     1633
     1634            if ( str_starts_with( $css_property, '--wp--style--root--' ) && $use_root_padding ) {
     1635                $root_variable_duplicates[] = substr( $css_property, strlen( '--wp--style--root--' ) );
     1636            }
    13501637
    13511638            // Look up protected properties, keyed by value path.
     
    13731660        }
    13741661
     1662        // If a variable value is added to the root, the corresponding property should be removed.
     1663        foreach ( $root_variable_duplicates as $duplicate ) {
     1664            $discard = array_search( $duplicate, array_column( $declarations, 'name' ), true );
     1665            if ( is_numeric( $discard ) ) {
     1666                array_splice( $declarations, $discard, 1 );
     1667            }
     1668        }
     1669
    13751670        return $declarations;
    13761671    }
     
    13971692     */
    13981693    protected static function get_property_value( $styles, $path, $theme_json = null ) {
    1399         $value = _wp_array_get( $styles, $path, '' );
     1694        $value = _wp_array_get( $styles, $path );
    14001695
    14011696        /*
     
    14301725        }
    14311726
    1432         if ( '' === $value || is_array( $value ) ) {
     1727        if ( is_array( $value ) ) {
    14331728            return $value;
    14341729        }
     
    16231918            }
    16241919
     1920            $feature_selectors = null;
     1921            if ( isset( $selectors[ $name ]['features'] ) ) {
     1922                $feature_selectors = $selectors[ $name ]['features'];
     1923            }
     1924
    16251925            $nodes[] = array(
    16261926                'name'     => $name,
     
    16281928                'selector' => $selector,
    16291929                'duotone'  => $duotone_selector,
     1930                'features' => $feature_selectors,
    16301931            );
    16311932
     
    16601961     * @since 6.1.0
    16611962     *
    1662      * @param array $block_metadata Meta data about the block to get styles for.
     1963     * @param array $block_metadata Metadata about the block to get styles for.
     1964     *
    16631965     * @return array Styles for the block.
    16641966     */
    16651967    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' ) );
     1968        $node             = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );
     1969        $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments'];
     1970        $selector         = $block_metadata['selector'];
     1971        $settings         = _wp_array_get( $this->theme_json, array( 'settings' ) );
     1972
     1973        /*
     1974         * Process style declarations for block support features the current
     1975         * block contains selectors for. Values for a feature with a custom
     1976         * selector are filtered from the theme.json node before it is
     1977         * processed as normal.
     1978        */
     1979        $feature_declarations = array();
     1980
     1981        if ( ! empty( $block_metadata['features'] ) ) {
     1982            foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) {
     1983                if ( ! empty( $node[ $feature_name ] ) ) {
     1984                    // Create temporary node containing only the feature data
     1985                    // to leverage existing `compute_style_properties` function.
     1986                    $feature = array( $feature_name => $node[ $feature_name ] );
     1987                    // Generate the feature's declarations only.
     1988                    $new_feature_declarations = static::compute_style_properties( $feature, $settings, null, $this->theme_json );
     1989
     1990                    // Merge new declarations with any that already exist for
     1991                    // the feature selector. This may occur when multiple block
     1992                    // support features use the same custom selector.
     1993                    if ( isset( $feature_declarations[ $feature_selector ] ) ) {
     1994                        $feature_declarations[ $feature_selector ] = array_merge( $feature_declarations[ $feature_selector ], $new_feature_declarations );
     1995                    } else {
     1996                        $feature_declarations[ $feature_selector ] = $new_feature_declarations;
     1997                    }
     1998                    // Remove the feature from the block's node now the
     1999                    // styles will be included under the feature level selector.
     2000                    unset( $node[ $feature_name ] );
     2001                }
     2002            }
     2003        }
    16712004
    16722005        /*
     
    17102043            && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true )
    17112044        ) {
    1712             $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json );
     2045            $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding );
    17132046        } else {
    1714             $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json );
     2047            $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding );
    17152048        }
    17162049
     
    17292062        }
    17302063
    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 
    17432064        // 2. Generate and append the rules that use the general selector.
    17442065        $block_rules .= static::to_ruleset( $selector, $declarations );
     
    17502071        }
    17512072
    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             }
     2073        // 4. Generate Layout block gap styles.
     2074        if (
     2075            static::ROOT_BLOCK_SELECTOR !== $selector &&
     2076            ! empty( $block_metadata['name'] )
     2077        ) {
     2078            $block_rules .= $this->get_layout_styles( $block_metadata );
     2079        }
     2080
     2081        // 5. Generate and append the feature level rulesets.
     2082        foreach ( $feature_declarations as $feature_selector => $individual_feature_declarations ) {
     2083            $block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations );
    17622084        }
    17632085
    17642086        return $block_rules;
     2087    }
     2088
     2089    /**
     2090     * Outputs the CSS for layout rules on the root.
     2091     *
     2092     * @since 6.1.0
     2093     *
     2094     * @param string $selector The root node selector.
     2095     * @param array  $block_metadata The metadata for the root block.
     2096     * @return string The additional root rules CSS.
     2097     */
     2098    public function get_root_layout_rules( $selector, $block_metadata ) {
     2099        $css              = '';
     2100        $settings         = _wp_array_get( $this->theme_json, array( 'settings' ) );
     2101        $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments'];
     2102
     2103        /*
     2104        * Reset default browser margin on the root body element.
     2105        * This is set on the root selector **before** generating the ruleset
     2106        * from the `theme.json`. This is to ensure that if the `theme.json` declares
     2107        * `margin` in its `spacing` declaration for the `body` element then these
     2108        * user-generated values take precedence in the CSS cascade.
     2109        * @link https://github.com/WordPress/gutenberg/issues/36147.
     2110        */
     2111        $css .= 'body { margin: 0;';
     2112
     2113        /*
     2114        * If there are content and wide widths in theme.json, output them
     2115        * as custom properties on the body element so all blocks can use them.
     2116        */
     2117        if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) {
     2118            $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize'];
     2119            $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial';
     2120            $wide_size    = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize'];
     2121            $wide_size    = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial';
     2122            $css         .= '--wp--style--global--content-size: ' . $content_size . ';';
     2123            $css         .= '--wp--style--global--wide-size: ' . $wide_size . ';';
     2124        }
     2125
     2126        $css .= ' }';
     2127
     2128        if ( $use_root_padding ) {
     2129            // Top and bottom padding are applied to the outer block container.
     2130            $css .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }';
     2131            // Right and left padding are applied to the first container with `.has-global-padding` class.
     2132            $css .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }';
     2133            // Nested containers with `.has-global-padding` class do not get padding.
     2134            $css .= '.has-global-padding :where(.has-global-padding) { padding-right: 0; padding-left: 0; }';
     2135            // Alignfull children of the container with left and right padding have negative margins so they can still be full width.
     2136            $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); }';
     2137            // The above rule is negated for alignfull children of nested containers.
     2138            $css .= '.has-global-padding :where(.has-global-padding) > .alignfull { margin-right: 0; margin-left: 0; }';
     2139            // Some of the children of alignfull blocks without content width should also get padding: text blocks and non-alignfull container blocks.
     2140            $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); }';
     2141            // The above rule also has to be negated for blocks inside nested `.has-global-padding` blocks.
     2142            $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; }';
     2143        }
     2144
     2145        $css .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }';
     2146        $css .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }';
     2147        $css .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
     2148
     2149        $block_gap_value       = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' );
     2150        $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
     2151        if ( $has_block_gap_support ) {
     2152            $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) );
     2153            $css            .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }';
     2154            $css            .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }";
     2155
     2156            // For backwards compatibility, ensure the legacy block gap CSS variable is still available.
     2157            $css .= "$selector { --wp--style--block-gap: $block_gap_value; }";
     2158        }
     2159        $css .= $this->get_layout_styles( $block_metadata );
     2160
     2161        return $css;
    17652162    }
    17662163
Note: See TracChangeset for help on using the changeset viewer.