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

Add duotone theme.json styles support #34667

Merged
merged 24 commits into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bbdd7ab
Add duotone theme.json styles support
ajlende Sep 8, 2021
5ec2efe
Add support for generating duotone CSS variables
ajlende Sep 8, 2021
3c0a97e
Add value_func and value_args metadata to compute duotone
ajlende Sep 9, 2021
ec86c0b
Update phpunit tests
ajlende Sep 9, 2021
358c456
Fix duotone filter id on css variables
ajlende Sep 9, 2021
8c421cc
Exmplain why !important is needed
ajlende Sep 9, 2021
e9abc0b
Fix get_settings_values_by_slug example
ajlende Sep 9, 2021
cebab75
Add color.duotone to PATHS_WITH_MERGE
ajlende Sep 9, 2021
5956f18
Rename $meta to $preset_meta to be more descriptive
ajlende Sep 9, 2021
c15c1cf
Render duotone styles when settings are disabled
ajlende Sep 14, 2021
3c2c166
Improve mechanism to declare a selector for the property
oandregal Sep 21, 2021
220d86d
Revert "Improve mechanism to declare a selector for the property"
oandregal Sep 22, 2021
a0134b1
Merge branch 'trunk' into add/duotone-theme-json-styles
ajlende Sep 22, 2021
537d608
duotone-filter to just duotone css var infix
ajlende Sep 23, 2021
23cd929
Merge branch 'trunk' into add/duotone-theme-json-styles
ajlende Sep 27, 2021
775717e
Revert duotone SVG to be inside the footer
ajlende Sep 27, 2021
2a6a7e8
Simplify value_func args
ajlende Sep 27, 2021
c147b04
Additionally strip multiple spaces in SVG
ajlende Sep 27, 2021
e31fa8e
Improve code quality and inline docs
ajlende Sep 27, 2021
8aaa12f
Fix value_func after rename
ajlende Sep 27, 2021
c793a69
Improve inline docs
ajlende Sep 27, 2021
df50ef0
Update duotone-filter -> duotone for generated ids
ajlende Sep 28, 2021
53b154b
Rename preset_meta -> preset_metadata
ajlende Sep 28, 2021
008c5d5
Move duotone styles from color -> filter
ajlende Sep 28, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 77 additions & 68 deletions lib/block-supports/duotone.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,30 +251,15 @@ function gutenberg_register_duotone_support( $block_type ) {
}

/**
* Render out the duotone stylesheet and SVG.
* Get the duotone filter property.
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
* @param string $duotone_id Unique id for the duotone filter.
* @param array $duotone_colors Array of CSS color strings that can be parsed by tinycolor.
*
* @return string Duotone CSS filter property.
*/
function gutenberg_render_duotone_support( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );

$duotone_support = false;
if ( $block_type && property_exists( $block_type, 'supports' ) ) {
$duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
}

$has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] );

if (
! $duotone_support ||
! $has_duotone_attribute
) {
return $block_content;
}

$duotone_colors = $block['attrs']['style']['color']['duotone'];
function gutenberg_get_duotone_filter_property( $duotone_id, $duotone_colors ) {
$filter_id = 'wp-duotone-filter-' . $duotone_id;

$duotone_values = array(
'r' => array(),
Expand All @@ -289,46 +274,21 @@ function gutenberg_render_duotone_support( $block_content, $block ) {
$duotone_values['b'][] = $color['b'] / 255;
}

$duotone_id = 'wp-duotone-filter-' . uniqid();

$selectors = explode( ',', $duotone_support );
$selectors_scoped = array_map(
function ( $selector ) use ( $duotone_id ) {
return '.' . $duotone_id . ' ' . trim( $selector );
},
$selectors
);
$selectors_group = implode( ', ', $selectors_scoped );

ob_start();

?>

<style>
<?php echo $selectors_group; ?> {
filter: url( <?php echo esc_url( '#' . $duotone_id ); ?> );
}
</style>

<svg
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 0 0"
width="0"
height="0"
focusable="false"
role="none"
style="visibility: hidden; position: absolute; left: -9999px; overflow: hidden;"
>
<svg xmlns="http://www.w3.org/2000/svg">
ajlende marked this conversation as resolved.
Show resolved Hide resolved
<defs>
<filter id="<?php echo esc_attr( $duotone_id ); ?>">
<filter id="<?php echo esc_attr( $filter_id ); ?>">
<feColorMatrix
type="matrix"
<?php // phpcs:disable Generic.WhiteSpace.DisallowSpaceIndent ?>
values=".299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
0 0 0 1 0"
<?php // phpcs:enable Generic.WhiteSpace.DisallowSpaceIndent ?>
values="
.299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
0 0 0 1 0
"
/>
<feComponentTransfer color-interpolation-filters="sRGB" >
<feFuncR type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['r'] ) ); ?>" />
Expand All @@ -341,25 +301,74 @@ function ( $selector ) use ( $duotone_id ) {

<?php

$duotone = ob_get_clean();
$svg = ob_get_clean();

// Clean up the whitespace so it can be used in a data uri.
$svg = preg_replace( "/(\r|\n|\t)+/", ' ', $svg );
$svg = preg_replace( '/> </', '><', $svg );
$svg = trim( $svg );

$data_uri = 'data:image/svg+xml,' . $svg . '#' . $filter_id;

// All the variables are already escaped above, so we're not calling esc_url() here.
return "url('" . $data_uri . "')";
ajlende marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Render out the duotone stylesheet and SVG.
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
*/
function gutenberg_render_duotone_support( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );

$duotone_support = false;
if ( $block_type && property_exists( $block_type, 'supports' ) ) {
$duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
}

$has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] );

