Skip to content

Commit

Permalink
Block Themes: Avoid specificity bump for top-level element-only selec…
Browse files Browse the repository at this point in the history
…tors

Prevent issues (e.g. links being underlined) caused by a bump in CSS
specificity for top-level element-only global element styles.

Backports the PHP changes from WordPress/gutenberg#63403.

Fixes #61630.
Fixes #61660.
Props aaronrobertshaw, andrewserong, noisysocks.


git-svn-id: https://develop.svn.wordpress.org/trunk@58749 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
noisysocks committed Jul 18, 2024
1 parent 340bf4c commit 842a353
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 8 deletions.
17 changes: 16 additions & 1 deletion src/wp-includes/class-wp-theme-json.php
Original file line number Diff line number Diff line change
Expand Up @@ -2888,8 +2888,23 @@ static function ( $pseudo_selector ) use ( $selector ) {
$declarations = static::update_separator_declarations( $declarations );
}

/*
* Top-level element styles using element-only specificity selectors should
* not get wrapped in `:root :where()` to maintain backwards compatibility.
*
* Pseudo classes, e.g. :hover, :focus etc., are a class-level selector so
* still need to be wrapped in `:root :where` to cap specificity for nested
* variations etc. Pseudo selectors won't match the ELEMENTS selector exactly.
*/
$element_only_selector = $current_element &&
isset( static::ELEMENTS[ $current_element ] ) &&
// buttons, captions etc. still need `:root :where()` as they are class based selectors.
! isset( static::__EXPERIMENTAL_ELEMENT_CLASS_NAMES[ $current_element ] ) &&
static::ELEMENTS[ $current_element ] === $selector;

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

