Skip to content

Commit

Permalink
Elements: Avoid specificity bump for top-level element-only selectors (
Browse files Browse the repository at this point in the history
…#63403) (#63637)

Co-authored-by: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Co-authored-by: aaronrobertshaw <aaronrobertshaw@git.wordpress.org>
Co-authored-by: talldan <talldanwp@git.wordpress.org>
Co-authored-by: youknowriad <youknowriad@git.wordpress.org>
Co-authored-by: carolinan <poena@git.wordpress.org>
Co-authored-by: wolffe <butterflymedia@git.wordpress.org>
Co-authored-by: aaronrobertshaw <aaronrobertshaw@git.wordpress.org>
Co-authored-by: andrewserong <andrewserong@git.wordpress.org>
  • Loading branch information
8 people authored Jul 17, 2024
1 parent 0b9a945 commit e7e2525
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 11 deletions.
3 changes: 3 additions & 0 deletions backport-changelog/6.6/7012.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://github.com/WordPress/wordpress-develop/pull/7012

* https://github.com/WordPress/gutenberg/pull/63403
17 changes: 16 additions & 1 deletion lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -2846,8 +2846,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
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ describe( 'global styles renderer', () => {
},
},
selector: ELEMENTS.link,
skipSelectorWrapper: true,
},
{
styles: {
Expand Down Expand Up @@ -480,7 +481,7 @@ describe( 'global styles renderer', () => {
};

expect( toStyles( tree, blockSelectors ) ).toEqual(
':where(body) {margin: 0;}.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)) { max-width: var(--wp--style--global--content-size); margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > .alignwide { max-width: var(--wp--style--global--wide-size); }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){background-color: red;margin: 10px;padding: 10px;}:root :where(a:where(:not(.wp-element-button))){color: blue;}a:where(:not(.wp-element-button)):hover{color: orange;}a:where(:not(.wp-element-button)):focus{color: orange;}:root :where(h1){font-size: 42px;}:root :where(.wp-block-group){margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}:root :where(h1,h2,h3,h4,h5,h6){color: orange;}:root :where(h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button))){color: hotpink;}h1 a:where(:not(.wp-element-button)):hover,h2 a:where(:not(.wp-element-button)):hover,h3 a:where(:not(.wp-element-button)):hover,h4 a:where(:not(.wp-element-button)):hover,h5 a:where(:not(.wp-element-button)):hover,h6 a:where(:not(.wp-element-button)):hover{color: red;}h1 a:where(:not(.wp-element-button)):focus,h2 a:where(:not(.wp-element-button)):focus,h3 a:where(:not(.wp-element-button)):focus,h4 a:where(:not(.wp-element-button)):focus,h5 a:where(:not(.wp-element-button)):focus,h6 a:where(:not(.wp-element-button)):focus{color: red;}:root :where(.wp-block-image img, .wp-block-image .wp-crop-area){border-radius: 9999px;}:root :where(.wp-block-image){color: red;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}h1.has-blue-color,h2.has-blue-color,h3.has-blue-color,h4.has-blue-color,h5.has-blue-color,h6.has-blue-color{color: var(--wp--preset--color--blue) !important;}h1.has-blue-background-color,h2.has-blue-background-color,h3.has-blue-background-color,h4.has-blue-background-color,h5.has-blue-background-color,h6.has-blue-background-color{background-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-color,h2.has-blue-border-color,h3.has-blue-border-color,h4.has-blue-border-color,h5.has-blue-border-color,h6.has-blue-border-color{border-color: var(--wp--preset--color--blue) !important;}'
':where(body) {margin: 0;}.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)) { max-width: var(--wp--style--global--content-size); margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > .alignwide { max-width: var(--wp--style--global--wide-size); }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){background-color: red;margin: 10px;padding: 10px;}a:where(:not(.wp-element-button)){color: blue;}:root :where(a:where(:not(.wp-element-button)):hover){color: orange;}:root :where(a:where(:not(.wp-element-button)):focus){color: orange;}h1{font-size: 42px;}:root :where(.wp-block-group){margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}:root :where(h1,h2,h3,h4,h5,h6){color: orange;}:root :where(h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button))){color: hotpink;}:root :where(h1 a:where(:not(.wp-element-button)):hover,h2 a:where(:not(.wp-element-button)):hover,h3 a:where(:not(.wp-element-button)):hover,h4 a:where(:not(.wp-element-button)):hover,h5 a:where(:not(.wp-element-button)):hover,h6 a:where(:not(.wp-element-button)):hover){color: red;}:root :where(h1 a:where(:not(.wp-element-button)):focus,h2 a:where(:not(.wp-element-button)):focus,h3 a:where(:not(.wp-element-button)):focus,h4 a:where(:not(.wp-element-button)):focus,h5 a:where(:not(.wp-element-button)):focus,h6 a:where(:not(.wp-element-button)):focus){color: red;}:root :where(.wp-block-image img, .wp-block-image .wp-crop-area){border-radius: 9999px;}:root :where(.wp-block-image){color: red;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}h1.has-blue-color,h2.has-blue-color,h3.has-blue-color,h4.has-blue-color,h5.has-blue-color,h6.has-blue-color{color: var(--wp--preset--color--blue) !important;}h1.has-blue-background-color,h2.has-blue-background-color,h3.has-blue-background-color,h4.has-blue-background-color,h5.has-blue-background-color,h6.has-blue-background-color{background-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-color,h2.has-blue-border-color,h3.has-blue-border-color,h4.has-blue-border-color,h5.has-blue-border-color,h6.has-blue-border-color{border-color: var(--wp--preset--color--blue) !important;}'
);
} );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ import { getValueFromObjectPath, setImmutably } from '../../utils/object';
import { unlock } from '../../lock-unlock';
import { setThemeFileUris } from './theme-file-uri-utils';

// Elements that rely on class names in their selectors.
const ELEMENT_CLASS_NAMES = {
button: 'wp-element-button',
caption: 'wp-element-caption',
};

// List of block support features that can have their related styles
// generated under their own feature level selector rather than the block's.
const BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS = {
Expand Down Expand Up @@ -636,6 +642,9 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => {
nodes.push( {
styles: tree.styles?.elements?.[ name ],
selector,
// Top level elements that don't use a class name should not receive the
// `:root :where()` wrapper to maintain backwards compatibility.
skipSelectorWrapper: ! ELEMENT_CLASS_NAMES[ name ],
} );
}
} );
Expand Down Expand Up @@ -942,6 +951,7 @@ export const toStyles = (
hasLayoutSupport,
featureSelectors,
styleVariationSelectors,
skipSelectorWrapper,
} ) => {
// Process styles for block support features with custom feature level
// CSS selectors set.
Expand Down Expand Up @@ -1000,7 +1010,10 @@ export const toStyles = (
disableRootPadding
);
if ( styleDeclarations?.length ) {
ruleset += `:root :where(${ selector }){${ styleDeclarations.join(
const generalSelector = skipSelectorWrapper
? selector
: `:root :where(${ selector })`;
ruleset += `${ generalSelector }{${ styleDeclarations.join(
';'
) };}`;
}
Expand Down Expand Up @@ -1094,7 +1107,11 @@ export const toStyles = (
.map( ( sel ) => sel + pseudoKey )
.join( ',' );

const pseudoRule = `${ _selector }{${ pseudoDeclarations.join(
// As pseudo classes such as :hover, :focus etc. have class-level
// specificity, they must use the `:root :where()` wrapper. This.
// caps the specificity at `0-1-0` to allow proper nesting of variations
// and block type element styles.
const pseudoRule = `:root :where(${ _selector }){${ pseudoDeclarations.join(
';'
) };}`;

Expand Down
Loading

0 comments on commit e7e2525

Please sign in to comment.