Skip to content

Add new filter or API to allow modifying theme.json elements' style selectors #59163

@daviedR

Description

@daviedR

What problem does this address?

When defining element styles in the theme.json or customizing the Global Styles, WP will generate a global CSS for the specified element.

For example, if a theme define a button element style in the theme.json like this:

...
"styles": {
	"button": {
		"typography": {
			"fontWeight": "600"
		}
	}
}
...

WP will generate this global CSS:

.wp-element-button, .wp-block-button__link { font-weight: 600 }

The valid elements list (and the selectors) are defined in the class-wp-theme-json.php file (ELEMENTS constant). It's also defined in the JS file: package/blocks/src/api/constants.js in the __EXPERIMENTAL_ELEMENTS constant. These are double definitions, one for PHP and the other for JS. The JS constant is used in Site Editor mode to generate real-time global CSS from the Global Styles settings panel.

The valid elements list and the selectors are currently fixed and can't be modified using filters.

In a few cases, theme developers might want to add new selectors to the valid elements list. For example, I want to add .button into the button element selectors.

Original selector: wp-element-button, .wp-block-button__link
It will become: wp-element-button, .wp-block-button__link, .button

When using the :hover pseudo selector, the selector should be: .wp-element-button:hover, .wp-block-button__link:hover, .button:hover.


Currently, there is no way to modify the selectors properly and implement this kind of feature.

I did some workaround on the PHP side by using the wp_theme_json_get_style_nodes filter to check each style node and add the new selectors manually. It seems more like a hack than a proper filter. The snippet is like this:

add_filter(
	'wp_theme_json_get_style_nodes',
	function( $nodes ) {
		foreach ( $nodes as &$node ) {
			if ( array( 'styles', 'elements', 'button' ) === $node['path'] ) {
				if ( -1 < strpos( $node['selector'], ':hover' ) ) {
					// Add selector for button's style node with :hover pseudo selector.
					$node['selector'] = $node['selector'] . ', .button:hover';
				} else {
					// Add selector for button's style node.
					$node['selector'] = $node['selector'] . ', .button';
				}
			}
		}

		return $nodes;
	}
);

That works perfectly both in the front end and Post Editor because the CSS is loaded via PHP.

But this doesn't work in the Site Editor. The Site Editor JS will always replace the global styles with the newly generated CSS according to the Global Styles settings panel. The CSS selector used in the newly generated CSS is the one defined in the __EXPERIMENTAL_ELEMENTS constant which is hard coded and can't be modified.


Also, there's also a minor bug in the JS side __EXPERIMENTAL_ELEMENTS constant.
The selectors for the link element are different from the PHP one.
in PHP: a:where(:not(.wp-element-button))
in JS: a

What is your proposed solution?

I think there should be a new filter or API to modify the element's selectors and make it synced between PHP and JS.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Global StylesAnything related to the broader Global Styles efforts, including Styles Engine and theme.json[Type] EnhancementA suggestion for improvement.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions