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

Font Face: to generate and print font-face styles for theme.json fonts #51770

Merged
merged 38 commits into from
Jul 10, 2023

Conversation

hellofromtonya
Copy link
Contributor

@hellofromtonya hellofromtonya commented Jun 21, 2023

What?

Introduce Font Face processing to generate and print @font-face styles for fonts in theme.json merged data.

It's minimalistic in what it publicly exposes to avoid BC concerns.

There's no register, deregister, or enqueue of fonts or providers.

The fonts to be processed come directly from theme.json merged data which contains the theme's defined fonts and user "activated" fonts (from the Font Library).

Why?

See #51769.

How?

It adds 2 new classes WP_Font_Face and WP_Font_Face_Resolver which currently live in /lib/experimental/fonts/ folder. That folder could also house the new Fonts Library.

Jobs:

  • The Resolver's job is to handle getting and formatting the font data from theme.json.
  • The WP_Font_Face's job is to generate and print the @font-face styles.
  • The local provider functionality is integrated into WP_Font_Face.

If in the future support for custom providers is needed, the local provider can be removed from theWP_Font_Face and reintroduced as a separate class without breaking BC (backwards-compatibility).

When it's time to print, wp_print_font_faces() handles a call to the WP_Font_Face_Resolver::get_fonts_from_theme_json() and passes the fonts to WP_Font_Face::print().

This also means other sources could pass fonts to WP_Font_Face::print(), such as plugins on a classic site.

Prepared to be merged before Fonts Library

The new files will not be loaded into memory unless the Fonts Library exists. In this way, this PR can be merged into trunk before the Fonts Library without breaking websites that are using the Fonts API.

TODO

  • Add WP_Font_Face_Resolver.
  • Add WP_Font_Face.
  • Integrate local provider into WP_Font_Face.
  • Only expose print() to hide the internal workings (eliminate future BC concerns).
  • Conditional load only when the Fonts Library exists.
  • Add PHPUnit tests.

Testing Instructions

  1. Enable the loading of this PR's files by:
    a. Open lib/load.php.
    b. Scroll down to where the Font Face files are loaded.
    c. Add true || in the if:
if ( true || class_exists( 'WP_Fonts_Library' ) || class_exists( 'WP_Fonts_Library_Controller' ) ) {
  1. With TT3:
    a. In Site Editor using Dev Tools, check that the <script id="wp-fonts-local"> and @font-face styles are present in <head> and the iframed editor.
    b. In a post or page, check that the <script id="wp-fonts-local"> and @font-face styles are present in <head> and the iframed editor.
    c. On the front-end, check that the <script id="wp-fonts-local"> and @font-face styles are present.
<style id="wp-fonts-local">
@font-face{font-family:"DM Sans";font-style:normal;font-weight:400;font-display:fallback;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/dm-sans/DMSans-Regular.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:"DM Sans";font-style:italic;font-weight:400;font-display:fallback;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/dm-sans/DMSans-Regular-Italic.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:"DM Sans";font-style:normal;font-weight:700;font-display:fallback;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/dm-sans/DMSans-Bold.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:"DM Sans";font-style:italic;font-weight:700;font-display:fallback;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/dm-sans/DMSans-Bold-Italic.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:"IBM Plex Mono";font-style:normal;font-weight:300;font-display:block;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/ibm-plex-mono/IBMPlexMono-Light.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:"IBM Plex Mono";font-style:normal;font-weight:400;font-display:block;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/ibm-plex-mono/IBMPlexMono-Regular.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:"IBM Plex Mono";font-style:italic;font-weight:400;font-display:block;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/ibm-plex-mono/IBMPlexMono-Italic.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:"IBM Plex Mono";font-style:normal;font-weight:700;font-display:block;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/ibm-plex-mono/IBMPlexMono-Bold.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:Inter;font-style:normal;font-weight:200 900;font-display:fallback;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/inter/Inter-VariableFont_slnt,wght.ttf') format('truetype');font-stretch:normal;}
@font-face{font-family:"Source Serif Pro";font-style:normal;font-weight:200 900;font-display:fallback;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:"Source Serif Pro";font-style:italic;font-weight:200 900;font-display:fallback;src:url('http://localhost:8889/wp-content/themes/twentytwentythree/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2') format('woff2');font-stretch:normal;}

</style>
  1. With TT1:
    • In the functions.php, add the following code:
add_action( 'wp_head', 'tt1_test_print_font_faces', 50 );
add_action( 'admin_print_styles', 'tt1_test_print_font_faces', 50 );
function tt1_test_print_font_faces() {
	$fonts = array(
			'Source Serif Pro' => array(
					array(
							'font-family' => 'Source Serif Pro',
							'font-style'   => 'normal',
							'font-weight'  => '200 900',
							'src'          => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ),
					),
					array(
							'font-family' => 'Source Serif Pro',
							'font-style'   => 'italic',
							'font-weight'  => '200 900',
							'src'          => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ),
					),
			),
	);
	wp_print_font_faces( $fonts );
}
  • Using Dev Tools, check in the front-end and admin area that the <script id="wp-fonts-local"> and @font-face styles are present.
