Directory

Webfonts: enqueue fonts listed in theme.json by zaguiini · Pull Request #39988 · WordPress/gutenberg · GitHub
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

Webfonts: enqueue fonts listed in theme.json #39988

Closed
wants to merge 37 commits into from

Conversation

zaguiini
Copy link
Contributor

@zaguiini zaguiini commented Apr 1, 2022

Part of #39332.

What?

We want to enqueue only webfonts listed in theme.json.

And we also want to register webfonts if needed by passing a provider key in a font face declaration, along with any additional properties required by the specified provider.

Why?

Save data. Each font face declaration might add up to 2kb to every page load. Allow-listing webfonts through a centralized theme file -- like theme.json -- makes it possible for the theme developer to pick only a subset of the registered fonts.

How

The logic runs in three steps:

  1. Register webfonts listed in theme.json;
  2. Add programmatically registered webfonts to theme.json (so they show up in the editor);
  3. Enqueue webfonts listed in theme.json.

Testing

Externally registered font families not listed in theme.json

They should not be enqueued.

Enqueue an externally registered font family

In functions.php:

add_action(
  'after_setup_theme',
  function() {
    wp_register_webfont(
      array(
        'font-family'  => 'Roboto',
        'font-style'   => 'normal',
        'font-stretch' => 'normal',
        'font-weight'  => '400',
        'src'          => get_theme_file_uri( '/assets/fonts/Roboto-regular.ttf' ),
      )
    );

    wp_register_webfont(
      array(
        'font-family'  => 'Source Serif Pro',
        'font-style'   => 'normal',
        'font-stretch' => 'normal',
        'font-weight'  => '400',
        'src'          => get_theme_file_uri( '/assets/fonts/Source-Serif-Pro-Regular.ttf' ),
      )
    );
  }
);

In theme.json[settings][typography][fontFamilies]:

{
  "fontFamily": "Roboto",
  "slug": "roboto",
  "name": "Roboto"
}

Only Roboto should be enqueued.

Enqueue only one of all externally registered font faces

In functions.php:

add_action(
  'after_setup_theme',
  function() {
    wp_register_webfont(
      array(
        'font-family'  => 'Roboto',
        'font-style'   => 'normal',
        'font-stretch' => 'normal',
        'font-weight'  => '400',
        'src'          => get_theme_file_uri( '/assets/fonts/Roboto-Regular.ttf' ),
      )
    );

    wp_register_webfont(
      array(
        'font-family'  => 'Roboto',
        'font-style'   => 'bold',
        'font-stretch' => 'normal',
        'font-weight'  => '900',
        'src'          => get_theme_file_uri( '/assets/fonts/Roboto-Bold.ttf' ),
      )
    );
  }
);

In theme.json[settings][typography][fontFamilies]:

{
  "fontFamily": "Roboto",
  "slug": "roboto",
  "name": "Roboto",
  "fontFaces": [
    {
      "fontFamily": "Roboto",
      "fontWeight": "900",
      "fontStyle": "bold"
    }
  ]
}

Only Roboto:900:bold should be enqueued.

Register (and enqueue) a collection of font faces to the same provider

In theme.json[settings][typography][fontFamilies]:

{
  "fontFamily": "Roboto",
  "slug": "roboto",
  "name": "Roboto",
  "provider": "local",
  "fontFaces": [
    {
      "fontFamily": "Roboto",
      "fontWeight": "400",
      "fontStyle": "regular",
      "src": "file:./assets/fonts/Roboto-Regular.ttf"
    },
    {
      "fontFamily": "Roboto",
      "fontWeight": "900",
      "fontStyle": "bold",
      "src": "file:./assets/fonts/Roboto-Bold.ttf"
    }
  ]
}

Both faces of Roboto should be enqueued by the same provider.

Register (and enqueue) a collection of font faces through different providers

In theme.json[settings][typography][fontFamilies]:

{
  "fontFamily": "Roboto",
  "slug": "roboto",
  "name": "Roboto",
  "fontFaces": [
    {
      "provider": "google",
      "fontFamily": "Roboto",
      "fontWeight": "400",
      "fontStyle": "regular",
      "src": "file:./assets/fonts/Roboto-Regular.ttf"
    },
    {
      "provider": "local",
      "fontFamily": "Roboto",
      "fontWeight": "900",
      "fontStyle": "bold",
      "src": "file:./assets/fonts/Roboto-Bold.ttf"
    }
  ]
}

Both faces of Roboto should be enqueued through different providers.

