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 API implementation in theme.json #35591

Closed
aristath opened this issue Oct 13, 2021 · 21 comments · Fixed by #35625
Closed

Webfonts API implementation in theme.json #35591

aristath opened this issue Oct 13, 2021 · 21 comments · Fixed by #35625
Labels
Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json Needs Decision Needs a decision to be actionable or relevant

Comments

@aristath
Copy link
Member

aristath commented Oct 13, 2021

In WP5.9 a new webfonts API will be included (see WordPress/wordpress-develop#1736).
The twentytwentytwo theme is a block-based theme, and it loads custom webfonts. There is a draft PR in the TT2 theme to implement the webfonts API (see WordPress/twentytwentytwo#95) but ideally webfonts would be defined in the theme.json file.

In the current form of the API, webfonts can be defined in PHP using code like this (example is from the TT2 theme PR):

wp_register_webfonts(
 	array(
 		array(
 			'fontFamily'  => 'Source Serif Pro',
 			'fontWeight'  => '200 900',
 			'fontStyle'   => 'normal',
 			'fontStretch' => 'normal',
 			'src'         => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ),
 			'provider'    => 'local',
 		),
 		array(
 			'fontFamily'  => 'Source Serif Pro',
 			'fontWeight'  => '200 900',
 			'fontStyle'   => 'italic',
 			'fontStretch' => 'normal',
 			'src'         => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ),
 			'provider'    => 'local',
 		),
 	)
 );

That structure is easy to use in a theme.jsonfile - which is why we went with this schema.

So in theme.json we should be able to add a "webfonts" item and then parse the items and call the Webfonts-API functions to register the webfonts:

{
	"settings": {
		"typography": {
			"webfonts": [
				{
					"fontFamily": "Source Serif Pro",
					"fontWeight": "200 900",
					"fontStyle": "normal",
					"fontStretch": "normal",
					"src": "file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2",
					"provider": "local"
				},
				{
					"fontFamily": "Source Serif Pro",
					"fontWeight": "200 900",
					"fontStyle": "italic",
					"fontStretch": "normal",
					"src": "file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2",
					"provider": "local"
				}
			]
		}
	}
}

The webfonts API takes care of generating the @font-face styles and adds them to the frontend as well as the editor (tested with the TT2 theme using the PR mentioned above).

The only missing piece here is to decide if webfonts in theme.json should be a root-level item or maybe part of settings? 🤷 . Then once we decide that, the JSON parser should be tweaked to allow registering the fonts.
EDIT: It was decided to put these inside settings.typography. The code above was updated accordingly.

As an additional benefit, maybe when parsing the webfonts in theme.json we could add then registered fonts in typography.fontFamilies automatically without requiring users to manually add them.

cc @hellofromtonya @oandregal @youknowriad @jorgefilipecosta @scruffian @ntsekouras @mtias


Timeframe: Ideally this should be included in WP5.9 in order to be properly implemented & tested in the TT2 theme.

@aristath aristath added the Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json label Oct 13, 2021
@mtias mtias added the Needs Decision Needs a decision to be actionable or relevant label Oct 13, 2021
@oandregal
Copy link
Member

As an additional benefit, maybe when parsing the webfonts in theme.json we could add then registered fonts in typography.fontFamilies automatically without requiring users to manually add them.

Can we use settings.typography.fontFamilies to register all the fonts?

I've also provided some thoughts on the use of file:./ at https://github.com/WordPress/wordpress-develop/pull/1736/files#r728265713

I'll leave Jorge and others to provide any more details or thought, as they've looked into this in the past.

@mtias
Copy link
Member

mtias commented Oct 13, 2021

+1 for using something under settings.typography and keeping it all together

@skorasaurus
Copy link
Member

heavily related/previously: #34752

@aristath
Copy link
Member Author

aristath commented Oct 14, 2021

Can we use settings.typography.fontFamilies to register all the fonts?

Hmmm using settings.typography makes perfect sense. But we need to take some things into consideration and I'm not sure settings.typography.fontFamilies would be a good fit 🤔

I'll use what we currently have in the TT2 theme as an example (settings.typography.fontFamilies)

{
	"fontFamily": "\"Source Serif Pro\", serif",
	"name": "Source Serif Pro",
	"slug": "source-serif-pro",
}
  • The fontFamily there is the CSS value of font-family, which contains not only the main font but also fallbacks for the font. So it's not the same fontFamily as the one used in @font-face
  • There are multiple files and variations for each font (normal, italic, different weights etc). So each item inside settings.typography.fontFamilies can be tied to multiple @font-face items.
  • The defined webfonts must create @font-face styles, so maybe they need to be somewhat separated from the settings.typography.fontFamilies items.

