mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[PR #956] [MERGED] fix(fonts): stabilize next/font/google runtime registrations #982
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#982
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/cloudflare/vinext/pull/956
Author: @NathanDrake2406
Created: 4/29/2026
Status: ✅ Merged
Merged: 4/29/2026
Merged by: @james-elicx
Base:
main← Head:nathan/font-google-phase1📝 Commits (4)
29c561bfix(fonts): make Google font shim registrations stableba7126efix(fonts): address Google font review feedbackf27c9cdfix(fonts): canonicalize Google font identity hashing2f8de11fix(fonts): normalize default fallback font identities📊 Changes
2 files changed (+203 additions, -37 deletions)
View changed files
📝
packages/vinext/src/shims/font-google-base.ts(+94 -24)📝
tests/font-google.test.ts(+109 -13)📄 Description
What this changes
Fixes #883.
The
next/font/googleruntime shim now gives each(family, options)registration a deterministic class identity instead of using a module-level counter. Repeated equivalent calls now reuse the sameclassNameandvariableclass, so dev HMR re-evaluation no longer appends a new pair of SSR style rules every time the app module reloads.This also removes the shim-only
:root { --font-... }emission. Google font variables now live only on the returned.variableclass, matching Next.js behavior.Why
The issue is not really three separate bugs. Counter runaway, stale head rules, and pinned
:rootvariables all come from the same bad invariant: the shim treated every runtime call as a new font registration even when the call represented the same font and options.Next.js avoids this shape entirely:
Inter({ subsets: ["latin"] })into a font CSS module request:next-font-loader/index.tslines 18-30.next-font-loader/index.tslines 128-134.classNameandvariableexports into__${exportName}_${fontFamilyHash}:next-font.tslines 44-53..classNamerule and, when requested, a.variablerule. It does not emit a:rootrule:postcss-next-font.tslines 138-174.Approach
classCounter++with a deterministic FNV-based identity over the font family, effective font-family string, CSS variable name, public font options, and self-hosted CSS.injectedRootVariablesand the:rootrule path.Validation
:rootfont variable emission, and no SSR style growth for repeated equivalent calls.vp test run tests/font-google.test.tsvp test run tests/shims.test.ts -t "next/font/google shim"vp checkvp run vinext#buildRisks / follow-ups
Apps that accidentally relied on vinext injecting font variables at
:rootwithout applying the returned.variableclass will need to applyfont.variable, which is the Next.js-compatible usage. This PR intentionally does not attempt the larger Phase 2 architecture where dev font calls become Vite-owned virtual CSS modules.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.