Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Styles: Level global styles specificity at 0-1-0 #6633

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions src/wp-includes/block-supports/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* also be updated.
*
* @since 6.3.0
* @since 6.6.0 Updated specificity for compatibility with 0-1-0 global styles specificity.
* @access private
*
* @return array[] Layout definitions.
Expand Down Expand Up @@ -51,13 +52,13 @@ function wp_get_layout_definitions() {
),
'spacingStyles' => array(
array(
'selector' => ' > :first-child:first-child',
'selector' => ' > :first-child',
'rules' => array(
'margin-block-start' => '0',
),
),
array(
'selector' => ' > :last-child:last-child',
'selector' => ' > :last-child',
'rules' => array(
'margin-block-end' => '0',
),
Expand Down Expand Up @@ -116,13 +117,13 @@ function wp_get_layout_definitions() {
),
'spacingStyles' => array(
array(
'selector' => ' > :first-child:first-child',
'selector' => ' > :first-child',
'rules' => array(
'margin-block-start' => '0',
),
),
array(
'selector' => ' > :last-child:last-child',
'selector' => ' > :last-child',
'rules' => array(
'margin-block-end' => '0',
),
Expand Down Expand Up @@ -150,7 +151,7 @@ function wp_get_layout_definitions() {
),
),
array(
'selector' => ' > *',
'selector' => ' > :is(*, div)', // :is(*, div) instead of just * increases the specificity by 001.
'rules' => array(
'margin' => '0',
),
Expand All @@ -172,7 +173,7 @@ function wp_get_layout_definitions() {
'displayMode' => 'grid',
'baseStyles' => array(
array(
'selector' => ' > *',
'selector' => ' > :is(*, div)', // :is(*, div) instead of just * increases the specificity by 001.
'rules' => array(
'margin' => '0',
),
Expand Down Expand Up @@ -222,6 +223,7 @@ function wp_register_layout_support( $block_type ) {
* @since 5.9.0
* @since 6.1.0 Added `$block_spacing` param, use style engine to enqueue styles.
* @since 6.3.0 Added grid layout type.
* @since 6.6.0 Removed duplicated selector from layout styles.
* @access private
*
* @param string $selector CSS selector.
Expand Down Expand Up @@ -261,7 +263,7 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false
),
),
array(
'selector' => "$selector$selector > * + *",
'selector' => "$selector > * + *",
'declarations' => array(
'margin-block-start' => $gap_value,
'margin-block-end' => '0',
Expand Down Expand Up @@ -370,7 +372,7 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false
),
),
array(
'selector' => "$selector$selector > * + *",
'selector' => "$selector > * + *",
'declarations' => array(
'margin-block-start' => $gap_value,
'margin-block-end' => '0',
Expand Down Expand Up @@ -549,6 +551,7 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false
* @since 5.8.0
* @since 6.3.0 Adds compound class to layout wrapper for global spacing styles.
* @since 6.3.0 Check for layout support via the `layout` key with fallback to `__experimentalLayout`.
* @since 6.6.0 Removed duplicate container class from layout styles.
* @access private
*
* @param string $block_content Rendered block content.
Expand Down Expand Up @@ -795,7 +798,7 @@ function wp_render_layout_support_flag( $block_content, $block ) {
$has_block_gap_support = isset( $block_gap );

$style = wp_get_layout_style(
".$container_class.$container_class",
".$container_class",
$used_layout,
$has_block_gap_support,
$gap_value,
Expand Down
29 changes: 16 additions & 13 deletions src/wp-includes/class-wp-theme-json.php
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +1266,7 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets'
* Processes the CSS, to apply nesting.
*
* @since 6.2.0
* @since 6.6.0 Enforced 0-1-0 specificity for block custom CSS selectors.
*
* @param string $css The CSS to process.
* @param string $selector The selector to nest.
Expand All @@ -1280,7 +1281,7 @@ protected function process_blocks_custom_css( $css, $selector ) {
$is_root_css = ( ! str_contains( $part, '{' ) );
if ( $is_root_css ) {
// If the part doesn't contain braces, it applies to the root level.
$processed_css .= trim( $selector ) . '{' . trim( $part ) . '}';
$processed_css .= ':root :where(' . trim( $selector ) . '){' . trim( $part ) . '}';
} else {
// If the part contains braces, it's a nested CSS rule.
$part = explode( '{', str_replace( '}', '', $part ) );
Expand All @@ -1292,8 +1293,8 @@ protected function process_blocks_custom_css( $css, $selector ) {
$part_selector = str_starts_with( $nested_selector, ' ' )
? static::scope_selector( $selector, $nested_selector )
: static::append_to_selector( $selector, $nested_selector );
$processed_css .= $part_selector . '{' . trim( $css_value ) . '}';
}
$final_selector = ":root :where($part_selector)";
$processed_css .= $final_selector . '{' . trim( $css_value ) . '}';}
}
return $processed_css;
}
Expand Down Expand Up @@ -1414,6 +1415,7 @@ protected function get_block_classes( $style_nodes ) {
* @since 6.3.0 Reduced specificity for layout margin rules.
* @since 6.5.1 Only output rules referencing content and wide sizes when values exist.
* @since 6.5.3 Add types parameter to check if only base layout styles are needed.
* @since 6.6.0 Updated layout style specificity to be compatible with overall 0-1-0 specificity in global styles.
*
* @param array $block_metadata Metadata about the block to get styles for.
* @param array $types Optional. Types of styles to output. If empty, all styles will be output.
Expand All @@ -1440,7 +1442,7 @@ protected function get_layout_styles( $block_metadata, $types = array() ) {
$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.
$node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );
$layout_definitions = wp_get_layout_definitions();
$layout_selector_pattern = '/^[a-zA-Z0-9\-\.\ *+>:\(\)]*$/'; // Allow alphanumeric classnames, spaces, wildcard, sibling, child combinator and pseudo class selectors.
$layout_selector_pattern = '/^[a-zA-Z0-9\-\.\,\ *+>:\(\)]*$/'; // Allow alphanumeric classnames, spaces, wildcard, sibling, child combinator and pseudo class selectors.

/*
* Gap styles will only be output if the theme has block gap support, or supports a fallback gap.
Expand Down Expand Up @@ -1515,7 +1517,7 @@ protected function get_layout_styles( $block_metadata, $types = array() ) {
$spacing_rule['selector']
);
} else {
$format = static::ROOT_BLOCK_SELECTOR === $selector ? ':where(%s .%s) %s' : '%s-%s%s';
$format = static::ROOT_BLOCK_SELECTOR === $selector ? '.%2$s %3$s' : '%1$s-%2$s %3$s';
$layout_selector = sprintf(
$format,
$selector,
Expand Down Expand Up @@ -1599,8 +1601,7 @@ protected function get_layout_styles( $block_metadata, $types = array() ) {
}

$layout_selector = sprintf(
'%s .%s%s',
$selector,
'.%s%s',
$class_name,
$base_style_rule['selector']
);
Expand Down Expand Up @@ -2507,6 +2508,7 @@ private static function get_block_nodes( $theme_json ) {
*
* @since 6.1.0
* @since 6.6.0 Setting a min-height of HTML when root styles have a background gradient or image.
* Updated general global styles specificity to 0-1-0.
*
* @param array $block_metadata Metadata about the block to get styles for.
*
Expand Down Expand Up @@ -2656,7 +2658,7 @@ static function ( $pseudo_selector ) use ( $selector ) {
}

// 2. Generate and append the rules that use the general selector.
$block_rules .= static::to_ruleset( $selector, $declarations );
$block_rules .= static::to_ruleset( ":root :where($selector)", $declarations );

// 3. Generate and append the rules that use the duotone selector.
if ( isset( $block_metadata['duotone'] ) && ! empty( $declarations_duotone ) ) {
Expand All @@ -2673,12 +2675,12 @@ static function ( $pseudo_selector ) use ( $selector ) {

// 5. Generate and append the feature level rulesets.
foreach ( $feature_declarations as $feature_selector => $individual_feature_declarations ) {
$block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations );
$block_rules .= static::to_ruleset( ":root :where($feature_selector)", $individual_feature_declarations );
}

// 6. Generate and append the style variation rulesets.
foreach ( $style_variation_declarations as $style_variation_selector => $individual_style_variation_declarations ) {
$block_rules .= static::to_ruleset( $style_variation_selector, $individual_style_variation_declarations );
$block_rules .= static::to_ruleset( ":root :where($style_variation_selector)", $individual_style_variation_declarations );
}

return $block_rules;
Expand All @@ -2689,6 +2691,7 @@ static function ( $pseudo_selector ) use ( $selector ) {
*
* @since 6.1.0
* @since 6.6.0 Use `ROOT_CSS_PROPERTIES_SELECTOR` for CSS custom properties and improved consistency of root padding rules.
* Updated specificity of body margin reset and first/last child selectors.
*
* @param string $selector The root node selector.
* @param array $block_metadata The metadata for the root block.
Expand Down Expand Up @@ -2720,7 +2723,7 @@ public function get_root_layout_rules( $selector, $block_metadata ) {
* user-generated values take precedence in the CSS cascade.
* @link https://github.com/WordPress/gutenberg/issues/36147.
*/
$css .= 'body { margin: 0; }';
$css .= ':where(body) { margin: 0; }';

if ( $use_root_padding ) {
// Top and bottom padding are applied to the outer block container.
Expand All @@ -2743,8 +2746,8 @@ public function get_root_layout_rules( $selector, $block_metadata ) {
if ( isset( $this->theme_json['settings']['spacing']['blockGap'] ) ) {
$block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) );
$css .= ":where(.wp-site-blocks) > * { margin-block-start: $block_gap_value; margin-block-end: 0; }";
$css .= ':where(.wp-site-blocks) > :first-child:first-child { margin-block-start: 0; }';
$css .= ':where(.wp-site-blocks) > :last-child:last-child { margin-block-end: 0; }';
$css .= ':where(.wp-site-blocks) > :first-child { margin-block-start: 0; }';
$css .= ':where(.wp-site-blocks) > :last-child { margin-block-end: 0; }';

// For backwards compatibility, ensure the legacy block gap CSS variable is still available.
$css .= static::ROOT_CSS_PROPERTIES_SELECTOR . " { --wp--style--block-gap: $block_gap_value; }";
Expand Down
32 changes: 26 additions & 6 deletions tests/phpunit/tests/block-supports/wpGetLayoutStyle.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Tests_Block_Supports_WpGetLayoutStyle extends WP_UnitTestCase {
/**
* @dataProvider data_wp_get_layout_style
* @ticket 56467
* @ticket 61165
*
* @param array $args Dataset to test.
* @param string $expected_output The expected output.
Expand Down Expand Up @@ -72,7 +73,7 @@ public function data_wp_get_layout_style() {
'has_block_gap_support' => true,
'gap_value' => '1em',
),
'expected_output' => '.wp-layout > *{margin-block-start:0;margin-block-end:0;}.wp-layout.wp-layout > * + *{margin-block-start:1em;margin-block-end:0;}',
'expected_output' => '.wp-layout > *{margin-block-start:0;margin-block-end:0;}.wp-layout > * + *{margin-block-start:1em;margin-block-end:0;}',
),
'skip serialization should return empty value' => array(
'args' => array(
Expand All @@ -89,7 +90,7 @@ public function data_wp_get_layout_style() {
'has_block_gap_support' => true,
'gap_value' => array( 'top' => '1em' ),
),
'expected_output' => '.wp-layout > *{margin-block-start:0;margin-block-end:0;}.wp-layout.wp-layout > * + *{margin-block-start:1em;margin-block-end:0;}',
'expected_output' => '.wp-layout > *{margin-block-start:0;margin-block-end:0;}.wp-layout > * + *{margin-block-start:1em;margin-block-end:0;}',
),
'constrained layout with sizes' => array(
'args' => array(
Expand Down Expand Up @@ -128,7 +129,7 @@ public function data_wp_get_layout_style() {
'has_block_gap_support' => true,
'gap_value' => '2.5rem',
),
'expected_output' => '.wp-layout > *{margin-block-start:0;margin-block-end:0;}.wp-layout.wp-layout > * + *{margin-block-start:2.5rem;margin-block-end:0;}',
'expected_output' => '.wp-layout > *{margin-block-start:0;margin-block-end:0;}.wp-layout > * + *{margin-block-start:2.5rem;margin-block-end:0;}',
),
'constrained layout with axial block gap support' => array(
'args' => array(
Expand All @@ -139,7 +140,7 @@ public function data_wp_get_layout_style() {
'has_block_gap_support' => true,
'gap_value' => array( 'top' => '2.5rem' ),
),
'expected_output' => '.wp-layout > *{margin-block-start:0;margin-block-end:0;}.wp-layout.wp-layout > * + *{margin-block-start:2.5rem;margin-block-end:0;}',
'expected_output' => '.wp-layout > *{margin-block-start:0;margin-block-end:0;}.wp-layout > * + *{margin-block-start:2.5rem;margin-block-end:0;}',
),
'constrained layout with block gap support and spacing preset' => array(
'args' => array(
Expand All @@ -150,7 +151,7 @@ public function data_wp_get_layout_style() {
'has_block_gap_support' => true,
'gap_value' => 'var:preset|spacing|50',
),
'expected_output' => '.wp-layout > *{margin-block-start:0;margin-block-end:0;}.wp-layout.wp-layout > * + *{margin-block-start:var(--wp--preset--spacing--50);margin-block-end:0;}',
'expected_output' => '.wp-layout > *{margin-block-start:0;margin-block-end:0;}.wp-layout > * + *{margin-block-start:var(--wp--preset--spacing--50);margin-block-end:0;}',
),
'flex layout with no args should return empty value' => array(
'args' => array(
Expand Down Expand Up @@ -248,6 +249,25 @@ public function data_wp_get_layout_style() {
),
'expected_output' => '.wp-layout{flex-wrap:nowrap;flex-direction:column;align-items:flex-start;justify-content:flex-end;}',
),
'default grid layout' => array(
'args' => array(
'selector' => '.wp-layout',
'layout' => array(
'type' => 'grid',
),
),
'expected_output' => '.wp-layout{grid-template-columns:repeat(auto-fill, minmax(min(12rem, 100%), 1fr));container-type:inline-size;}',
),
'grid layout with columnCount' => array(
'args' => array(
'selector' => '.wp-layout',
'layout' => array(
'type' => 'grid',
'columnCount' => 3,
),
),
'expected_output' => '.wp-layout{grid-template-columns:repeat(3, minmax(0, 1fr));}',
),
'default layout with blockGap to verify converting gap value into valid CSS' => array(
'args' => array(
'selector' => '.wp-block-group.wp-container-6',
Expand All @@ -260,7 +280,7 @@ public function data_wp_get_layout_style() {
'blockGap' => 'var(--wp--preset--spacing--70)',
),
),
'expected_output' => '.wp-block-group.wp-container-6 > *{margin-block-start:0;margin-block-end:0;}.wp-block-group.wp-container-6.wp-block-group.wp-container-6 > * + *{margin-block-start:var(--wp--preset--spacing--70);margin-block-end:0;}',
'expected_output' => '.wp-block-group.wp-container-6 > *{margin-block-start:0;margin-block-end:0;}.wp-block-group.wp-container-6 > * + *{margin-block-start:var(--wp--preset--spacing--70);margin-block-end:0;}',
),
);
}
Expand Down
Loading
Loading