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

Duotone: Only output Duotone filters that are used #48291

Closed
wants to merge 12 commits into from

Conversation

scruffian
Copy link
Contributor

@scruffian scruffian commented Feb 21, 2023

What?

This is an attempt at fixing #38299. The idea is that by outputting only the SVGs that are used, rather than all of the presets, we can remove a lot of unused SVGs on the frontend.

Why?

Less code will make the page load faster.

How?

  1. Create a new WP_Gutenberg class.
  2. Add an action to the WP_Theme_JSON class to make is possible to hook onto declarations.
  3. Save a list of the CSS variables that duotone uses to reference SVGs, in a static array on the new class.
  4. Add a filter to the WP_Theme_JSON class to make is possible to output SVGs from another class.
  5. Output the SVGs that are saved in the static array created in step 1.

Testing Instructions

  1. Switch to a theme that has a default duotone applied to all blocks (you can use the Sherbet variation of TT3)
  2. Add an image to a post
  3. Confirm that the image has a duotone applied
  4. Check the page source and check that none of the default duotone SVGs are being output.

Testing Instructions for Keyboard

For step 3 above check that the source contains an SVG with an ID which is the same as the id that the CSS on an image references in its filter property.
For step 4 check that there are no other SVGs with filters in this format on the page:
<filter id="wp-duotone-*">

@@ -2425,8 +2461,34 @@ function( $pseudo_selector ) use ( $selector ) {
$declarations_duotone = array();
foreach ( $declarations as $index => $declaration ) {
if ( 'filter' === $declaration['name'] ) {
unset( $declarations[ $index ] );
$filter_preset = $this->gutenberg_get_preset_from_declaration( $declarations[ $index ] );
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 one area that needs a lot more attention. What we have is a filter preset of the form var(--wp--preset--duotone--(.*)). What we need is a preset object from theme.json to get the settings so we can output the correct SVG. I think there must be a better way of getting the preset than using the slug like this.

@scruffian scruffian self-assigned this Feb 21, 2023
@ajlende
Copy link
Contributor

ajlende commented Feb 22, 2023

I feel a bit weird about adding a side-effect to get_styles_for_block. It's the sort of function that could end up getting called elsewhere outside of rendering which could cause issues.

I wonder if we could do something similar to this function that just returns the string of SVGs and then call it in gutenberg_enqueue_global_styles or keep the wp_body_open action?

*
* @since 5.9.1
*/
function gutenberg_global_styles_render_svg_filters() {
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 just a copy of wp_global_styles_render_svg_filters

*
* @return string
*/
function gutenberg_get_global_styles_svg_filters() {
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 just a copy of gutenberg_get_global_styles_svg_filters with the reference to the WP_Theme_JSON_Resolver class updated to WP_Theme_JSON_Resolver_Gutenberg.

@scruffian scruffian marked this pull request as ready for review February 22, 2023 11:24
@getdave
Copy link
Contributor

getdave commented Feb 22, 2023

As I see it the problem people have is that we're dumping a tonne of SVG markup on the page when those SVGs may not be used. So what we need is a way to retain the ability to select from all of the presets but only load the SVGs for the presets that are in use.

This PR partly addresses this but might leave us in a situation where the available presets are not presented/available to the user because they were not currently in use on the given page.

Perhaps a different approach could be to

  • allow all presets to be defined and availanle as CSS custom props
  • only inline SVGs that are currently in use
  • lazy load any SVGs that are needed as a result of users selecting a new preset

@scruffian
Copy link
Contributor Author

This PR partly addresses this but might leave us in #48318 because they were not currently in use on the given page.

I don't think this is true. The presets will continue to be presented to the user, they just might not be output in the frontend.

@scruffian
Copy link
Contributor Author

I updated the approach here to use actions and filters. I know that this comes with a maintenance burden, but the benefit in this case is that it will allow other plugins to provide their own filters. cc @oandregal

@jeryj
Copy link
Contributor

jeryj commented Feb 22, 2023

I updated the approach here to use actions and filters. I know that this comes with a maintenance burden,

What is the extra maintenance burden with using actions and filters here?

@scruffian
Copy link
Contributor Author

What is the extra maintenance burden with using actions and filters here?

Once an action / filter is in core we have to support it indefinitely.

$duotone_preset_css_var = WP_Theme_JSON_Gutenberg::get_preset_css_var( array( 'color', 'duotone' ), $duotone_preset['slug'] );

// Only output the preset if it's used by a block.
if ( in_array( $duotone_preset_css_var, WP_Duotone::$duotone_presets, true ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm on Theme Twenty Twenty Two and getting a fatal error at this line due to WP_Duotone::$duotone_presets being null. I haven't figured out why it's getting set to null yet, but adding an is_array( WP_Duotone::$duotone_presets ) && check does fix the error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. It's null because TT2 doesn't set a duotone by default.

jeryj and others added 2 commits February 22, 2023 14:21
The in_array check on WP_Duotone::$duotone_presets causes a fatal error if there are no filter declarations present in the theme json.
…s/gutenberg into update/output-only-used-filters
@github-actions
Copy link

github-actions bot commented Feb 22, 2023

Flaky tests detected in 4e1876d.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/4247183882
📝 Reported issues:

@scruffian
Copy link
Contributor Author

One problem with the approach here is that it still outputs the CSS variables for the filters that aren't being used.

Comment on lines +555 to +556
// I think we should do this differently, but I'm not sure how.
$filters .= '<style>body{' . $duotone_preset_css_var . ':'. gutenberg_get_duotone_filter_property( $duotone_preset ) . ';}</style>';
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice if it could still be handled by value_func in WP_Theme_JSON_Gutenberg instead of moving it here.

Maybe we have to think a little more about where the filters/actions belong in that code? Could it be more useful in gutenberg_add_global_styles_for_blocks—modifying the $metadata passed into get_styles_for_block?

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 @ajlende has a point. Somehow appending that preset CSS code doesn't feel it belongs in get_filter_svg and since it's a preset and WP Theme json gives us a way to add callback for presets then we should use that.

@getdave
Copy link
Contributor

getdave commented Feb 23, 2023

One problem with the approach here is that it still outputs the CSS variables for the filters that aren't being used.

I don't see that as a major problem and not one you need to tackle here. Getting rid of the SVG markup is the main thing.

@oandregal
Copy link
Member

I updated the approach here to use actions and filters. I know that this comes with a maintenance burden, but the benefit in this case is that it will allow other plugins to provide their own filters. cc @oandregal

Thanks for the ping. Not super fan of adding a filter to a private method/class, as it prevents us from modifying that private unit going forward (it makes it effectively public and part of a flow we should not change). What other options do we have?

Is allowing plugins to modify the theme.json schema something we need? I'd decouple that decision from what this PR is trying to achieve, as it comes with a different set of trade-offs.

@scruffian
Copy link
Contributor Author

Closing in favour of #48374

@scruffian scruffian closed this Mar 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants