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

Google Fonts with weights above 900 won't load #54304

Closed
1 task done
thecmdrunner opened this issue Aug 20, 2023 · 4 comments
Closed
1 task done

Google Fonts with weights above 900 won't load #54304

thecmdrunner opened this issue Aug 20, 2023 · 4 comments
Labels
bug Issue was opened via the bug report template. Font (next/font) Related to Next.js Font Optimization. locked

Comments

@thecmdrunner
Copy link

thecmdrunner commented Aug 20, 2023

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC Thu Jul 27 19:56:37 UTC 2023
Binaries:
  Node: 18.16.0
  npm: 9.5.1
  Yarn: 1.22.19
  pnpm: 8.6.8
Relevant Packages:
  next: 13.4.20-canary.0
  eslint-config-next: 13.4.2
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.1.6
Next.js Config:
  output: N/A

Which area(s) of Next.js are affected? (leave empty if unsure)

Font optimization (next/font)

Link to the code that reproduces this issue or a replay of the bug

https://codesandbox.io/p/sandbox/strange-matsumoto-nlnw4p?file=/app/layout.tsx:32,41

To Reproduce

  1. Create a next app
  2. Use DM Sans with all weights
import { DM_Sans } from "next/font/google";

const DMSans = DM_Sans({
  weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900", '1000'],
  subsets: ["latin"],
});

const Component = () => <p className={fonts.DMSans.className}>Hello</p>

Describe the Bug

image

Can't load DM sans even though it's not a network issue. This is likely due to the generated Google Fonts URL for DM Sans being rejected, if the font weights aren't sorted in the correct order (as shown below):

image

  • The URL generated by next/font (1000 comes after 100):
https://fonts.googleapis.com/css2?family=DM+Sans:wght@100;1000;200;300;400;500;600;700;800;900&display=swap
  • This is how the URL should be (1000 at the end)
https://fonts.googleapis.com/css2?family=DM+Sans:wght@100;200;300;400;500;600;700;800;900;1000&display=swap

Expected Behavior

DM sans font should be able to load with all weights selected

Which browser are you using? (if relevant)

Brave Version 1.56.20 Chromium: 115.0.5790.171

How are you deploying your application? (if relevant)

locally for now, but I'd assume this would be the same on vercel

@thecmdrunner thecmdrunner added the bug Issue was opened via the bug report template. label Aug 20, 2023
@github-actions github-actions bot added the Font (next/font) Related to Next.js Font Optimization. label Aug 20, 2023
@indraantoor
Copy link
Contributor

indraantoor commented Aug 21, 2023

It seems like in the case of the URL generated by next/font, there might be an issue in the sorting function, if a sorting function is being called during the generation of the URL.

Explanation:

Consider an array named "numbers" with the values (weights of fonts) assigned to it.

const numbers = ["100", "200", "300", "400", "500", "600", "700", "800", "900", '1000'];

When the sort function is called on it without passing any comparison function, it gives us the result:

["100", "1000", "200", "300", "400", "500", "600", "700", "800", "900"]

which is "not" the desired outcome.

The reason is, due to the default behaviour of the Array.prototype.sort() method in JavaScript. When no comparison function is provided, the sort() method converts the elements of the array to strings and then performs a lexicographic (dictionary-style) sorting.

In this case, all the elements in the "numbers" array are strings, so they are sorted lexicographically. When lexicographically sorted, "1000" comes before "200" because "1" comes before "2" in the character encoding.

By providing the comparison function (a, b) => parseInt(a) - parseInt(b), it will ensure that the elements are compared as integers, and thus the sorting will be done numerically.

I think we should check where this sorting is taking place.

@thecmdrunner
Copy link
Author

@indraantoor thanks for the great explanation, I had thought something similar as well.

kodiakhq bot pushed a commit that referenced this issue Aug 28, 2023
## What?
This PR fixes issue #54304. As discussed in the issue it was suspected that the values of weights were not being sorted correctly. The weights were being sorted in lexicographical order. 

In order for Google fonts to work, Google Fonts need the values of weights present in the url in sorted order.

Due to this the url which was being generated for Google Fonts which had a value of "1000" in weight was giving an error and the font was not working.

## Why I Think This Solution Works?

Let's assume that we have an object named axes with the property named named "wght" which will have the value of weights as following:

```
const axes = {
  wght: ["100", "200", "300", "400", "500", "600", "700", "800", "900", '1000']
}
```

Let's create another variable named "display" with the value set as "swap" and create another variable named "fontFamily" with the value as "DM Sans" (this is the font which was being used in the issue).

```
const fontFamily = "DM Sans";
const display = "swap";
```

If we call the function using them
```
const FONT_URL = getGoogleFontsUrl(fontFamily, axes, display);
console.log("The font url is: ", FONT_URL)

Output Will Be:
The font url is: https://fonts.googleapis.com/css2?family=DM+Sans:wght@100;1000;200;300;400;500;600;700;800;900&display=swap
```
It will give the incorrect value which was shown in the issue. 

If you see at line number 56 there was no comparison function passed in the sort function which is what was suspected earlier and it was sorting the values lexicographically.

Create another variable inside the function above line 54 where we are updating the value of "url" variable.

```
const variantValues = variants.map((variant) => variant.map(([, val]) => val).join(','))
console.log("Variant Values: ", variantValues)

Output Will Be:
Variant Values: ["100", "200", "300", "400", "500", "600", "700", "800", "900", "1000"]
```

Now call the function again with the same values you will see the values in the correct order but as soon as you call "sort" function it will give you incorrect sorted values which is what is happening in the "url" variable.

```
const variantValues = variants.map((variant) => variant.map(([, val]) => val).join(',')).sort()
console.log("Variant Values: ", variantValues)

Output Will Be:
["100", "1000", "200", "300", "400", "500", "600", "700", "800", "900"] 

(As you can see the values are sorted incorrectly)
```
There was one case when I implemented this solution. When "ital" is also passed then the values are in "ital,wght" format and because of it the sorting was giving incorrect result for this case.

Let's update the "axes" variable which we made previously to contain these values:

```
const axes = {
  wght: ["500","300", "400"],
  ital: ["0","1"]
}
```
Now when we run the function it gives us the result as:
`
https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,500;0,300;0,400;1,500;1,300;1,400&display=swap`

Where as the expected result was: 

`https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap`

So, I rewrote the "sort" function to also handle this case.

## What Did I Do To Fix It?

I passed the comparison function inside the "sort" function which was being to ensure that the elements are compared as integers, and thus the sorting will be done numerically.

Which gave me the correct output as url:

`https://fonts.googleapis.com/css2?family=DM+Sans:wght@100;200;300;400;500;600;700;800;900;1000&display=swap`

With values being sorted correctly and if you visit the link you will see the response instead of the error.

Any thought's or suggestions would be greatly appreciated.
@huozhi
Copy link
Member

huozhi commented Aug 28, 2023

Closes as the fix mentioned here is landed in #54339

@huozhi huozhi closed this as completed Aug 28, 2023
@github-actions
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 12, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template. Font (next/font) Related to Next.js Font Optimization. locked
Projects
None yet
Development

No branches or pull requests

3 participants