<style id="wp-fonts-local">
@font-face{font-family:"Source Serif Pro";font-style:normal;font-weight:200 900;font-display:fallback;src:url('http://localhost:8889/wp-content/themes/twentytwentyone/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2') format('woff2');}
@font-face{font-family:"Source Serif Pro";font-style:italic;font-weight:200 900;font-display:fallback;src:url('http://localhost:8889/wp-content/themes/twentytwentyone/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2') format('woff2');}

</style>

@hellofromtonya hellofromtonya added [Feature] Fonts API [Feature] Typography Font and typography-related issues and PRs labels Jun 21, 2023
@WordPress WordPress deleted a comment from github-actions bot Jun 22, 2023
@hellofromtonya
Copy link
Contributor Author

This PR is ready to be manually tested. I've added test instructions for TT3 (block theme) and TT1 (classic theme). Please test and add test reports.

@anton-vlasenko
Copy link
Contributor

Test Report

Env:

  • WordPress: 6.3-alpha-55505-src
  • PHP: 8.0.27
  • Server: Apache/2.4.55 (Unix) PHP/8.0.27
  • Database: mysqli (Server: 5.7.38 / Client: mysqlnd 8.0.27)
  • Browser: Safari 16.5 (macOS)
  • Theme: Twenty Twenty-One 1.8
  • MU-Plugins: None activated
  • Plugins:
    • Gutenberg 16.0.0

Test Instructions

  1. Follow the testing instructions listed in the PR's description.

  2. Enable TT3, perform the requested code changes and make sure that the style#wp-fonts-local element is present and contains all the needed elements, as described in the Testing Instructions section (both on frontend and backend).

  3. Enable TT1, perform the requested code changes and make sure that the style#wp-fonts-local element is present and contains all the needed elements, as described in the Testing Instructions section (both on frontend and backend).

Results

  • Yes, the style#id-fonts-local is present in head and the iframed editor (backend) and on the frontend and backend when using the TT3 theme. ✅
  • Yes, the style#id-fonts-local is present on the frontend and backend when using the TT1 theme. ✅

@github-actions
Copy link

github-actions bot commented Jun 26, 2023

Flaky tests detected in 2b61cf2.
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/5462793951
📝 Reported issues:

@aristath
Copy link
Member

Pushed a codings-standards fix 👍

@ndiego
Copy link
Member

ndiego commented Jun 29, 2023

Hi team; where are we on this one? Fonts loaded by theme.json are currently not working in the Editor in WordPress 6.3 Beta 2 when metaboxes exist. I strongly believe this will need to get fixed in 6.3. See #51209

@anton-vlasenko
Copy link
Contributor

anton-vlasenko commented Jun 29, 2023

Hi team; where are we on this one? Fonts loaded by theme.json are currently not working in the Editor in WordPress 6.3 Beta 2 when metaboxes exist. I strongly believe this will need to get fixed in 6.3. See #51209

@ndiego I think it's almost ready. I just need to fix some unit tests.
I need an hour or so.

UPD: This PR is ready to be reviewed.

@ndiego
Copy link
Member

ndiego commented Jul 3, 2023

Just tested to see if this fixes the issue where fonts registed in theme.json do not load when metaboxes are present in Gutenberg 16.1 or WordPress 6.3 Beta 2 (i.e. the Editor is iframed). Unfortunately, I am still seeing the issue.

In WordPress 6.2.2, without Gutenberg active, if you enable the Yoast plugin, the fonts in TT3 will load as expected. Yoast includes a metabox and the post Editor in 6.2.2 is not iframed.

Now if you update to 6.3 Beta 2, the fonts are not loaded when a metabox is present (the Editor is no longer iframed).

Reverting back to 6.2.2, if you activate Gutenberg with this PR, unfortunately, the fonts are still not loaded when the metabox is present.

WordPress 6.2.2 (fonts loaded) 6.3 Beta 2 or 6.2.2 + this PR (fonts not loaded)
image image

@hellofromtonya
Copy link
Contributor Author

Rebased on top of trunk to pull in the latest changes ✅

1. Adds a test case to the fonts directory.

2. Adds a dataset trait for sharing datasets amongst different test classes.
This change the maintance burden of redundant datasets by centralizing in a shared trait.

3.Removes the style variation tests as these are no longer relevant to the Font Face.

4. A wee bit of renaming and reformatting.
1. Adds test for when no fonts are passed into the function, i.e. meaning it will pull from the resolver.

2. Improves resolver test to validate the full returned array structure.

3. Moves the expected fonts to the dataset trait to share it between these 2 tests.

4. A wee bit of girlscouting.
1. Adds guard clause to WP_Font_Face::generate_and_print().
2. Adds unhappy test for it.
3. Adds similar unhappy test for wp_print_font_faces().
@hellofromtonya
Copy link
Contributor Author

@anton-vlasenko I reviewed the tests. You did a great job! Thank you :) To help, I made the changes from the review in the 3 added commits with commit messages explaining the changes. What do you think?

Copy link
Contributor

@anton-vlasenko anton-vlasenko left a comment