// 3. Generate and append the rules that use the duotone selector.
if ( isset( $block_metadata['duotone'] ) && ! empty( $declarations_duotone ) ) {
Expand Down
21 changes: 14 additions & 7 deletions tests/phpunit/tests/theme/wpThemeJson.php
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ public function test_get_settings_appearance_false_does_not_opt_in() {
* @ticket 60365
* @ticket 60936
* @ticket 61165
* @ticket 61630
*/
public function test_get_stylesheet() {
$theme_json = new WP_Theme_JSON(
Expand Down Expand Up @@ -565,7 +566,7 @@ public function test_get_stylesheet() {
);

$variables = ':root{--wp--preset--color--grey: grey;--wp--preset--gradient--custom-gradient: linear-gradient(135deg,rgba(0,0,0) 0%,rgb(0,0,0) 100%);--wp--preset--font-size--small: 14px;--wp--preset--font-size--big: 41px;--wp--preset--font-family--arial: Arial, serif;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}';
$styles = ':where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}:root :where(body){color: var(--wp--preset--color--grey);}:root :where(a:where(:not(.wp-element-button))){background-color: #333;color: #111;}:root :where(.wp-element-button, .wp-block-button__link){box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.66);}:root :where(.wp-block-cover){min-height: unset;aspect-ratio: 16/9;}:root :where(.wp-block-group){background: var(--wp--preset--gradient--custom-gradient);border-radius: 10px;min-height: 50vh;padding: 24px;}:root :where(.wp-block-group a:where(:not(.wp-element-button))){color: #111;}:root :where(.wp-block-heading){color: #123456;}:root :where(.wp-block-heading a:where(:not(.wp-element-button))){background-color: #333;color: #111;font-size: 60px;}:root :where(.wp-block-media-text){text-align: center;}:root :where(.wp-block-post-date){color: #123456;}:root :where(.wp-block-post-date a:where(:not(.wp-element-button))){background-color: #777;color: #555;}:root :where(.wp-block-post-excerpt){column-count: 2;}:root :where(.wp-block-image){margin-bottom: 30px;}:root :where(.wp-block-image img, .wp-block-image .wp-block-image__crop-area, .wp-block-image .components-placeholder){border-top-left-radius: 10px;border-bottom-right-radius: 1em;}:root :where(.wp-block-image img, .wp-block-image .components-placeholder){filter: var(--wp--preset--duotone--custom-duotone);}';
$styles = ':where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}:root :where(body){color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}:root :where(.wp-element-button, .wp-block-button__link){box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.66);}:root :where(.wp-block-cover){min-height: unset;aspect-ratio: 16/9;}:root :where(.wp-block-group){background: var(--wp--preset--gradient--custom-gradient);border-radius: 10px;min-height: 50vh;padding: 24px;}:root :where(.wp-block-group a:where(:not(.wp-element-button))){color: #111;}:root :where(.wp-block-heading){color: #123456;}:root :where(.wp-block-heading a:where(:not(.wp-element-button))){background-color: #333;color: #111;font-size: 60px;}:root :where(.wp-block-media-text){text-align: center;}:root :where(.wp-block-post-date){color: #123456;}:root :where(.wp-block-post-date a:where(:not(.wp-element-button))){background-color: #777;color: #555;}:root :where(.wp-block-post-excerpt){column-count: 2;}:root :where(.wp-block-image){margin-bottom: 30px;}:root :where(.wp-block-image img, .wp-block-image .wp-block-image__crop-area, .wp-block-image .components-placeholder){border-top-left-radius: 10px;border-bottom-right-radius: 1em;}:root :where(.wp-block-image img, .wp-block-image .components-placeholder){filter: var(--wp--preset--duotone--custom-duotone);}';
$presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-custom-gradient-gradient-background{background: var(--wp--preset--gradient--custom-gradient) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-big-font-size{font-size: var(--wp--preset--font-size--big) !important;}.has-arial-font-family{font-family: var(--wp--preset--font-family--arial) !important;}';
$all = $variables . $styles . $presets;

Expand Down Expand Up @@ -835,6 +836,7 @@ public function test_get_stylesheet_generates_proper_classes_and_css_vars_from_s
* @ticket 58550
* @ticket 60936
* @ticket 61165
* @ticket 61630
*/
public function test_get_styles_for_block_handles_whitelisted_element_pseudo_selectors() {
$theme_json = new WP_Theme_JSON(
Expand Down Expand Up @@ -882,7 +884,7 @@ public function test_get_styles_for_block_handles_whitelisted_element_pseudo_sel
'selector' => 'a:where(:not(.wp-element-button)):focus',
);

$link_style = ':root :where(a:where(:not(.wp-element-button))){background-color: red;color: green;}';
$link_style = 'a:where(:not(.wp-element-button)){background-color: red;color: green;}';
$hover_style = ':root :where(a:where(:not(.wp-element-button)):hover){background-color: green;color: red;font-size: 10em;text-transform: uppercase;}';
$focus_style = ':root :where(a:where(:not(.wp-element-button)):focus){background-color: black;color: yellow;}';

Expand Down Expand Up @@ -938,6 +940,7 @@ public function test_get_stylesheet_handles_only_pseudo_selector_rules_for_given
* @ticket 58550
* @ticket 60936
* @ticket 61165
* @ticket 61630
*/
public function test_get_stylesheet_ignores_pseudo_selectors_on_non_whitelisted_elements() {
$theme_json = new WP_Theme_JSON(
Expand Down Expand Up @@ -968,7 +971,7 @@ public function test_get_stylesheet_ignores_pseudo_selectors_on_non_whitelisted_
)
);

$expected = ':root :where(h4){background-color: red;color: green;}';
$expected = 'h4{background-color: red;color: green;}';

$this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ) );
}
Expand All @@ -978,6 +981,7 @@ public function test_get_stylesheet_ignores_pseudo_selectors_on_non_whitelisted_
* @ticket 58550
* @ticket 60936
* @ticket 61165
* @ticket 61630
*/
public function test_get_stylesheet_ignores_non_whitelisted_pseudo_selectors() {
$theme_json = new WP_Theme_JSON(
Expand Down Expand Up @@ -1008,7 +1012,7 @@ public function test_get_stylesheet_ignores_non_whitelisted_pseudo_selectors() {
)
);

$expected = ':root :where(a:where(:not(.wp-element-button))){background-color: red;color: green;}:root :where(a:where(:not(.wp-element-button)):hover){background-color: green;color: red;}';
$expected = 'a:where(:not(.wp-element-button)){background-color: red;color: green;}:root :where(a:where(:not(.wp-element-button)):hover){background-color: green;color: red;}';

$this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ) );
$this->assertStringNotContainsString( 'a:levitate{', $theme_json->get_stylesheet( array( 'styles' ) ) );
Expand All @@ -1022,6 +1026,7 @@ public function test_get_stylesheet_ignores_non_whitelisted_pseudo_selectors() {
* @ticket 58550
* @ticket 60936
* @ticket 61165
* @ticket 61630
*/
public function test_get_stylesheet_handles_priority_of_elements_vs_block_elements_pseudo_selectors() {
$theme_json = new WP_Theme_JSON(
Expand Down Expand Up @@ -1060,7 +1065,7 @@ public function test_get_stylesheet_handles_priority_of_elements_vs_block_elemen
)
);

$expected = ':root :where(a:where(:not(.wp-element-button))){background-color: red;color: green;}:root :where(a:where(:not(.wp-element-button)):hover){background-color: green;color: red;}:root :where(.wp-block-group a:where(:not(.wp-element-button)):hover){background-color: black;color: yellow;}';
$expected = 'a:where(:not(.wp-element-button)){background-color: red;color: green;}:root :where(a:where(:not(.wp-element-button)):hover){background-color: green;color: red;}:root :where(.wp-block-group a:where(:not(.wp-element-button)):hover){background-color: black;color: yellow;}';

$this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ) );
}
Expand Down Expand Up @@ -1359,6 +1364,7 @@ public function test_get_stylesheet_custom_root_selector() {
*
* @ticket 61118
* @ticket 61165
* @ticket 61630
*/
public function test_get_stylesheet_generates_fluid_typography_values() {
register_block_type(
Expand Down Expand Up @@ -1413,7 +1419,7 @@ public function test_get_stylesheet_generates_fluid_typography_values() {
unregister_block_type( 'test/clamp-me' );

$this->assertSame(
':root{--wp--preset--font-size--pickles: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.156), 16px);--wp--preset--font-size--toast: clamp(14.642px, 0.915rem + ((1vw - 3.2px) * 0.575), 22px);}:root :where(body){font-size: clamp(0.875em, 0.875rem + ((1vw - 0.2em) * 0.156), 1em);}:root :where(h1){font-size: clamp(50.171px, 3.136rem + ((1vw - 3.2px) * 3.893), 100px);}:root :where(.wp-block-test-clamp-me){font-size: clamp(27.894px, 1.743rem + ((1vw - 3.2px) * 1.571), 48px);}.has-pickles-font-size{font-size: var(--wp--preset--font-size--pickles) !important;}.has-toast-font-size{font-size: var(--wp--preset--font-size--toast) !important;}',
':root{--wp--preset--font-size--pickles: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.156), 16px);--wp--preset--font-size--toast: clamp(14.642px, 0.915rem + ((1vw - 3.2px) * 0.575), 22px);}:root :where(body){font-size: clamp(0.875em, 0.875rem + ((1vw - 0.2em) * 0.156), 1em);}h1{font-size: clamp(50.171px, 3.136rem + ((1vw - 3.2px) * 3.893), 100px);}:root :where(.wp-block-test-clamp-me){font-size: clamp(27.894px, 1.743rem + ((1vw - 3.2px) * 1.571), 48px);}.has-pickles-font-size{font-size: var(--wp--preset--font-size--pickles) !important;}.has-toast-font-size{font-size: var(--wp--preset--font-size--toast) !important;}',
$theme_json->get_stylesheet( array( 'styles', 'variables', 'presets' ), null, array( 'skip_root_layout_styles' => true ) )
);
}
Expand Down Expand Up @@ -4946,6 +4952,7 @@ public function test_shadow_preset_styles() {
* @ticket 58550
* @ticket 60936
* @ticket 61165
* @ticket 61630
*/
public function test_get_shadow_styles_for_blocks() {
$theme_json = new WP_Theme_JSON(
Expand Down Expand Up @@ -4980,7 +4987,7 @@ public function test_get_shadow_styles_for_blocks() {
);

$variable_styles = ':root{--wp--preset--shadow--natural: 5px 5px 0 0 black;}';
$element_styles = ':root :where(a:where(:not(.wp-element-button))){box-shadow: var(--wp--preset--shadow--natural);}:root :where(.wp-element-button, .wp-block-button__link){box-shadow: var(--wp--preset--shadow--natural);}:root :where(p){box-shadow: var(--wp--preset--shadow--natural);}';
$element_styles = 'a:where(:not(.wp-element-button)){box-shadow: var(--wp--preset--shadow--natural);}:root :where(.wp-element-button, .wp-block-button__link){box-shadow: var(--wp--preset--shadow--natural);}:root :where(p){box-shadow: var(--wp--preset--shadow--natural);}';
$expected_styles = $variable_styles . $element_styles;
$this->assertSame( $expected_styles, $theme_json->get_stylesheet( array( 'styles', 'presets', 'variables' ), null, array( 'skip_root_layout_styles' => true ) ) );
}
Expand Down

0 comments on commit 842a353

Please sign in to comment.