if (
! $duotone_support ||
! $has_duotone_attribute
) {
return $block_content;
}

$duotone_id = uniqid();
$duotone_colors = $block['attrs']['style']['color']['duotone'];
$filter_property = gutenberg_get_duotone_filter_property( $duotone_id, $duotone_colors );

$filter_id = 'wp-duotone-filter-' . $duotone_id;

$scope = '.' . $filter_id;
$selectors = explode( ',', $duotone_support );
$scoped = array();
foreach ( $selectors as $sel ) {
$scoped[] = $scope . ' ' . trim( $sel );
}
$selector = implode( ', ', $scoped );

// !important is needed because these styles render before global styles,
// and they should be overriding the duotone filters set by global styles.
$filter_style = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG
? $selector . " {\n\tfilter: " . $filter_property . " !important;\n}\n"
: $selector . '{filter:' . $filter_property . ' !important;}';
Comment on lines +374 to +378
Copy link
Contributor Author

@ajlende ajlende Sep 9, 2021

Choose a reason for hiding this comment

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

Would it be safe to add specificity by prepending this with .wp-site-blocks instead of using !important? Can I count on having that as a container? Is it weird to use body instead of that class if it's not available?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can rely on .wp-site-blocks, but i do wonder if this is a valid use of !important - since this is a setting on an individual block, I think it should always take precedence over other CSS.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There was mention of a similar situation for elements block supports in the #core-editor Slack today.

The linked issue is #33437 and there's a fix by removing !important in #34689. That issue is caused by the cascade and because sometimes buttons, that we want to style differently, use the <a> tag.

The filter property doesn't cascade the same way, so we're probably fine here. But the elements block supports goes to show that just because we're setting an individual block, !important still might not be safe to use.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a specific need for the duotone filters to be enqueued before the global styles?

Copy link
Member

@oandregal oandregal Sep 10, 2021

Choose a reason for hiding this comment

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

Yeah, if we can make it work without the !important is nice.

The issue with #33437 was that .wp-element-* a targeted any a element in a container (group with paragraph with link + button + social link blocks). So the issue was about how unspecific the selector was. Which makes me think: is it possible that the container with the .wp-duotone-ID has an image with duotone and another without it? I'm just not familiar with all the blocks this can affect to. I can't think of any off the top of my head.

Copy link
Member

Choose a reason for hiding this comment

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

More context about where we use !important: we use it in the preset classes, such as .has-{value}-color to make sure the user selection is respected despite its low specificity. More context #29533

In the case of the link color, it was probably too much due to the wide reach of the selector and side-effects. We're reverting it in this case if the current PR seems solid.


wp_register_style( $filter_id, false, array(), true, true );
wp_add_inline_style( $filter_id, $filter_style );
wp_enqueue_style( $filter_id );

// Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
$content = preg_replace(
return preg_replace(
'/' . preg_quote( 'class="', '/' ) . '/',
'class="' . $duotone_id . ' ',
'class="' . $filter_id . ' ',
$block_content,
1
);

add_action(
// Ideally we should use wp_head, but SVG defs can't be put in there.
'wp_footer',
function () use ( $duotone ) {
echo $duotone;
}
);

return $content;
}

// Register the block support.
Expand Down
Loading