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

Fix link element hover bleeding into button element default styles #42072

Merged
merged 1 commit into from
Jul 1, 2022

Conversation

getdave
Copy link
Contributor

@getdave getdave commented Jun 30, 2022

What?

In #42055 we discovered that link element styles were bleeding into button element styles. This PR fixes that by targeting only non-button element a nodes with the CSS. It also fixes a bug with the detection of pseudo selectors in the Theme JSON.

Fixes #42055.

Why?

The link element is mapped to a raw a CSS selector:

const ELEMENTS = array(
'link' => 'a',
'h1' => 'h1',
'h2' => 'h2',
'h3' => 'h3',
'h4' => 'h4',
'h5' => 'h5',
'h6' => 'h6',
'button' => '.wp-element-button, .wp-block-button__link', // We have the .wp-block-button__link class so that this will target older buttons that have been serialized.
'caption' => '.wp-element-caption, .wp-block-audio figcaption, .wp-block-embed figcaption, .wp-block-gallery figcaption, .wp-block-image figcaption, .wp-block-table figcaption, .wp-block-video figcaption', // The block classes are necessary to target older content that won't use the new class names.
);

When this config is translated into CSS rules for elements then we get following - admittedly rather broad - selector:

a {
    // rules here
}

Unfortunately however, the button block uses an a in its markup.

<div class="wp-block-button">
    <a class="wp-block-button__link wp-element-button">I am a button</a>
</div>

This means that the styles intended for link elements only, end up unintentionally "polluting" the button block styling.

How?

The suggested solution is to more carefully target anchor nodes which are not button elements. This is done by checking that the a node in question is :not(.wp-element-button).

This PR therefore updates the elements constant with a new selector mapping:

const ELEMENTS = array(
-    'link'    => 'a',
+    'link'    => 'a:not(.wp-element-button)',
    // other element mappings
);

This results in a more specific rule being generated to target global a (aka link) elements:

a:not(.wp-element-button) {
    // rules here
}

Unfortunately however, this has a side effect of exposing a bug in the way we generated pseudo class styles for elements in Theme JSON. Specifically we use naive regex (I wrote it so it's ok to say that! 😆 ) that is based on the presence of a colon : to parse out the potential pseudo class (e.g. :hover from the selector a:hover).

// Attempt to parse a pseudo selector (e.g. ":hover") from the $selector ("a:hover").
$pseudo_matches = array();
preg_match( '/:[a-z]+/', $selector, $pseudo_matches );
$pseudo_selector = isset( $pseudo_matches[0] ) ? $pseudo_matches[0] : null;

The problem is that :not() is incorrectly determined to be a "interactivity state" psuedo class (e.g. :hover, :focus...etc) which causes the rules to be misapplied.

This PR updates the logic to check for the presence of an element-specific set of whitelisted pseudo classes.

// Illustrative code only - see `Files changed` for actual code implementation.
$selector = 'a:not('.wp-element-button'):hover';
$element_pseudo_whitelist = array(':hover',':focus',':active');
$pseudo_matches = array_values(
    array_filter(
        $element_pseudo_whitelist,
        function( $pseudo_selector ) use ( $selector ) {
            return str_contains( $selector, $pseudo_selector ); 
        }
    )
);

The whitelist is as per that which already exists in trunk:

/**
* Whitelist which defines which pseudo selectors are enabled for
* which elements.
* Note: this will effect both top level and block level elements.
*/
const VALID_ELEMENT_PSEUDO_SELECTORS = array(
'link' => array( ':hover', ':focus', ':active' ),
);

By doing this the pseudo selectors are output correctly.

Testing Instructions

  1. Switch to emtpytheme
  2. Add this to your theme.json file under elements
			"link": {
				"border": {
					"color": "grey",
					"radius": "1px",
					"width": "5px",
					"style": "solid"
				},
				"color": {
					"background": "lightblue",
					"text": "red"
				},
				"spacing": {
					"padding": "10px"
				},
				"typography": {
					"fontFamily": "sans-serif",
					"fontSize": "20px",
					"textDecoration": "none"
				},
				":hover": {
					"border": {
						"color": "black",
						"radius": "10px",
						"width": "5px",
						"style": "solid"
					},
					"color": {
						"background": "red",
						"text": "black"
					}
				},
				":focus": {
					"border": {
						"color": "black",
						"radius": "10px",
						"width": "5px",
						"style": "solid"
					},
					"color": {
						"background": "red",
						"text": "black"
					}
				}
			}
  1. Add some buttons blocks
  2. Switch to the front of your site.
  3. Check that none of the rules for the link element are applied to buttons as well as to links.
  4. Check against the testing instructions in Global Styles: Elements: Link style rules are being applied to buttons #42055

Screenshots or screencast

Before

Both the link and button elements take on the styles shown in the theme.json excerpt from the Testing Instructions. This shouldn't happen. Buttons should not be targetted.

Screen Shot 2022-06-30 at 11 48 48

After

The link elements take on the styles shown in the theme.json excerpt from the Testing Instructions whereas the button elements ignore these styles.
Screen Shot 2022-06-30 at 11 49 16

@getdave getdave added the Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json label Jun 30, 2022
@@ -431,7 +431,7 @@ private static function get_block_nodes( $theme_json, $selectors = array() ) {
'selector' => $selectors[ $name ]['elements'][ $element ],
);

// Handle any psuedo selectors for the element.
// Handle any pseudo selectors for the element.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Told you I'd never learn to spell this word

@@ -475,6 +470,24 @@ public function get_styles_for_block( $block_metadata ) {

$current_element = $is_processing_element ? $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ] : null;

$element_pseudo_whitelist = isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] ) ? static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] : array();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the element-specific whitelist of valid pseudo selectors. As things stand only link is allowed to have pseudo selectors and these are whitelisted to hover, focus and active.