One option would be to nest the webfonts for each font-family in a fontFace item:

{
	"fontFamily": "\"Source Serif Pro\", serif",
	"name": "Source Serif Pro",
	"slug": "source-serif-pro",
	"fontFace": [
		{
			"fontFamily": "Source Serif Pro",
			"fontWeight": "200 900",
			"fontStyle": "normal",
			"fontStretch": "normal",
			"src": "file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2",
			"provider": "local"
		},
		{
			"fontFamily": "Source Serif Pro",
			"fontWeight": "200 900",
			"fontStyle": "italic",
			"fontStretch": "normal",
			"src": "file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2",
			"provider": "local"
		}
	]
}

The other option would be to just add a new settings.typography.webfonts under settings.typography. That would probably be a lot cleaner and would provide the separation we need

@overclokk
Copy link

The other option would be to just add a new settings.typography.webfonts under settings.typography. That would probably be a lot cleaner and would provide the separation we need

I like this simpler version but I would have used settings.typography.fontFace instead, because fontFamily is a CSS property and fontFace is it too.

I would add also font-display as property and make src an array if you have multiple font types.

@aristath
Copy link
Member Author

I would add also font-display as property and make src an array if you have multiple font types.

The code examples above are just examples and dont' include everything that's possible.
All properties that are valid for @font-face can be entered and they all work properly. When implementing the API we accounted for all descriptors listed in https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face#descriptors

When a webfont requires multiple font-files, you can use an array (that's already implemented in the API). In the case of TT2 we only have a .woff2 file, so there is no need for an array.

@aristath
Copy link
Member Author

I would have used settings.typography.fontFace instead, because fontFamily is a CSS property and fontFace is it too.

It's not the same... settings.typography.webfonts will be on the same level as settings.typography.fontFamilies, settings.typography.fontSizes, settings.typography.customLineHeight etc. None of those things is a CSS property. 🙂

@aristath
Copy link
Member Author

As a proof of concept, I created a draft PR on #35625. The PR adds a simple function to register the webfonts if they are defined in settings.typography.webfonts.
It's probably not the best way to do it, but it can help with testing and experimentation to get the ball rolling 👍

@fabiankaegy
Copy link
Member

@aristath I see this is currently not tracked on the Gutenberg 11.9 Project and therefore isn't targeted for 5.9. Is that correct?

Is there any plan to have this feature in Core in 5.9 with the main Webfont API?

@aristath
Copy link
Member Author

aristath commented Nov 4, 2021

@aristath I see this is currently not tracked on the Gutenberg 11.9 Project and therefore isn't targeted for 5.9. Is that correct?

Is there any plan to have this feature in Core in 5.9 with the main Webfont API?

Yes, we're planning to merge this in 5.9 in core.
The reason it's not currently tracked in Gutenberg is because the implementation hasn't (yet) been committed to core and therefore this can't be properly tested or evaluated without using the patch from WordPress/wordpress-develop#1736 in WP's trunk

@fabiankaegy
Copy link
Member

Ahh okay makes sense. Thanks for the clarification:) so it will actually ship in core before the plug-in 🤔

@aristath
Copy link
Member Author

aristath commented Nov 4, 2021

Well, it needs to... The plugin mods can't function unless the core code is there. So the order of committing needs to be core - Gutenberg - tt2

@aristath
Copy link
Member Author

aristath commented Nov 4, 2021

Of course we could merge this in Gutenberg, as it will only function when the core code gets merged... If we're pressed for time because of the feature-freeze deadline, we can merge the POC which is functional and safe, and then improve it.

@aristath aristath linked a pull request Nov 4, 2021 that will close this issue
7 tasks
@aristath
Copy link
Member Author

aristath commented Nov 4, 2021

I opened the PR in #35625 and rebased it 👍

@jorgefilipecosta
Copy link
Member

I like the option of using {fontFamily, name, slug, fontFace }under families. I think we should join font faces with font families. The fontFamilies key allows one to specify using theme.json, which font families are available on the UI. Why would I be defining a webfont on theme.json but then not make it available on the UI? Joining the two concepts together seems like a good idea. When a fontFace is provided fontFamily should be optional e.g: we are specifying how to load the font, so the font should be available and the fallback is not something important if fontFamily is not passed we use the fontFamily inside fontFace.

