Make WordPress Core

Opened 2 years ago

Last modified 5 months ago

#59911 reopened defect (bug)

WordPress 6.4 changes the font-face generation using wrong fontFamily attribute from theme.json preset

Reported by: alexandrebuffet's profile alexandrebuffet Owned by:
Milestone: Future Release Priority: normal
Severity: normal Version: 6.4
Component: Themes Keywords: has-patch needs-unit-tests
Focuses: Cc:

Description

For various reasons, I used to declare font families in theme.json as follows:

"fontFamilies": [
    {
        "fontFamily": "var(--font-primary)",
        "name": "Primary (Halcom)",
        "slug": "primary",
        "fontFace": [
            {
                "fontFamily": "Halcom Variable",
                "fontStretch": "normal",
                "fontStyle": "normal",
                "fontWeight": "500 700",
                "src": ["file:./assets/fonts/halcom/Halcom-VariableFont_slnt,wght.woff2"]
            }
        ]
    },
    {
        "fontFamily": "var(--font-secondary)",
        "name": "Secondary (Oskar)",
        "slug": "secondary",
        "fontFace": [
            {
                "fontFamily": "Oskar",
                "fontStretch": "normal",
                "fontStyle": "normal",
                "fontWeight": "700",
                "src": ["file:./assets/fonts/oskar/Oskar-One-Bold.woff2"]
            }
        ]
    }
],

Before WordPress 6.4, the result of the generated font-face was as follows:

@font-face{font-family:"Halcom Variable";font-style:normal;font-weight:500 700;font-display:fallback;src:url('https://test.com/app/themes/my-theme/assets/fonts/halcom/Halcom-VariableFont_slnt,wght.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:Oskar;font-style:normal;font-weight:700;font-display:fallback;src:url('https://test.com/app/themes/my-theme/assets/fonts/oskar/Oskar-One-Bold.woff2') format('woff2');font-stretch:normal;}

Since WordPress 6.4, the generated font-face is as follows:

@font-face{font-family:var(--font-primary);font-style:normal;font-weight:500 700;font-display:fallback;src:url('http://test.local/app/themes/my-theme/assets/fonts/halcom/Halcom-VariableFont_slnt,wght.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:var(--font-secondary);font-style:normal;font-weight:700;font-display:fallback;src:url('http://test.local/app/themes/my-theme/assets/fonts/oskar/Oskar-One-Bold.woff2') format('woff2');font-stretch:normal;}

It seems that the wrong "fontFamily" attribute, the one of the preset, is now used as font-family property value inside font-face declaration (in the new wp-includes/fonts/class-wp-font-face.php). Is there a specific reason for this?

As for all other font size or spacing preset values, it should be possible to reference another CSS variable in font families too. It may be necessary to inject variables from a third-party framework into the theme.json, as in my first example. IMO I think that the value of the preset and its "fontFamily" attribute should be able to be different from the "fontFamily" value to be used in the font-face, as was the case before 6.4.

Change History (16)

#1 @dogwonder
2 years ago

Same here, I've noted the same with multiple weights and font src

https://core.trac.wordpress.org/ticket/59876

#2 follow-up: @ironprogrammer
2 years ago

  • Keywords reporter-feedback added

Welcome back to Trac, @alexandrebuffet, and thanks for the report!

6.4 shipped an update where the @font-face { font-family:... value is derived (see GB PR 54615). Now the font-family is set to the first font found in the typography.fontFamilies.fontFamily prop.

This change addressed the intended behavior when registering font families -- from the docs:

fontFamily: (Required) A valid value that will map to the CSS font-family value. Generally, this will be a font stack (a list of families that the browser will try to use in order).

Hence, the resulting font-family:var(--font-primary) you're seeing.

IMO I think that the value of the preset and its "fontFamily" attribute should be able to be different from the "fontFamily" value to be used in the font-face, as was the case before 6.4.

Do you have an example where/how this would be used, i.e. having a font variation that doesn't belong to the same family?

#3 in reply to: ↑ 2 @alexandrebuffet
2 years ago

Thanks for the reply @ironprogrammer !

I understand the problem initially raised in the GB PR 54615. But I don't understand the point of separating the values of the preset's "fontFamily" value for use in the @font-face when the "fontFamily" entry in the "fontFace" is specifically designed for this.

I think that matiasbenedetto proposal is much more consistent with the code that needs to be generated and, above all, makes it possible to differentiate between the preset value and the font-face value.

In addition to having more consistency, it would also technically reduce the need to split the values to arrive at the same result for the generation of the @font-face as he says in a comment further on in the discussion.

Returning to my example above, the value of the "fontFamily" entry in a preset should never correspond to the value in the @font-face, since this is the value of my variable to be applied in CSS and not necessarily the name of my font to be loaded.

Here's a more detailed example of a declaration in my theme.json. For the example, I don't split the font family value to get the first value to better understand the inconsistency.

"fontFamilies": [
    {
        "fontFamily": "var(--font-primary)", // Generates the preset token: --wp--preset--font-family--primary: var(--font-primary).
        "name": "Primary (Haffer)",
        "slug": "primary",
        "fontFace": [
            {
                "fontFamily": "Haffer", // Should generate: @font-face: { font-family:"Haffer"; ... } not @font-face: { font-family:var(--font-primary); ... }
                "fontStretch": "normal",
                "fontStyle": "normal",
                "fontWeight": "400 700",
                "src": ["file:./assets/fonts/haffer/Haffer-Upright-VariableFont.woff2"]
            }
        ]
    },
    {
        "fontFamily": "var(--font-secondary)", // Generates the preset token: --wp--preset--font-family--secondary: var(--font-secondary).
        "name": "Secondary (Grenette Pro)",
        "slug": "secondary",
        "fontFace": [
            {
                "fontFamily": "Grenette Pro", // Should generate: @font-face: { font-family:"Grenette Pro"; ... } not @font-face: { font-family:var(--font-secondary); ... }
                "fontStretch": "normal",
                "fontStyle": "italic",
                "fontWeight": "400",
                "src": ["file:./assets/fonts/grenette-pro/Grenette-Pro-Italic.woff2"]
            }
        ]
    },
    {
        "fontFamily": "system-ui, sans-serif", // Will generate : --wp--preset--font-family--system: system-ui, sans-serif.
        "name": "System",
        "slug": "system"
        // No @font-face to generate.
    }
],

But in any case, the problem is the same if I didn't use a CSS variable as a value.

"fontFamilies": [
    {
        "fontFamily": "Haffer, Inter, system-ui, sans-serif", // Generates the preset token: --wp--preset--font-family--primary: Haffer, Inter, system-ui, sans-serif.
        "name": "Primary (Haffer)",
        "slug": "primary",
        "fontFace": [
            {
                "fontFamily": "Haffer", // Should generate: @font-face: { font-family:"Haffer"; ... } not @font-face: { font-family:"Haffer, Inter, system-ui, sans-serif"; ... }
                "fontStretch": "normal",
                "fontStyle": "normal",
                "fontWeight": "400 700",
                "src": ["file:./assets/fonts/haffer/Haffer-Upright-VariableFont.woff2"]
            }
        ]
    },
    {
        "fontFamily": "Grenette Pro, serif", // Generates the preset token: --wp--preset--font-family--secondary: Grenette Pro, serif.
        "name": "Secondary (Grenette Pro)",
        "slug": "secondary",
        "fontFace": [
            {
                "fontFamily": "Grenette Pro", // Should generate: @font-face: { font-family:"Grenette Pro"; ... } not @font-face: { font-family:"Grenette Pro, serif"; ... }
                "fontStretch": "normal",
                "fontStyle": "italic",
                "fontWeight": "400",
                "src": ["file:./assets/fonts/grenette-pro/Grenette-Pro-Italic.woff2"]
            }
        ]
    },
    {
        "fontFamily": "system-ui, sans-serif", // Will generate : --wp--preset--font-family--system: system-ui, sans-serif.
        "name": "System",
        "slug": "system"
        // No @font-face to generate.
    }
],

This ticket was mentioned in Slack in #core by jorbin. View the logs.


2 years ago

#5 @jorbin
2 years ago

  • Keywords close added

I think this might be better discussed in the Gutenberg repo where the Font library is getting updates in 6.5. https://github.com/WordPress/gutenberg/issues/55277 is the tracking ticket, I would suggest adding your comments there.

Adding the close tag as I think this can be closed as reported-upstream after the discussion is moved over.

This ticket was mentioned in Slack in #core-editor by jorbin. View the logs.


2 years ago

#7 @ironprogrammer
21 months ago

  • Keywords reporter-feedback removed
  • Milestone Awaiting Review deleted
  • Resolution set to reported-upstream
  • Status changed from new to closed

Thanks for opening GB 57207 to track this, @alexandrebuffet! Closing as noted in comment:5.

This ticket was mentioned in PR #7473 on WordPress/wordpress-develop by @webmandesign.


16 months ago
#8

  • Keywords has-patch added

#9 @webmandesign
16 months ago

  • Keywords close removed

Please re-open this issue as the code is no longer part of Gutenberg, but the core WordPress.

I've provided a fix in my pull request.

(For more info check my comments at https://github.com/WordPress/gutenberg/issues/57207#issuecomment-2375427210 )

#10 @webmandesign
16 months ago

  • Resolution reported-upstream deleted
  • Status changed from closed to reopened

Oh, I see I can re-open it here. Sorry for confusion.

#11 @alexandrebuffet
5 months ago

Oh, I haven't been here in a while.

Thanks @webmandesign for the PR. However, I can't test it in Playground. I get an error when I try to launch: https://playground.wordpress.net/wordpress.html?pr=7473.

Locally, the changes made seem to fix the problem mentioned above.

@jorbin, how can we move forward on this issue?

#12 @webmandesign
5 months ago

Hi @alexandrebuffet, is there anything I can do, or is your comment just an FYI? I don't know why the playground throws the error.

@alexandrebuffet commented on PR #7473:


5 months ago
#13

I tried to test this PR with Playground but got following error :

[07-Aug-2025 16:27:07 UTC] JavaScript Error: isFeatureBroken@<anonymous code>:980:143
updateFeaturesInner/<@<anonymous code>:9240:12
updateFeaturesInner@<anonymous code>:9239:22

[07-Aug-2025 16:27:10 UTC] JavaScript Error: PHP.run() failed with exit code 255. 

=== Stdout ===
 <br />
<b>Fatal error</b>:  Uncaught Exception: Could not unzip file: No error in /internal/eval.php:23
Stack trace:
#0 /internal/eval.php(26): unzip('/tmp/file.zip', '/tmp/unzipped-w...', true)
#1 {main}
  thrown in <b>/internal/eval.php</b> on line <b>23</b><br />


=== Stderr ===
 PHP Fatal error:  Uncaught Exception: Could not unzip file: No error in /internal/eval.php:23
Stack trace:
#0 /internal/eval.php(26): unzip('/tmp/file.zip', '/tmp/unzipped-w...', true)
#1 {main}
  thrown in /internal/eval.php on line 23

PHPExecutionFailureError@https://playground.wordpress.net/worker-thread-Dz3Cst75.js:39:431
run@https://playground.wordpress.net/worker-thread-Dz3Cst75.js:54:909

Does anyone have any idea why? Maybe the PR zip file is outdated?

#14 @alexandrebuffet
5 months ago

Hi @webmandesign!

I just wanted to know how we can move forward on this issue.

Regarding the error with Playground, it seems that it is unable to retrieve the .zip file from the PR?

I am also wondering if the fix was applied, could it cause errors in the themes that have been integrated since then?

#15 @jorbin
5 months ago

  • Keywords needs-unit-tests added
  • Milestone set to Future Release

@webmandesign Github actions only keep the zip for a period of time (I believe it's 90 days). If you merge trunk into your branch, that will create a new one and allow for testing on the playground again.

It looks like the affected lines of code were introduced in [56688].

I would like to see some automated tests included here. Adding those will help move this forward. It will also need testing to ensure that there is no effect on other themes.

#16 @webmandesign
5 months ago

I've merged the trunk into my branch and the Playground seems to be working fine now.

I'm not at home writing tests, so if someone could help, it would be faster. Thanks!

There should be no effect on other themes. If there is, it's because the theme is trying to bypass this currently incorrect WordPress behavior with some hack, I think.

The fix here is really just for using correct fontFamily value - the one set in the actual fontFace should be used (the settings.typography.fontFamilies[0].fontFace.fontFamily setting in theme.json).
But currently the settings.typography.fontFamilies[0].fontFamily is used instead, which is wrong.


I've tested anew, here is how:

  1. On WP6.8.2 I added this into Twenty Twenty-Five theme's theme.json settings:
"custom": {
	"test": {
		"font-family": "'Fira code', cursive"
	}
}
  1. In twentytwentyfive/styles/typography folder I've created new file typography-preset-test.json with this content:
{
	"version": 3,
	"$schema": "https://schemas.wp.org/wp/6.7/theme.json",
	"title": "Test font",
	"slug": "typography-preset-test",
	"settings": {
		"typography": {
			"fontFamilies": [
				{
					"name": "Test font",
					"slug": "test",
					"fontFamily": "var(--wp--custom--test--font-family)",
					"fontFace": [
						{
							"src": [
								"file:./assets/fonts/fira-code/FiraCode-VariableFont_wght.woff2"
							],
							"fontWeight": "300 400 700",
							"fontStyle": "normal",
							"fontFamily": "\"Fira Code\""
						}
					]
				}
			]
		}
	},
	"styles": {
		"typography": {
			"fontFamily": "var:preset|font-family|test"
		}
	}
}

  1. In WordPress Site Editor I've chosen the new "Test font" for Styles → Typography option.
  2. Checking the front-end page source I've noticed the wrong style.wp-fonts-local output. After I've applied the proposed fix, the style.wp-fonts-local output was corrected. Here are results:
Wrong (current WP6.8.2 without fix):
====================================
<style class='wp-fonts-local'>
@font-face {
	font-family: var(--wp--custom--test--font-family);
	font-style: normal;
	font-weight: 300 400 700;
	font-display: fallback;
	src: url('.../assets/fonts/fira-code/FiraCode-VariableFont_wght.woff2') format('woff2');
}
</style>

Correct (fixed):
================
<style class='wp-fonts-local'>
@font-face {
	font-family: "Fira Code";
	font-style: normal;
	font-weight: 300 400 700;
	font-display: fallback;
	src: url('.../assets/fonts/fira-code/FiraCode-VariableFont_wght.woff2') format('woff2');
}
</style>
Note: See TracTickets for help on using tickets.