Choose a reason for hiding this comment

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

I have tested this PR again, and my test report is still valid considering the recent fixes.

Additionally, I tested different combinations, such as meta boxes enabled/disabled, block/classical theme, and I couldn't find any issues (to the best of my knowledge).

The code also looks good to me.
Therefore, I approve this PR.

&&
function_exists( 'current_theme_supports' ) && ! current_theme_supports( 'html5', 'style' )
) {
$this->style_tag_attrs = array( 'type' => 'text/css' );
Copy link
Contributor

Choose a reason for hiding this comment

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

The type attribute is deprecated, as stated in https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#deprecated_attributes.
However, I don't think this could create any issues (and the type attribute can be removed later if absolutely necessary).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Core still supports pre HTML 5 syntax. This particular code comes from WP Styles. Once Core drops < HTML 5, then this can also be removed.

@hellofromtonya hellofromtonya merged commit 92dd32e into trunk Jul 10, 2023
@hellofromtonya hellofromtonya deleted the try/font-face branch July 10, 2023 17:31
@github-actions github-actions bot added this to the Gutenberg 16.3 milestone Jul 10, 2023
@hellofromtonya
Copy link
Contributor Author

Thank you everyone for your contributions :) I've merged this PR into trunk.

The new files will not be loaded into memory unless the Fonts Library exists. In this way, this PR can be merged into trunk before the Fonts Library without breaking websites that are using the Fonts API.

Remember, Font Face only loads into memory (replacing the Fonts API) when the Fonts Library is also loaded into memory. That's by design to keep sites running (using Fonts API) while providing the new typography path forward for testing with the Fonts Library.

@mburridge mburridge added the [Type] Enhancement A suggestion for improvement. label Jul 18, 2023
@mikachan mikachan added the Needs PHP backport Needs PHP backport to Core label Sep 5, 2023
@hellofromtonya hellofromtonya removed the Needs PHP backport Needs PHP backport to Core label Sep 6, 2023
@hellofromtonya
Copy link
Contributor Author

Removed the Needs PHP backport label as Font Face was merged into Core via changeset https://core.trac.wordpress.org/changeset/56500


// Converts the "file:./" src placeholder into a theme font file URI.
if ( ! empty( $font_face['src'] ) ) {
$font_face['src'] = static::to_theme_file_uri( (array) $font_face['src'] );
Copy link
Member

Choose a reason for hiding this comment

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

I know this is in Core already, but it relates to a feature being built for 6.6: relative paths to background images in theme.json styles

For consistency, and after a nudge from @creativecoder, I decided to follow the file:./ dotslash path convention to represent paths to these images.

@noisysocks then pointed out that dotslash indicates the current directory, and might imply other dot-style paths, e.g., ../

So, if web fonts can be used in theme variations, e.g., my-theme/styles/variation-with-fonts.json, the ./ would indicate a path relative to the styles directory.

It might also imply that paths with ../../some/path.file could also be resolved. But I'm not sure if get_theme_file_uri() supports such things as it looks for files relative to the active or parent theme root. Maybe I'm missing something.

https://github.com/WordPress/gutenberg/blob/trunk/lib/compat/wordpress-6.4/fonts/font-face/class-wp-font-face-resolver.php#L147C27-L147C44

I was wondering if we should enforce a convention. Either:

  1. Enforce that all paths must be relative to the theme root, or
  2. Allow relative to current directory, and then in resolve them in the background with PHP trickery with realpath or something.

What do folks think?

Copy link
Contributor

Choose a reason for hiding this comment

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

I see what you mean... the syntax and behavior seem mismatched: file:./ seems to imply a reference to the current directory, but really it's a placeholder for the theme root looking at how it's coded.

Updating file:./ to apply to the current directory (e.g. theme-root/styles) for theme variations and/or adding resolution of ../ paths would make the syntax clearer. But I worry about backward compatibility (I imagine that some theme variations are already using file:./?). And some care is needed to prevent directory traversal outside the theme directory.

So I favor keeping things simple, and continuing to use file:./ simply as a placeholder to the theme directory root. But it's not a strong opinion.

(Note that either way, we should add to and update the theme handbook docs to make it clearer).

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the quick reply @creativecoder

So I favor keeping things simple, and continuing to use file:./ simply as a placeholder to the theme directory root.

👍🏻

I went with this approach for now given that folks might be using to it already.

Updating file:./ to apply to the current directory (e.g. theme-root/styles) for theme variations and/or adding resolution of ../ paths would make the syntax clearer. But I worry about backward compatibility (I imagine that some theme variations are already using file:./?).

True. Backwards compatibility is now a requirement regardless. And I considered this in #61271 and contemplated supporting file:./ (with the ./ for folks who are now used to it) and also a file: prefix.

But I agree that it's simpler to go with what's there until folks come up with a viable alternative. Even if it's "Enforce that all paths must be relative to the theme root" - that way, we could update the docs now and make the ./ optional later without adding any complex ../ support etc. 🤷🏻

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 [Type] Enhancement A suggestion for improvement.
Projects
None yet
8 participants