@zaguiini zaguiini force-pushed the try/register-and-enqueue-webfonts-by-family branch 2 times, most recently from edaa21d to f5f8339 Compare April 2, 2022 01:10
@zaguiini zaguiini force-pushed the try/enqueue-fonts-used-in-theme-json branch from 55397b0 to 5c963e9 Compare April 2, 2022 01:15
@hellofromtonya hellofromtonya force-pushed the try/register-and-enqueue-webfonts-by-family branch from f5f8339 to 90cc444 Compare April 4, 2022 12:42
@zaguiini zaguiini force-pushed the try/enqueue-fonts-used-in-theme-json branch 2 times, most recently from 2fc8d5a to 6eed9b7 Compare April 4, 2022 13:38
Base automatically changed from try/register-and-enqueue-webfonts-by-family to trunk April 4, 2022 13:52
@zaguiini zaguiini force-pushed the try/enqueue-fonts-used-in-theme-json branch 6 times, most recently from adcc6ce to 12d41f8 Compare April 5, 2022 04:07
Copy link
Contributor Author

@zaguiini zaguiini left a comment

Choose a reason for hiding this comment

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

Where should I put utility functions? Should I keep them as functions prefixed by gutenberg_ or put them in the WP_Webfonts class?

If the former applies: should we move WP_Webfonts::get_font_slug elsewhere? Should we create a file so the utils are concentrated somewhere, or should we keep them collocated where they're defined/used?

lib/experimental/add-registered-webfonts-to-theme-json.php Outdated Show resolved Hide resolved
@zaguiini zaguiini self-assigned this Apr 5, 2022
@zaguiini zaguiini changed the title Enqueue fonts used in theme.json Webfonts: enqueue fonts used in theme.json Apr 5, 2022
@zaguiini zaguiini changed the title Webfonts: enqueue fonts used in theme.json Webfonts: enqueue fonts listed in theme.json Apr 5, 2022
@mattwiebe
Copy link
Contributor

Where should I put utility functions? Should I keep them as functions prefixed by gutenberg_ or put them in the WP_Webfonts class?

If the former applies: should we move WP_Webfonts::get_font_slug elsewhere? Should we create a file so the utils are concentrated somewhere, or should we keep them collocated where they're defined/used?

Good questions, I don't have a perfect answer here. Generally speaking, if a function gets called outside of an internal class context, we prefer to have raw global functions with a wp_ or gutenberg_ prefix. Oftentimes that will actually be a wrapper to some object's method, as in the way that wp_register_webfont calls WP_Webfonts::register_webfont. So you can see the general principle in that webfonts.php contains the public API and methods we want to expose, while class-wp-webfonts.php contains all of the logic needed to make the functionality work.

In the case of something like WP_Webfonts::get_font_slug, I don't see the use-case for having it more publicly available as global function and would leave it as a class method. And as for the various methods inside add-registered-webfonts-to-theme-json.php and register-webfonts-from-theme-json.php, I think we would probably be best served with a common font utils file of some kind.

Copy link
Contributor

@mattwiebe mattwiebe left a comment

Choose a reason for hiding this comment

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

This is some really good work! ✨

I think we should look at a different approach for gutenberg_add_registered_webfonts_to_theme_json here, though. As I am understanding it, this is adding any registered webfont to theme.json so that those fonts will be available in the Editor, yes?

If so, I'd like to explore a more sensible approach where that isn't happening all the time. We certainly don't need to be patching all of those font families in for the frontend, where this will pay a performance penalty that we don't need or want. We should find out where the Editor is pulling in fonts and only run it under that condition.

@zaguiini
Copy link
Contributor Author

zaguiini commented Apr 6, 2022

How about we don't run this function when requesting the front-end, @mattwiebe? We could check is_admin and only run that function if it's true.

@mattwiebe
Copy link
Contributor

How about we don't run this function when requesting the front-end, @mattwiebe? We could check is_admin and only run that function if it's true.

That is a fine place to start and probably good enough for this pass. We can always iterate on it down the road if issues crop up.

@zaguiini zaguiini force-pushed the try/enqueue-fonts-used-in-theme-json branch from 12d41f8 to 10e9964 Compare April 7, 2022 00:47
@zaguiini zaguiini requested a review from mattwiebe April 7, 2022 00:51
@zaguiini
Copy link
Contributor Author

zaguiini commented Apr 7, 2022

Did some moving @mattwiebe. Also, we're not running the add registered webfonts to theme.json anymore, unless the admin interface is requested.

@hellofromtonya
Copy link
Contributor

hellofromtonya commented Apr 19, 2022

Test Report

