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

Prioritize loading fonts for textual LCP elements #1313

Open
westonruter opened this issue Jun 23, 2024 · 2 comments
Open

Prioritize loading fonts for textual LCP elements #1313

westonruter opened this issue Jun 23, 2024 · 2 comments
Labels
[Plugin] Optimization Detective Issues for the Optimization Detective plugin [Type] Feature A new feature within an existing module

Comments

@westonruter
Copy link
Member

westonruter commented Jun 23, 2024

Feature Description

When the LCP element is text, the loading of the font being used should be prioritized. For example, on one of my blog posts (using the Twenty Twenty theme), the LCP element is an h1. It has a font-family style of:

"Inter var", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, sans-serif 

The Inter var font is loaded via this stylesheet:

<link rel="stylesheet" id="twentytwenty-fonts-css" href="https://weston.ruter.net/wp-content/themes/twentytwenty/assets/css/font-inter.css?ver=2.6" media="all">

The @font-face rule is:

@font-face {
	font-family: "Inter var";
	font-weight: 100 900; /* stylelint-disable-line font-weight-notation */
	font-style: normal;
	font-display: swap;
	src: url(../fonts/inter/Inter-upright-var.woff2) format("woff2");
}

The font-inter.css stylesheet is already loaded with highest priority, but the font file is not in the critical path so it is not discovered until after the critical CSS is parsed:

image

To improve performance, this font file should be getting loaded sooner by adding this link:

<link rel="preload" as="font" href="https://weston.ruter.net/wp-content/themes/twentytwenty/assets/fonts/inter/Inter-upright-var.woff2" fetchpriority="high">

This allows the font file to start loading the same time as the font-inter.css stylesheet:

image

And this will improve LCP.

Note that h1 is LCP element 5% of the time on mobile, with h2 and h3 being 2% and 1% respectively. The p element is the LCP element 9% off the time on mobile.

@westonruter westonruter added [Type] Feature A new feature within an existing module [Plugin] Optimization Detective Issues for the Optimization Detective plugin labels Jun 23, 2024
@westonruter westonruter changed the title Prioritizer loading fonts for textual LCP elements Prioritize loading fonts for textual LCP elements Jun 23, 2024
@westonruter
Copy link
Member Author

I'm thinking about how this would be implemented in practice.

It seems it would rely on calling getComputedStyle() on the LCP text element to determine the current font-family. It would then need to get the first font in that list, and then iterate over all of the stylehseets in document.styleSheets and for all of the styleSheet.cssRules` inside of them, for example:

( textElement ) => {
    const cssFontFaceRules = [];
    const stripQuotes = ( str ) => str.replace( /^"/, '' ).replace( /"$/, '' );
    const computedStyle = getComputedStyle( textElement );
    const fontFamilies = computedStyle.fontFamily.split( /\s*,\s*/ );
    const fontFamily = stripQuotes( fontFamilies[0] );
    for (const sheet of document.styleSheets) {
        for (const rule of sheet.cssRules) {
            if (rule.constructor.name === 'CSSFontFaceRule' && fontFamily === stripQuotes( rule.style.fontFamily )) {
                cssFontFaceRules.push( rule );
            }
        }
    }
    return cssFontFaceRules;
}

But note there can be multiple fonts that have the same font-family name, but just vary in terms of the font-weight and font-style:

@font-face {
	font-family: "Inter var";
	font-weight: 100 900;
	font-style: normal;
	font-display: swap;
	src: url(./assets/fonts/inter/Inter-upright-var.woff2) format("woff2");
}

@font-face {
	font-family: "Inter var";
	font-weight: 100 900;
	font-style: italic;
	font-display: swap;
	src: url(./assets/fonts/inter/Inter-italic-var.woff2) format("woff2");
}

So when determining the font file to prioritize loading, it would also need to look at the computed style to find the weight and style to determine which variant of the font should actually be preloaded.

Nevertheless, there could also be duplicate @font-face rules altogether. The above are from Twenty Twenty's style.css. However, Twenty Twenty also includes the following in assets/css/font-inter.css:

@font-face {
	font-family: "Inter var";
	font-weight: 100 900; /* stylelint-disable-line font-weight-notation */
	font-style: normal;
	font-display: swap;
	src: url(../fonts/inter/Inter-upright-var.woff2) format("woff2");
}

@font-face {
	font-family: "Inter var";
	font-weight: 100 900; /* stylelint-disable-line font-weight-notation */
	font-style: italic;
	font-display: swap;
	src: url(../fonts/inter/Inter-italic-var.woff2) format("woff2");
}

So these two stylesheets are duplicating the @font-face rules.

The last one encountered should be used since it wins the cascade.

@westonruter
Copy link
Member Author

This all depends on the new client-side extension system being implemented in #1373, so this issue is blocked by that.

I suppose a new dependent plugin would be required for this as it wouldn't make sense in Image Prioritizer or Embed Optimizer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Plugin] Optimization Detective Issues for the Optimization Detective plugin [Type] Feature A new feature within an existing module
Projects
None yet
Development

No branches or pull requests

1 participant