-
Notifications
You must be signed in to change notification settings - Fork 799
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
Google Fonts: enqueue only picked font families in Gutenberg #23810
Google Fonts: enqueue only picked font families in Gutenberg #23810
Conversation
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖 The e2e test report can be found here. Please note that it can take a few minutes after the e2e tests checks are complete for the report to be available. Once your PR is ready for review, check one last time that all required checks (other than "Required review") appearing at the bottom of this PR are passing or skipped. Jetpack plugin:
|
e7674df
to
e0d1aad
Compare
projects/plugins/jetpack/modules/google-fonts/class-jetpack-google-fonts.php
Outdated
Show resolved
Hide resolved
/** | ||
* Scan global styles for Google fonts. | ||
*/ | ||
public function scan_global_styles_for_google_fonts() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function probably does not belong here. Right now, however, I'm struggling to find a better place to put it...
When it's time, we can probably move this to Gutenberg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this could be in the package for now, maybe in the Utils class?
https://github.com/Automattic/jetpack/tree/212b4d7bc0ab2be67b0ed7347f6496454e390052/projects/packages/google-fonts-provider/src
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually created a brand new package. It didn't feel connected to the provider at all, and it could be used by other modules so hurray for reusability!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this may be overkill. When used by third-parties, wouldn't that code pretty much always be used alongside the existing automattic/jetpack-google-fonts-provider
package for now? Even if not fully related, maybe it could be a new class inside that package? We could always extract it into a new package if we start needing it elsewhere in the future, somewhere where the jetpack-google-fonts-provider package is not already required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this may be overkill.
I would agree; I think separating this out into its own package doesn't really provide much benefit. Realistically, you should always be introspecting fonts when using the provider with more than a few font faces (especially with how Google creates so many font face declarations for different character ranges). I'd hope that packaging the provider and introspection utilities together would encourage that.
If automattic/jetpack-google-fonts-provider
as a package is too specific, I'd rather deprecate it and create a new package to more broadly encompass webfont functionality.
Additionally, having Global styles introspection and block introspection in separate places seems odd; I think these should be together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. Any reason why we created this jetpack-google-fonts-provider
package in the first place? It looks weird to have this as separate parts.
And I do agree -- I think it's better if we collocated the provider and the introspector together within the Jetpack Google Fonts module. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry if I wasn't clear, I don't mean stop using packages all together. My understanding is that Jetpack (the monorepo) is built around packages for functionality that's reusable across plugins. That way if Woo, for example, wants to use something that's in Jetpack, it doesn't have to copy the code into itself, or require install the entire Jetpack plugin be installed, it can just require the specific package it needs.
So for our use case, I think this separation makes sense:
- Package: font provider, and any functionality needed for loading fonts from the provider. No hooks should be added by default, so it's up to package consumers to "turn on" any functionality from the package they want to use.
- Module: Our WP.com specific list of curated Google Fonts, and calling the necessary functions from the package to load those fonts.
The main goal of the separation here being any other plugin could use the fonts package to load it's own set of Google Fonts, independent from the fonts module in the Jetpack plugin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, a few things:
- The global styles introspection utils package is not adding any hook -- it's simply getting the global styles and looking for font families. However, that might be incomplete because only global styles introspection is not enough even for this use case as block settings are ignored. One thing I would like to have is an action called
jetpack_content_introspection_webfont_found
or something like that would get fired whenever a font family was found in the content; - The introspection package is not really related to the provider. Adding the provider does not add introspection, and vice-versa. One could use introspection without necessarily working with Google provider. The provider's job is to handle and emit the enqueued fonts, and that's it. It doesn't care about introspection.
- The module could (and will) use both packages. Other modules might not, and only use the provider, or only use the introspection... I do think they should be decoupled as those are very useful things to expose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other modules might not, and only use the provider, or only use the introspection... I do think they should be decoupled as those are very useful things to expose.
Imagining myself as a developer with 0 knowledge of the fonts work we've done, I'd find it unintuitive to need to import two separate packages to enqueue webfonts in a performant way. That is to say, I wouldn't think to look for an introspector while setting up fonts support.
I agree with Jeremy + Grant -- I suspect there'd be more clarity in colocating both introspection and provider logic in the same package for now. I understand that it would be strange to place everything inside the jetpack-google-fonts-provider
, but like Grant mentioned earlier, I'd be on board with creating a new, more appropriately named package that contains everything.
Either way, I see that we've decided to punt this decision for later -- just my 2 cents.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a few comments below, let me know what you think!
Also, could you add a changelog entry to your PR, using jetpack changelog add plugins/jetpack
?
Thank you!
projects/plugins/jetpack/modules/google-fonts/class-jetpack-google-fonts.php
Outdated
Show resolved
Hide resolved
/** | ||
* Scan global styles for Google fonts. | ||
*/ | ||
public function scan_global_styles_for_google_fonts() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this could be in the package for now, maybe in the Utils class?
https://github.com/Automattic/jetpack/tree/212b4d7bc0ab2be67b0ed7347f6496454e390052/projects/packages/google-fonts-provider/src
projects/plugins/jetpack/modules/google-fonts/jetpack-google-fonts.php
Outdated
Show resolved
Hide resolved
e0d1aad
to
86b6972
Compare
29ee55d
to
7d5316b
Compare
7d5316b
to
ce6f7bc
Compare
Caution: This PR has changes that must be merged to WordPress.com |
/** | ||
* We are already enqueueing all registered fonts by default when loading the block editor, | ||
* so we only need to scan for webfonts when browsing as a guest. | ||
*/ | ||
if ( ! is_admin() ) { | ||
add_action( 'wp_loaded', array( $this, 'enqueue_global_styles_google_fonts' ) ); | ||
add_filter( 'pre_render_block', array( $this, 'scan_block_for_google_fonts' ), 10, 2 ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to keep it like that until Gutenberg lazy loads font-face declarations as they're picked in the block editor.
Not entirely certain if it's just me, but I keep running into critical errors when running this locally with the instructions provided.
What I'm doing:
What I'm seeing: |
9670f69
to
b9c92a4
Compare
I was getting the same error, but I think the latest changes (that add Note you'll need to run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The functionality here is working as expected. Running with current Gutenberg trunk:
- Without the change, fonts appear in Font Family dropdowns in the editor, but no fonts are loaded on the front-end.
- With the change, fonts from Global styles or block settings in the editor are loaded on the front-end.
We just need to figure out the right balance of package vs. module functionality (see my other comments) and I think this will be ready to go.
@zaguiini Here's a draft PR to illustrate what I was trying to say in the comments: #23958
Let me know what you think! |
Thanks, @creativecoder! You made very good points, I agree with all of them. In addition, there are some things I'd like to observe... All found font faces are enqueued, regardless if we were the ones that registered themThe implementation in my PR tries not to enqueue every single font that gets detected by introspection. We're keeping track of the fonts we registered, then, when a font is found, we're checking if it was registered by us before enqueueing. This tightens things a bit, so only the subset of fonts we registered get enqueued... I wonder how could we implement that intermediary state without the use of a class in a reliable way? One possibility is to get the list of registered fonts from the API ( That's why I transitioned into a class, and that's the purpose of the Could we achieve something similar without classes? Maybe using global variables or a singleton class but I'm not sure how common this pattern is in Jetpack... Introspection classes in the same plugin as the Google fonts providerIt looks really off to have the introspection classes inside the Google fonts provider plugin. Those are different things that could work without each other... But I cannot think of a better place to put it without creating a new plugin. If there's absolutely no better place to put it (how about putting it in the module?), I'm fine with leaving it where it is. Right now they do have some degree of relationship because as you read in my first point, we want only to enqueue the fonts that we registered. However, I see them as more general. It would be really nice if we could abstract the global styles and block introspection and apply an action or filter whenever a registered font is found. Exposing that as a plugin, I can see a lot of external plugins using it to do the same thing within their own plugins. What I'm trying to say is that it's not really tied to Google -- it has a lot of potential reusability by third parties. Unless, of course, we don't want them using it. Migrating to GutenbergNot specific to our implementations, but how are going to migrate from Jetpack to Gutenberg when it's time? Is there any guideline on how to deal with these situations? Should we add a guard that would not hook up our own filters in case Gutenberg already did so? Curious about how it'll work... Thinking about the migration to Gutenberg. Introspection is going to happen there at some point (see WordPress/gutenberg#40363 and WordPress/gutenberg#40386), so the introspection code included here is temporary. All things considered... Does it make sense to include it on a package instead of simply adding it to the module? I believe it's easier to remove things from a module than to deprecate a plugin, right? If that's the case, why bother adding it to a package? Sorry for the wall of text, but those are my concerns 😄 |
Out of curiosity, is there a reason we'd prefer not to use a class to implement this logic? |
} | ||
|
||
/** | ||
* Introspect global styles for webfonts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add an @return inline comment here for consistency?
return $matches['slug']; | ||
} | ||
|
||
return $font_family; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you describe what this line would return and under what circumstances this condition would be triggered? I'm guessing one of the presets we evaluate ( blocks, HTML, or global typography ) return a normal slug.
Also completely unrelated to this PR, but it seems so odd to have so many different ways of representing the font family slug 🙃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also completely unrelated to this PR, but it seems so odd to have so many different ways of representing the font family slug 🙃
Right! That's because you can specify a CSS variable to link it inside theme.json
. You can also pick a font in the editor, and then it would become the second format var:preset|font-family|slug
, which is the one that's registered in the global styles CPT.
But then, you can also solely specify the slug, and that's why we're returning... If none of the formats match, just return what's in there and we'll try our luck. It might be a system font, might be just the slug... Who knows.
...ckages/global-styles-webfonts-introspector/src/class-global-styles-webfonts-introspector.php
Show resolved
Hide resolved
|
||
$found_webfonts = array(); | ||
|
||
// Look for fonts in block presets... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-blocking
: How would we feel about providing a little more context for those that might be unfamiliar with global styles?
// Global styles can specify typography for individual blocks,
// so look for fonts in block presets...
// Global styles can specify typography for elements like headings, paragraphs, etc.,
// so look for fonts in HTML element presets...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's more explicit, for sure! I'm curious if this is necessary since it kind of goes without saying that global styles can specify the typography since we're looking for them in the code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious if this is necessary since it kind of goes without saying that global styles can specify the typography since we're looking for them in the code?
Sure -- for those examples, I see what you're saying. My intention is to ultimately provide clarity for others by adding context about business use cases, especially since this code will live in Jetpack for the time being ( where folks might not be as familiar with the global styles feature ).
Does that make sense? Maybe we can think about different, more meaningful wording.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. Sure, we can think of something.
|
||
if ( empty( $font_family_slugs ) ) { | ||
// Fonts were not registered. | ||
continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this lead to errors being silently hidden?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really -- we're always emitting errors in the front-end so they'll be noticed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm...I might be missing something but here's the flow of logic I have in my head ( I might be mistaken about this! )
- We'd attempt to register webfonts
register_webfont
method would returnfalse
( link )wp_register_webfonts
would fail and return an empty array offont_family_slugs
- We wouldn't enqueue any fonts because
registered_google_fonts
would be empty - The frontend would silently fallback to some system or default font
When would errors be emitted on the frontend?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wp_register_webfonts
method runs for every font family declared in the list, so there'd be multiple runs and not a single one.
And yes, that's mostly right. One thing, though, is that the user/developer would be notified after register_webfont method would return false
on the front-end, so they'd have a clue on how to fix what went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
register_webfont method would return false on the front-end, so they'd have a clue on how to fix what went wrong
Ah I see -- that's what I was missing. When you have time, would you mind explaining how returning false would notify the user / dev in the frontend? The only way I can see this working is with error logging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not for the front-end. Return false
is more about conditional action upon registration failure for example. I do agree, though, that we're lacking descriptive return values on failure when dealing with the back-end (i.e. false
can mean multiple things). But that's not something to address in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not for the front-end.
Oh you had mentioned the front-end a few times in previous comments so there might have been a miscommunication ( link )
But that's not something to address in this PR.
Agreed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, my bad.
What I tried to say was that a notice is displayed on the front end, so the user can take action.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code generally looks good to me. @creativecoder made a number of great points though
- Regarding modularity and having global styles introspection play well with other introspection logic...from what I can tell, what's written here would work given those considerations.
- Tests! Totally agree.
+1 from me when my last few comments are answered and tests are written.
* @param string $font_family_slug The block content. | ||
*/ | ||
private function maybe_enqueue_font_family( $font_family_slug ) { | ||
if ( ! isset( $this->registered_google_fonts[ $font_family_slug ] ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to check if the font is registered before trying to enqueue it:
if ( ! isset( $this->registered_google_fonts[ $font_family_slug ] ) ) { | |
if ( ! wp_webfonts()->is_font_family_registered( $font_family_slug ) ) { |
The is_font_family_registered
function will be exposed after WordPress/gutenberg#39988 gets merged. For now, what we can use is:
if ( ! isset( $this->registered_google_fonts[ $font_family_slug ] ) ) { | |
if ( ! isset( wp_webfonts()->get_registered_webfonts()[ $font_family_slug ] ) ) { |
After meeting with @jeyip and @creativecoder, we came up with a few observations: We might not need a class for the Jetpack Google fonts moduleThe reason for having a class was to share the state between hook calls: the one that registers webfonts, and the introspection callbacks (e.g. This shared state existed so we could be a little more cautious regarding which fonts get enqueued. We wanted to make sure that only the fonts we registered were enqueued, and that's a safety measure. However, that's too cautious. See, right now, Gutenberg is in this weird state where externally registered webfonts picked in the editor do not have any effect on the front end. Removing the guard clause would allow any picked font in the editor to show up in the front end. This is the worst-case scenario, and one that we actually want to have as it would temporarily fix Gutenberg's behavior (see WordPress/gutenberg#40363 and WordPress/gutenberg#40386). A note regarding the implementation is this comment: https://github.com/Automattic/jetpack/pull/23810/files#r851520186. We still need to check if the font is registered before enqueueing it since we're not sure it was registered before now that we'll get rid of the intermediary state. Not checking and trying to enqueue a webfont that was not registered will add a notice on the front end. We might need additional information on where to put the introspection classes@jeherve could really help us here as we're uncertain about where to put them. It still feels unnatural to add the introspection classes to the Google provider package. Ideally, we should create a new package to expose a separate thing since one can use the introspection class without necessarily using the Google font provider, and vice-versa. But, how much of a hassle is it to create, deploy and maintain packages in WPCOM? Should we? How can we balance the trade-off between having a well-organized codebase and the burden of maintaining a new package? The answers are beyond our reach right now, but @Automattic/jetpack-crew might have them all. Migrating to GutenbergHow are going to migrate introspection from Jetpack to Gutenberg when it's time? Is there any guideline on how to deal with these situations? Should we add a guard to the module that would not hook up our own filters in case Gutenberg already did so? Curious about how it'll work... Introspection is going to happen there at some point (see WordPress/gutenberg#40363 and WordPress/gutenberg#40386), so the introspection code included here is temporary. Please take that into consideration when answering the questions on the "We might need additional information on where to put the introspection classes" paragraph. Shepherding the PR to
|
We may not ever need to. We may even remove the introspection from Jetpack when it makes sense. My takeaways from this long slack thread (p1649889597371109-slack-CHN6J22MP) with @mtias were:
When this is the case, there is no need for all the introspection since fonts available to individual blocks are already enqueued from being part of theme.json and any fonts selected by global styles would be enqueued from there. From my understanding, the introspection is now only a temporary solution to allow Google fonts on dotcom sooner. This serves to band-aid the gaps in enqueueing that should not be present in the end state of the Gutenberg webfonts API. It fills the gap of not enqueueing global styles selections (which should be resolved soon?). This also allows us to fill the gap in the current/temporary state of registered webfonts showing up in individual block settings - this part seems reasonable to me as once Gutenberg removes these webfonts from this section any previous selections on dotcom should be back-wards compatible due to this introspection code. |
It seems I may be mistaken about this point. Im still not certain of Gutenberg's plans but this part may remain in place meaning our introspection here would remain valuable for all the individual block-settings. |
To move this forward, I'd like to get some guidance from @Automattic/ground-control on whether to create a new package or not. You can see how I've attempted to cleanly divide the Google Fonts provider from the font enqueuing ("introspection") code in this draft PR: #23958. On the one hand, the enqueuing code is potentially useful as it's own package for use with other font providers, inside or outside Jetpack. On the other, the WP Webfonts API is still maturing and not really stable yet, so significant changes my be needed, or that code might become irrelevant, so it may be worth waiting. Also, I know creating a separate package incurs some overhead, particularly on wpcom. Do you have a preference on keeping the code for the Introspectors classes (from #23958) within the Google Fonts provider, or creating a separate package for it, at this time? We'd like to wrap this up and get it shipped asap. Thanks! |
Sorry, I was AFK for the past few days, only getting back to this now. All in all, and given that this is all building atop something that's not part of WordPress Core, thus not widely adopted yet, I would vote towards keeping it simple so we can move and iterate quickly. In practice, I think that means less packages. Even though a new package may prove useful to third-parties in the future, I don't think it will be useful to many people right now. Since there are still many moving pieces in the new API, I don't think anyone would reasonably start building their own solution on top of 2 separate Jetpack packages that themselves rely on a rapidly changing Gutenberg-only feature. In short, I would:
What do you think? |
Changes proposed in this Pull Request:
As WordPress/gutenberg#39559 got merged, we have more control over which font faces get to the front-end after registering.
This PR introduces code that introspects block usage and global styles configuration looking for Google fonts that have been registered.
Jetpack product discussion
N/A.
Does this pull request change what data or activity we track or use?
No.
Testing instructions:
Setting up the environment:
trunk
Gutenberg;a8c-wp-env
because we had loads of trouble trying to get it working);composer install
insideprojects/plugins/jetpack
so the module gets linked with the plugin;Enqueueing fonts used in blocks:
In the page source, check that only the picked font family shows up in the
style#webfonts-inline-css
element.Enqueueing fonts used in global styles:
In the page source, check that only the picked font families, both used in blocks above and the one that you just set in global styles, shows up in the
style#webfonts-inline-css
element.