Env:

  • OS: macOS Big Sur
  • Localhost: Core's version of wp-env
  • Browser: Firefox
  • WordPress version: latest trunk
  • Theme: TT2 using this version
  • Plugins:
    • Gutenberg with this branch
    • A custom plugin that adds a normal and italic Source Sans Pro font

Setup:

  • Gutenberg plugin:
    • After pulling this PR, build it.
    • Then either symlink it or copy/paste in wordpress-develop/src/wp-content/plugins/gutenberg/.
    • Then activate it.
  • Custom plugin:

Testing Steps

  1. Just to make sure global styles are in a starting state, go to "Appearance" > "Editor", open the global styles UI, click the 3 vertical dots icon (to the right of "Styles Beta" and then select "Reset to defaults".
  2. Refresh the page.
  3. Open your browser's dev tools. Inspect the <head>. Expected behavior: The font-faces for TT2's webfonts should be in the <head> and no errors/notices/warnings generated on screen, in browser console or in error/debug log.
  4. Change TT2's theme.json key from fontFace to fontFaces by opening wp-content/themes/twentytwentytwo/theme.json and then at line 182 change the key to fontFaces.
  5. Refresh the page. Expected: Same as step 3.
  6. Change the fontFamily in each of the fontFaces to match the parent, i.e. "fontFamily": "\"Source Serif Pro\", serif",.
  7. Refresh the page. Expected: Same as step 3.

Testing Results:

For steps 1-5: ❌

  • TT2's webfonts are not enqueued.
  • TT2's webfont font-faces are not generated / rendered.
  • PHP Notice is thrown
  • headers already sent warning is thrown

Screen Shot 2022-04-19 at 8 39 53 AM

Notice: Function WP_Webfonts::enqueue_webfont was called incorrectly. The "source-serif-pro-serif" font family is not registered. Please see [Debugging in WordPress](https://wordpress.org/support/article/debugging-in-wordpress/) for more information. (This message was added in version 6.0.0.) in /var/www/src/wp-includes/functions.php on line 5831

Notice: Function WP_Webfonts::enqueue_webfont was called incorrectly. The "source-serif-pro-serif" font family is not registered. Please see [Debugging in WordPress](https://wordpress.org/support/article/debugging-in-wordpress/) for more information. (This message was added in version 6.0.0.) in /var/www/src/wp-includes/functions.php on line 5831

Warning: Cannot modify header information - headers already sent by (output started at /var/www/src/wp-includes/functions.php:5831) in /var/www/src/wp-includes/functions.php on line 6878

Warning: Cannot modify header information - headers already sent by (output started at /var/www/src/wp-includes/functions.php:5831) in /var/www/src/wp-admin/includes/misc.php on line 1380

Warning: Cannot modify header information - headers already sent by (output started at /var/www/src/wp-includes/functions.php:5831) in /var/www/src/wp-admin/admin-header.php on line 9

Warning: Cannot modify header information - headers already sent by (output started at /var/www/src/wp-includes/functions.php:5831) in /var/www/src/wp-includes/option.php on line 1111

Warning: Cannot modify header information - headers already sent by (output started at /var/www/src/wp-includes/functions.php:5831) in /var/www/src/wp-includes/option.php on line 1112

For steps 6-7: ❌

  • PHP Notice is thrown
  • headers already sent warning is thrown
  • TT2's webfont font-faces are not generated / rendered.

Screen Shot 2022-04-19 at 8 49 19 AM

Notice: Undefined index: fontFamily in /var/www/src/wp-content/plugins/gutenberg/lib/experimental/webfonts-utils.php on line 73

Notice: Undefined index: fontFamily in /var/www/src/wp-content/plugins/gutenberg/lib/experimental/webfonts-utils.php on line 73

Notice: Undefined index: fontFamily in /var/www/src/wp-content/plugins/gutenberg/lib/experimental/webfonts-utils.php on line 73

Notice: Undefined index: fontFamily in /var/www/src/wp-content/plugins/gutenberg/lib/experimental/webfonts-utils.php on line 73

Warning: Cannot modify header information - headers already sent by (output started at /var/www/src/wp-content/plugins/gutenberg/lib/experimental/webfonts-utils.php:73) in /var/www/src/wp-includes/functions.php on line 6878

Warning: Cannot modify header information - headers already sent by (output started at /var/www/src/wp-content/plugins/gutenberg/lib/experimental/webfonts-utils.php:73) in /var/www/src/wp-admin/includes/misc.php on line 1380

Warning: Cannot modify header information - headers already sent by (output started at /var/www/src/wp-content/plugins/gutenberg/lib/experimental/webfonts-utils.php:73) in /var/www/src/wp-admin/admin-header.php on line 9

Warning: Cannot modify header information - headers already sent by (output started at /var/www/src/wp-content/plugins/gutenberg/lib/experimental/webfonts-utils.php:73) in /var/www/src/wp-includes/option.php on line 1111

Warning: Cannot modify header information - headers already sent by (output started at /var/www/src/wp-content/plugins/gutenberg/lib/experimental/webfonts-utils.php:73) in /var/www/src/wp-includes/option.php on line 1112

Results

TT2's webfonts are not being enqueued and the font-faces not generated. ❌

@hellofromtonya
Copy link
Contributor

Ran the same tests against the current version in trunk (without this PR). The issues noted in the above test report are coming from this PR.

Adds a guard clause to check if the attribute exists in each webfont
before the comparison happens. If no, it bails out, returning false,
as there's nothing to compare.

Some girl scouting:
* Renames the function's properties to be more readable.
* Adds `array` type declaration as only arrays are accepted.
* A wee bit of alignment for consistency in Core.
The font-family in each `fontFaces` may be different than its parent.
For example, the parent may have a fallback defined such as
"fontFamily": "\"Source Serif Pro\", serif",`.
@hellofromtonya
Copy link
Contributor

hellofromtonya commented Apr 19, 2022

The errors noted in my test report are fixed by the last 2 commits ✅

bd4259e uses each font-face's font-family as that's the key used during registration and it may be different than the parent font-family.

dc14844 resolves the notice for missing index:

Notice: Undefined index: fontFamily in /var/www/src/wp-content/plugins/gutenberg/lib/experimental/webfonts-utils.php on line 73

1. Added @SInCE annotations.
2. Removed empty line between last @param and @return in DocBlock.
3. Renamed variables that were using PHP reserved key words, such as `$array`.
   Required to avoid PHP 8.x deprecation notices and PHP 9 fatal errors.
4. Used `empty()` instead of `count()` for readability and consistency.
5. Moved `_wp_array_keys_to_kebab_case()` to group the array key transformation functions.
6. A wee bit of girl scouting in the DocBlocks for readability and consistency.
@aristath
Copy link
Member

aristath commented Apr 20, 2022

@hellofromtonya I went ahead and did some cleanup/refactor in the utility functions. I don't think we should be introducing new functions like that - unless absolutely necessary.

  • _gutenberg_is_webfont_equal was removed. It was only used in a single place, so it was merged in the functionality of the _gutenberg_find_webfont function. (260156a)
  • I moved the _gutenberg_find_webfont function inside the WP_Webfonts class and renamed it to find_webfonts. (b2eb078)
  • _wp_resolve_font_face_uri was removed. It was only used once inside the _wp_register_webfonts_from_theme_json function, so I moved the code there. (0de1117)
  • I removed the _wp_array_keys_to_camel_case function. It was only used inside _wp_add_registered_webfonts_to_theme_json, so that's where I moved it. (e2fe01c)
  • Removed the _gutenberg_is_externally_registered_webfont function. It was a single line, so no reason to abstract a simple condition like that which is only used internally in the API. (e94fffa)

@aristath aristath force-pushed the try/enqueue-fonts-used-in-theme-json branch from bb569ae to e2fe01c Compare April 20, 2022 10:23
@priethor
Copy link
Contributor

Let's remove this PR from the 6.0 Project Board as the alternative #40472 was implemented for WP 6.0

@annezazu annezazu added [Feature] Typography Font and typography-related issues and PRs and removed [Feature] Fonts API labels Sep 25, 2023
@github-actions
Copy link

Warning: Type of PR label error

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Type-related labels to choose from: [Type] Accessibility (a11y), [Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Performance, [Type] Project Management, [Type] Regression, [Type] Security, [Type] WP Core Ticket, Backport from WordPress Core.
  • Labels found: [Feature] Typography.

Read more about Type labels in Gutenberg.

1 similar comment
@github-actions
Copy link

Warning: Type of PR label error

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Type-related labels to choose from: [Type] Accessibility (a11y), [Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Performance, [Type] Project Management, [Type] Regression, [Type] Security, [Type] WP Core Ticket, Backport from WordPress Core.
  • Labels found: [Feature] Typography.

Read more about Type labels in Gutenberg.

@hellofromtonya
Copy link
Contributor

The Fonts API is deprecated, replaced by Font Face to work with the Font Library. Enqueuing is no longer needed. Thus this PR can be closed without merging.

@zaguiini zaguiini deleted the try/enqueue-fonts-used-in-theme-json branch October 20, 2023 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Typography Font and typography-related issues and PRs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants