WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.54k stars 4.21k forks source link

Font Face: Inconsistency between font-family value in theme.json preset and font-family value in generated @font-face. #57207

Open alexandrebuffet opened 11 months ago

alexandrebuffet commented 11 months ago

Description

This issue refers to ticket 59911 on track.

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

I understand the problem initially raised in the PR 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 in PR 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 theme.json.

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.

Here's an example below to illustrate that the value of the "fontFamily" entry in a preset should not be used to generate the value of @font-face, since this value can be a CSS variable and not necessarily the name of the font to be loaded.

Step-by-step reproduction instructions

  1. Open theme.json in your favorite code editor
  2. Register new font families as follow:
    "fontFamilies": [
    {
        "fontFamily": "var(--font-primary)",
        "name": "Primary (Inter)",
        "slug": "primary",
        "fontFace": [
            {
                "fontFamily": "Inter",
                "fontStretch": "normal",
                "fontStyle": "normal",
                "fontWeight": "400 700",
                "fontDisplay": "swap",
                "src": ["file:./assets/fonts/Inter-VariableFont_slnt,wght.woff2"]
            },
            {
                "fontFamily": "Inter",
                "fontStretch": "normal",
                "fontStyle": "oblique 14deg",
                "fontDisplay": "swap",
                "fontWeight": "400 700",
                "src": ["file:./assets/fonts/Inter-VariableFont_slnt,wght.woff2"]
            }
        ]
    },
    ],
  3. Open any page of your site in your favorite browser
  4. Open inspector
  5. Notice in code the generated @font-face with inconsistent font-family

Screenshots, screen recording, code snippet

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;}

Environment info

Please confirm that you have searched existing issues in the repo.

Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

Yes

mrwweb commented 2 months ago

I just ran into this issue and wanted to add an additional use case that is currently impossible with the way theme.json outputs @font-face declarations (currently on WP 6.6): overriding specific glyphs in a font.

On a current project, the designer selected a font designed first for Japanese that includes latin characters as well. In Japanese, the ellipsis character ("…", code point U+2026) is vertically centered, which looks wrong in English. The generally accepted solution for this issue is to create a subsetted font file that only contains the ellipsis character. You then declare the subsetted font as the first font in font-family and "fall back" to the font used for displaying all other characters. This works because browsers can follow the declared order of fonts in font-family per glyph/character.

This technique can also be used when wanting to declare different fonts for different languages in a single font-family declaration.

Here's a slightly simplified version of what I had.

theme.json:

{
    "$schema": "https://schemas.wp.org/wp/6.6/theme.json",
    "version": 3,
    "settings": {
        "typography": {
            "fontFamilies": [
                {
                    "name": "DesignerFont",
                    "slug": "headings",
                    "fontFamily": "CustomEllipsisFont, DesignerFont, serif",
                    "fontFace": [
                        {
                            "fontFamily": "DesignerFont",
                            "src": [
                                "file:./assets/fonts/DesignerFont.woff2"
                            ]
                        }
                    ]
                }
            ]
        }
    }
}

Custom CSS:

/* this font only has the "…" character in it */
@font-face {
    font-family: "CustomEllipsisFont";
    src: url("../fonts/custom-ellipsis-font.woff2") format("woff2");
    unicode-range: U+2026;
}

I declared my font in theme.json and was spinning in circles for an hour before I realized that the rendered output for font-family is NOT the value of settings.typography.fontFamilies.fontFace.fontFamily but instead the first value of settings.typography.fontFamilies.fontFamily.

Output CSS:

@font-face {
  font-family: CustomEllipsisFont;
  src: url("https://my-site.local/wp-content/themes/my-theme/assets/fonts/DesignerFont.woff2")
    format("woff2");
}

Note the mismatch between the intended font file and the font-family name.

My solution was to leave theme.json alone (removing "CustomEllipsisFont" from the font-family declaration) then redeclare the font-family's custom property with the custom font added:

body {
    --wp--preset--font-family--headings: "EllipsisFont, DesignerFont, serif";
}

I don't know what the best solution is as I certainly haven't thought about this as deeply as @hellofromtonya or @matiasbenedetto. All I know is that the current setup prevents overriding custom glyphs in a font defined in theme.json without a workaround.

webmandesign commented 1 month ago

I can confirm the issue as I've also experienced it today. It is really confusing and debugging took me a while.

The confusing part also is that according to theme.json schema the settings.typography.fontFamilies.fontFace.fontFamily is required property although it is being ignored by WordPress when producing @font-face CSS code.

The issue seems to be caused by the code in WP_Font_Face_Resolver::parse_settings(). So, I'm not sure whether this issue is Gutenberg or WordPress core related issue.

webmandesign commented 1 month ago

BTW, the solution is simple: just replace $definition['fontFamily'] with $definition['fontFace']['fontFamily'] in WP_Font_Face_Resolver::parse_settings(), specifically on line 59 and 63.

I just don't know where to send the PR fixing the issue. I can't find the file with the code here in Gutenberg GH repo, so I should probably create a WordPress patch - is this correct, can anyone confirm?

webmandesign commented 1 month ago

As the code for this is no longer part of Gutenberg, I think this issue should be dealt at WordPress trac.

I've provided a pull request fixing the issue.