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

Elements: Add styles to the footer before the block is rendered #37728

Merged
merged 12 commits into from
Apr 13, 2022
77 changes: 63 additions & 14 deletions lib/block-supports/elements.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@
*/

/**
* Render the elements stylesheet.
* Get the elements class names.
*
* @param array $block Block object.
* @return string The unique class name.
*/
function gutenberg_get_elements_class_name( $block ) {
return 'wp-elements-' . md5( serialize( $block ) );
}

/**
* Update the block content with elements class names.
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
*/
function gutenberg_render_elements_support( $block_content, $block ) {

if ( ! $block_content ) {
return $block_content;
}
Expand All @@ -40,17 +49,7 @@ function gutenberg_render_elements_support( $block_content, $block ) {
return $block_content;
}

$class_name = wp_unique_id( 'wp-elements-' );

if ( strpos( $link_color, 'var:preset|color|' ) !== false ) {
// Get the name from the string and add proper styles.
$index_to_splice = strrpos( $link_color, '|' ) + 1;
$link_color_name = substr( $link_color, $index_to_splice );
$link_color = "var(--wp--preset--color--$link_color_name)";
}
$link_color_declaration = esc_html( safecss_filter_attr( "color: $link_color" ) );

$style = ".$class_name a{" . $link_color_declaration . ';}';
$class_name = gutenberg_get_elements_class_name( $block );

// Like the layout hook this assumes the hook only applies to blocks with a single wrapper.
// Retrieve the opening tag of the first HTML element.
Expand All @@ -72,11 +71,61 @@ function gutenberg_render_elements_support( $block_content, $block ) {
$content = substr_replace( $block_content, ' class="' . $class_name . '"', $first_element_offset + strlen( $first_element ) - 1, 0 );
}

return $content;
}

/**
* Render the elements stylesheet.
*
* In the case of nested blocks we want the parent element styles to be rendered before their descendants.
* This solves the issue of an element (e.g.: link color) being styled in both the parent and a descendant:
* we want the descendant style to take priority, and this is done by loading it after, in DOM order.
*
* @param string|null $pre_render The pre-rendered content. Default null.
* @param array $block The block being rendered.
*
* @return null
*/
function gutenberg_render_elements_support_styles( $pre_render, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$skip_link_color_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'color', 'link' );
if ( $skip_link_color_serialization ) {
return null;
}

$link_color = null;
if ( ! empty( $block['attrs'] ) ) {
$link_color = _wp_array_get( $block['attrs'], array( 'style', 'elements', 'link', 'color', 'text' ), null );
}

/*
* For now we only care about link color.
* This code in the future when we have a public API
* should take advantage of WP_Theme_JSON_Gutenberg::compute_style_properties
* and work for any element and style.
*/
if ( null === $link_color ) {
return null;
}

$class_name = gutenberg_get_elements_class_name( $block );

if ( strpos( $link_color, 'var:preset|color|' ) !== false ) {
// Get the name from the string and add proper styles.
$index_to_splice = strrpos( $link_color, '|' ) + 1;
$link_color_name = substr( $link_color, $index_to_splice );
$link_color = "var(--wp--preset--color--$link_color_name)";
}
$link_color_declaration = esc_html( safecss_filter_attr( "color: $link_color" ) );

$style = ".$class_name a{" . $link_color_declaration . ';}';

gutenberg_enqueue_block_support_styles( $style );

return $content;
return null;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did that for the sake of the test...

Copy link
Member

Choose a reason for hiding this comment

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

I removed the test I suggested we should have. As you said, in this case, the value would lie in a e2e test. It'd be great to have it :)

}

// Remove WordPress core filter to avoid rendering duplicate elements stylesheet.
remove_filter( 'render_block', 'wp_render_elements_support', 10, 2 );
add_filter( 'render_block', 'gutenberg_render_elements_support', 10, 2 );
add_filter( 'pre_render_block', 'gutenberg_render_elements_support_styles', 10, 2 );
Copy link
Contributor

Choose a reason for hiding this comment

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

For me this PR raises an interesting question about the styles order. Is there any place where we want the block styles to be generated "child" first then "parent" later? In other words, this PR does invert the order for the "elements" styles but why not invert it for all kind of styles.

It seems it's something worth thinking about for the future in regards of the style engine work. cc @andrewserong @ramonjd

Copy link
Member

Choose a reason for hiding this comment

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

Is there any place where we want the block styles to be generated "child" first then "parent" later?

Thanks for the ping.

I suppose depending on how we output styles we might want to look at a mix of specificity and style rendering order.

Anyway, I've added a note to look at a complete inheritance modal over at the style engine tracking issue. 🙇

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe it should be like this for all styles, to follow the pattern of CSS inheritance.

3 changes: 2 additions & 1 deletion phpunit/class-elements-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Gutenberg_Elements_Test extends WP_UnitTestCase {
* @return string String where the unique id classes were replaced with "wp-elements-1".
*/
private static function make_unique_id_one( $string ) {
return preg_replace( '/wp-elements-\d+/', 'wp-elements-1', $string );
return preg_replace( '/wp-elements-[a-zA-Z0-9]+/', 'wp-elements-1', $string );
}

/**
Expand Down Expand Up @@ -103,4 +103,5 @@ public function test_anchor_paragraph_link_color() {
'<p id="anchor" class="wp-elements-1">Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>'
);
}

}