@getdave getdave marked this pull request as ready for review June 30, 2022 12:30
@getdave getdave requested a review from spacedmonkey as a code owner June 30, 2022 12:30
@getdave
Copy link
Contributor Author

getdave commented Jun 30, 2022

One question I have is are we going to end up in an escalating arms race here of excluding elements from being "targetted" by the global a selector?

Currently it's only

a:not(.wp-element-button) {
    // rules here
}

...but I could see that escalating into the :not() containing lots of selectors.

@getdave getdave added the CSS Styling Related to editor and front end styles, CSS-specific issues. label Jun 30, 2022
@adamziel
Copy link
Contributor

adamziel commented Jun 30, 2022

One question I have is are we going to end up in an escalating arms race here of excluding elements from being "targetted" by the global a selector?

One way out of this would be to use a denylist and exclude all a elements with a class starting with wp-. We could use a selectors such as a:not( [class^='wp-'], [class*=' wp-'] ).

At this point, I'm starting to question the assumptions though. Which elements do we even want this to target? Is a.wp-block-pages-list__item__link okay? Why or why not? Also, if I write a plugin shipping a MyButton block, how can I ensure it won't get targeted by the global link styles?

@adamziel
Copy link
Contributor

adamziel commented Jun 30, 2022

I wonder if we could, instead, tell the button styles to ignore the a styles. Perhaps by setting inherit values for any a rules affecting the button but not covered by it? For example:

a {
   color: #000;
}
.wp-block-button {
   color: inherit; /* Automatically added by WP */
   border: 1px solid #fff;
}

@scruffian
Copy link
Contributor

I think that this is a special case. Firstly the element selector for links is much broader than most element selectors will be, and secondly buttons using a tags is an exception which I don't expect to happen often, if at all. I'd say let's merge this one as is, and if we do need to add more selectors we can come back to it.

@getdave getdave force-pushed the fix/link-element-hover-bleeding-into-button branch from 751b6d3 to 0d6af3c Compare July 1, 2022 13:07
@getdave getdave force-pushed the fix/link-element-hover-bleeding-into-button branch from 0d6af3c to ee67b53 Compare July 1, 2022 13:08
@getdave
Copy link
Contributor Author

getdave commented Jul 1, 2022

Conflicts now resolved. Waiting on tests to merge.

@getdave getdave merged commit 3ae8f81 into trunk Jul 1, 2022
@getdave getdave deleted the fix/link-element-hover-bleeding-into-button branch July 1, 2022 14:26
@github-actions github-actions bot added this to the Gutenberg 13.7 milestone Jul 1, 2022
@ZebulanStanphill
Copy link
Member

The selector used here doesn't work with Button blocks generated in older versions of the block editor, since they lack the wp-element-button CSS class. Worse, the new selector has higher specificity than the old one, so the underline style is even more likely to conflict with the styles being applied to the old Button blocks. And indeed, that's exactly what I ran into after updating Gutenberg on a site I'm working on.

Any ideas on how to resolve this? This feels awfully similar to the situation that #42122 is attempting to address, namely that there are a bunch of static blocks with outdated markup already on the front-end, and the only way to change their markup is by either resaving the posts they're in, or else by using dynamic rendering filters in PHP to alter the output markup.

@getdave
Copy link
Contributor Author

getdave commented Jul 22, 2022

@ZebulanStanphill Thank you for taking the time to report this.

I think that the direction of #42122 might be where we have to go for this. Dynamically adding the class names ensures we always have them even in older versions of Gutenberg.

The problem is that PR is still a while from shipping and then we have to write the same logic but for the buttons block.

@adamziel what's your take on this?

@adamziel
Copy link
Contributor

@getdave @ZebulanStanphill I'd love to have these classes added to all the older blocks. #42485 could help us do just that.

pento pushed a commit to WordPress/wordpress-develop that referenced this pull request Sep 10, 2022
This commit backports the original PRs from Gutenberg repository:

* [WordPress/gutenberg#40260 #40260 Add support for button elements to theme.json]
* [WordPress/gutenberg#40889 #40889 Theme Json: Don't output double selectors for elements inside blocks]
* [WordPress/gutenberg#41140 #41140 Global Styles: Add support for caption elements]
* [WordPress/gutenberg#41160 #41160 Global Styles: Load block CSS conditionally]
* [WordPress/gutenberg#41240 #41240 Global Styles: Button Element: update button element selector]
* [WordPress/gutenberg#41335 #41335 Duotone: Fix CSS Selectors rendered by theme.json duotone/filter settings for blocks on public pages]
* [WordPress/gutenberg#41446 #41446 Block styles: Account for style block nodes that have no name]
* [WordPress/gutenberg#41696 #41696 Global Styles: Allow references to values in other locations in the tree]
* [WordPress/gutenberg#41753 #41753 Elements: Add an API make it easier to get class names]
* [WordPress/gutenberg#41786 #41786 Support pseudo selectors on elements in theme json]
* [WordPress/gutenberg#41822 #41822 Elements: Button - Fix element selectors]
* [WordPress/gutenberg#41981 #41981 Global Styles: Add support for heading elements]
* [WordPress/gutenberg#42072 #42072 Fix link element hover bleeding into button element default styles]
* [WordPress/gutenberg#42096 #42096 Add visited to link element allowed pseudo selector list]
* [WordPress/gutenberg#42669 #42669 Link elements: Add a :where selector to the :not to lower specificity]
* [WordPress/gutenberg#42776 #42776 Theme JSON: Add a static $blocks_metadata data definition to the Gutenberg instance of WP_Theme_JSON]
* [WordPress/gutenberg#43088 #43088 Pseudo elements supports on button elements]
* [WordPress/gutenberg#43167 #43167 Theme_JSON: Use existing append_to_selector for pseudo elements]
* [WordPress/gutenberg#43988 #43988 Styles API: Fixed selectors for nested elements]

Props onemaggie, bernhard-reiter, cbravobernal, mmaattiiaass, scruffian, andraganescu, dpcalhoun, get_dave, Mamaduka, SergeyBiryukov.
See #56467.

git-svn-id: https://develop.svn.wordpress.org/trunk@54118 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Sep 10, 2022
This commit backports the original PRs from Gutenberg repository:

* [WordPress/gutenberg#40260 #40260 Add support for button elements to theme.json]
* [WordPress/gutenberg#40889 #40889 Theme Json: Don't output double selectors for elements inside blocks]
* [WordPress/gutenberg#41140 #41140 Global Styles: Add support for caption elements]
* [WordPress/gutenberg#41160 #41160 Global Styles: Load block CSS conditionally]
* [WordPress/gutenberg#41240 #41240 Global Styles: Button Element: update button element selector]
* [WordPress/gutenberg#41335 #41335 Duotone: Fix CSS Selectors rendered by theme.json duotone/filter settings for blocks on public pages]
* [WordPress/gutenberg#41446 #41446 Block styles: Account for style block nodes that have no name]
* [WordPress/gutenberg#41696 #41696 Global Styles: Allow references to values in other locations in the tree]
* [WordPress/gutenberg#41753 #41753 Elements: Add an API make it easier to get class names]
* [WordPress/gutenberg#41786 #41786 Support pseudo selectors on elements in theme json]
* [WordPress/gutenberg#41822 #41822 Elements: Button - Fix element selectors]
* [WordPress/gutenberg#41981 #41981 Global Styles: Add support for heading elements]
* [WordPress/gutenberg#42072 #42072 Fix link element hover bleeding into button element default styles]
* [WordPress/gutenberg#42096 #42096 Add visited to link element allowed pseudo selector list]
* [WordPress/gutenberg#42669 #42669 Link elements: Add a :where selector to the :not to lower specificity]
* [WordPress/gutenberg#42776 #42776 Theme JSON: Add a static $blocks_metadata data definition to the Gutenberg instance of WP_Theme_JSON]
* [WordPress/gutenberg#43088 #43088 Pseudo elements supports on button elements]
* [WordPress/gutenberg#43167 #43167 Theme_JSON: Use existing append_to_selector for pseudo elements]
* [WordPress/gutenberg#43988 #43988 Styles API: Fixed selectors for nested elements]

Props onemaggie, bernhard-reiter, cbravobernal, mmaattiiaass, scruffian, andraganescu, dpcalhoun, get_dave, Mamaduka, SergeyBiryukov.
See #56467.
Built from https://develop.svn.wordpress.org/trunk@54118


git-svn-id: http://core.svn.wordpress.org/trunk@53677 1a063a9b-81f0-0310-95a4-ce76da25c4cd
github-actions bot pushed a commit to platformsh/wordpress-performance that referenced this pull request Sep 10, 2022
This commit backports the original PRs from Gutenberg repository:

* [WordPress/gutenberg#40260 #40260 Add support for button elements to theme.json]
* [WordPress/gutenberg#40889 #40889 Theme Json: Don't output double selectors for elements inside blocks]
* [WordPress/gutenberg#41140 #41140 Global Styles: Add support for caption elements]
* [WordPress/gutenberg#41160 #41160 Global Styles: Load block CSS conditionally]
* [WordPress/gutenberg#41240 #41240 Global Styles: Button Element: update button element selector]
* [WordPress/gutenberg#41335 #41335 Duotone: Fix CSS Selectors rendered by theme.json duotone/filter settings for blocks on public pages]
* [WordPress/gutenberg#41446 #41446 Block styles: Account for style block nodes that have no name]
* [WordPress/gutenberg#41696 #41696 Global Styles: Allow references to values in other locations in the tree]
* [WordPress/gutenberg#41753 #41753 Elements: Add an API make it easier to get class names]
* [WordPress/gutenberg#41786 #41786 Support pseudo selectors on elements in theme json]
* [WordPress/gutenberg#41822 #41822 Elements: Button - Fix element selectors]
* [WordPress/gutenberg#41981 #41981 Global Styles: Add support for heading elements]
* [WordPress/gutenberg#42072 #42072 Fix link element hover bleeding into button element default styles]
* [WordPress/gutenberg#42096 #42096 Add visited to link element allowed pseudo selector list]
* [WordPress/gutenberg#42669 #42669 Link elements: Add a :where selector to the :not to lower specificity]
* [WordPress/gutenberg#42776 #42776 Theme JSON: Add a static $blocks_metadata data definition to the Gutenberg instance of WP_Theme_JSON]
* [WordPress/gutenberg#43088 #43088 Pseudo elements supports on button elements]
* [WordPress/gutenberg#43167 #43167 Theme_JSON: Use existing append_to_selector for pseudo elements]
* [WordPress/gutenberg#43988 #43988 Styles API: Fixed selectors for nested elements]

Props onemaggie, bernhard-reiter, cbravobernal, mmaattiiaass, scruffian, andraganescu, dpcalhoun, get_dave, Mamaduka, SergeyBiryukov.
See #56467.
Built from https://develop.svn.wordpress.org/trunk@54118


git-svn-id: https://core.svn.wordpress.org/trunk@53677 1a063a9b-81f0-0310-95a4-ce76da25c4cd
whereiscodedude pushed a commit to whereiscodedude/wpss that referenced this pull request Sep 18, 2022
This commit backports the original PRs from Gutenberg repository:

* [WordPress/gutenberg#40260 #40260 Add support for button elements to theme.json]
* [WordPress/gutenberg#40889 #40889 Theme Json: Don't output double selectors for elements inside blocks]
* [WordPress/gutenberg#41140 #41140 Global Styles: Add support for caption elements]
* [WordPress/gutenberg#41160 #41160 Global Styles: Load block CSS conditionally]
* [WordPress/gutenberg#41240 #41240 Global Styles: Button Element: update button element selector]
* [WordPress/gutenberg#41335 #41335 Duotone: Fix CSS Selectors rendered by theme.json duotone/filter settings for blocks on public pages]
* [WordPress/gutenberg#41446 #41446 Block styles: Account for style block nodes that have no name]
* [WordPress/gutenberg#41696 #41696 Global Styles: Allow references to values in other locations in the tree]
* [WordPress/gutenberg#41753 #41753 Elements: Add an API make it easier to get class names]
* [WordPress/gutenberg#41786 #41786 Support pseudo selectors on elements in theme json]
* [WordPress/gutenberg#41822 #41822 Elements: Button - Fix element selectors]
* [WordPress/gutenberg#41981 #41981 Global Styles: Add support for heading elements]
* [WordPress/gutenberg#42072 #42072 Fix link element hover bleeding into button element default styles]
* [WordPress/gutenberg#42096 #42096 Add visited to link element allowed pseudo selector list]
* [WordPress/gutenberg#42669 #42669 Link elements: Add a :where selector to the :not to lower specificity]
* [WordPress/gutenberg#42776 #42776 Theme JSON: Add a static $blocks_metadata data definition to the Gutenberg instance of WP_Theme_JSON]
* [WordPress/gutenberg#43088 #43088 Pseudo elements supports on button elements]
* [WordPress/gutenberg#43167 #43167 Theme_JSON: Use existing append_to_selector for pseudo elements]
* [WordPress/gutenberg#43988 #43988 Styles API: Fixed selectors for nested elements]

Props onemaggie, bernhard-reiter, cbravobernal, mmaattiiaass, scruffian, andraganescu, dpcalhoun, get_dave, Mamaduka, SergeyBiryukov.
See #56467.
Built from https://develop.svn.wordpress.org/trunk@54118
ootwch pushed a commit to ootwch/wordpress-develop that referenced this pull request Nov 4, 2022
This commit backports the original PRs from Gutenberg repository:

* [WordPress/gutenberg#40260 #40260 Add support for button elements to theme.json]
* [WordPress/gutenberg#40889 #40889 Theme Json: Don't output double selectors for elements inside blocks]
* [WordPress/gutenberg#41140 #41140 Global Styles: Add support for caption elements]
* [WordPress/gutenberg#41160 #41160 Global Styles: Load block CSS conditionally]
* [WordPress/gutenberg#41240 #41240 Global Styles: Button Element: update button element selector]
* [WordPress/gutenberg#41335 #41335 Duotone: Fix CSS Selectors rendered by theme.json duotone/filter settings for blocks on public pages]
* [WordPress/gutenberg#41446 #41446 Block styles: Account for style block nodes that have no name]
* [WordPress/gutenberg#41696 #41696 Global Styles: Allow references to values in other locations in the tree]
* [WordPress/gutenberg#41753 #41753 Elements: Add an API make it easier to get class names]
* [WordPress/gutenberg#41786 #41786 Support pseudo selectors on elements in theme json]
* [WordPress/gutenberg#41822 #41822 Elements: Button - Fix element selectors]
* [WordPress/gutenberg#41981 #41981 Global Styles: Add support for heading elements]
* [WordPress/gutenberg#42072 #42072 Fix link element hover bleeding into button element default styles]
* [WordPress/gutenberg#42096 #42096 Add visited to link element allowed pseudo selector list]
* [WordPress/gutenberg#42669 #42669 Link elements: Add a :where selector to the :not to lower specificity]
* [WordPress/gutenberg#42776 #42776 Theme JSON: Add a static $blocks_metadata data definition to the Gutenberg instance of WP_Theme_JSON]
* [WordPress/gutenberg#43088 #43088 Pseudo elements supports on button elements]
* [WordPress/gutenberg#43167 #43167 Theme_JSON: Use existing append_to_selector for pseudo elements]
* [WordPress/gutenberg#43988 #43988 Styles API: Fixed selectors for nested elements]

Props onemaggie, bernhard-reiter, cbravobernal, mmaattiiaass, scruffian, andraganescu, dpcalhoun, get_dave, Mamaduka, SergeyBiryukov.
See #56467.

git-svn-id: https://develop.svn.wordpress.org/trunk@54118 602fd350-edb4-49c9-b593-d223f7449a82
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CSS Styling Related to editor and front end styles, CSS-specific issues. Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Global Styles: Elements: Link style rules are being applied to buttons
5 participants