@aristath
Copy link
Member Author

Since the API was not included in WP5.9, this is no longer relevant so I'll go ahead and close it.

A draft PR is in the works to port the API to Gutenberg as per the comment on https://core.trac.wordpress.org/ticket/46370#comment:111.

Draft PR: #36394 👍

@poof86
Copy link

poof86 commented Apr 27, 2022

I would add also font-display as property and make src an array if you have multiple font types.

The code examples above are just examples and dont' include everything that's possible. All properties that are valid for @font-face can be entered and they all work properly. When implementing the API we accounted for all descriptors listed in https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face#descriptors

When a webfont requires multiple font-files, you can use an array (that's already implemented in the API). In the case of TT2 we only have a .woff2 file, so there is no need for an array.

This doesn't seem to work though. I had to download a variable font to get weights working

@badg0003
Copy link

badg0003 commented May 4, 2022

Just to echo on what @poof86 was saying, I'm seeing that when defining fonts within theme.json that it recognizes variable fonts only. For example, in attempting to setup various weights for the non-variable font Roboto (see snippet below), it ignores every entry after the initial one:

"fontFamilies": [
    {
      "name": "Roboto",
      "slug": "roboto",
      "fontFamily": "\"Roboto\", sans-serif",
      "fontFace": [
        {
          "fontFamily": "Roboto",
          "fontWeight": "400",
          "fontStyle": "normal",
          "src": ["file:./assets/fonts/roboto-v29-latin-regular.woff2"]
        },
        {
          "fontFamily": "Roboto",
          "fontWeight": "700",
          "fontStyle": "normal",
          "src": ["file:./assets/fonts/roboto-v29-latin-700.woff2"]
        }
      ]
    },
    {
      "name": "Oswald",
      "slug": "oswald",
      "fontFamily": "\"Oswald\", sans-serif",
      "fontFace": [
        {
          "fontFamily": "Oswald",
          "fontWeight": "200 300 400 500 600 700",
          "fontStyle": "normal",
          "src": ["file:./assets/fonts/Oswald-VariableFont_wght.woff2"]
        }
      ]
    }
],

So in the above example only Roboto at 400 weight works, while the 700 variation is ignored completely.

@simonwammerfors
Copy link

I have the same problem loading multiple weights/styles of the same non-variable font. Only the first is getting loaded. Using the snippet below, merriweather-regular-webfont.woff2 and poppins-bold-webfont.woff2 is loaded.

"fontFamilies": [
                {
                    "fontFamily": "\"Merriweather\", serif",
                    "slug": "merriweather",
                    "name": "Merriweather",
                    "fontFace": [
                        {
                            "fontFamily": "Merriweather",
                            "fontWeight": "normal",
                            "fontStyle": "normal",
                            "src": [ "file:./fonts/merriweather-regular-webfont.woff2" ]
                        },
                        {
                            "fontFamily": "Merriweather",
                            "fontWeight": "normal",
                            "fontStyle": "italic",
                            "src": [ "file:./fonts/merriweather-italic-webfont.woff2" ]
                        },
                        {
                            "fontFamily": "Merriweather",
                            "fontWeight": "bold",
                            "fontStyle": "normal",
                            "src": [ "file:./fonts/merriweather-bold-webfont.woff2" ]
                        }
                    ]
                },
		{
                    "fontFamily": "\"Poppins\", sans-serif",
                    "slug": "poppins",
                    "name": "Poppins",
                    "fontFace": [
                        {
                            "fontFamily": "Poppins",
                            "fontWeight": "bold",
                            "fontStyle": "normal",
                            "src": [ "file:./fonts/poppins-bold-webfont.woff2" ]
                        }
                    ]
                }
            ]```

@MichaelStaples
Copy link

Has a solution to the issue with multiple weight of the same font in the theme.json file been developed yet??

@strarsis
Copy link
Contributor

strarsis commented Mar 6, 2023

+1, same issue, but with different unicode-ranges (e.g. Latin, Latin-Extended and so on, what Google Font API extensively uses and what is best practices) which is also breaking.
Specifying multiple font files for a font face (which is expected by the spec) with differing unicode-range results in only the first font file being used, all other font files being discarded.

Related issue: #48813

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json Needs Decision Needs a decision to be actionable or relevant
Projects
None yet
Development

Successfully merging a pull request may close this issue.