Pair Space Grotesk with Inter, from one variable file each - #22
Merged
Conversation
Nine static Inter weights were fetched from gstatic by content hash, which pinned a build of Inter with no `opsz` axis, could be verified against nothing, and cost 2.7 MB. They existed to work around `FontWeight` not driving the `wght` axis — fixed in Flutter 3.41, and this package already requires 3.44. Both faces are now single variable files with no per-weight entries, together under 1 MB, and every weight is the axis moving rather than a file being matched. A test measures that rather than trusting it: laid-out width rises across w300/w400/w700, w450 lands between w400 and w500 where no static file existed, and the two families measure differently, which is how a font wiring mistake usually hides — everything still renders. Space Grotesk takes the display and headline roles. It is drawn for size, and its tight sidebearings read as character in a headline and as noise in a paragraph, so the title role stays on Inter too: it labels list rows rather than heading a screen. The families are applied through the text theme, not `ThemeData.fontFamily`, so every other property of the Material 3 type scale stays as the framework defines it — which is what should carry this through to Material Expressive. `Branding.seedColor` defaults to the EigenInteractive teal and `Branding.displayFontFamily` to Space Grotesk, so a game looks deliberate before anyone has configured it and is one line each to make its own. The download script is deleted rather than updated: `fonts.google.com/download` answers with the site's HTML rather than an archive, so anything automated would be scraping a page free to change. fonts/README.md documents the provenance instead, alongside the OFL notices the licence requires to travel with the files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Typography and the default theme, as the first step of the brand work. Colour on the engine's own pages and the docs site follows from the decision made here.
The static weights were working around a fixed bug
Nine static Inter weights were fetched from
fonts.gstatic.comby content hash. That existed becauseFontWeightdid not drive thewghtaxis — one variable file meant one rendered weight. It landed in Flutter 3.41, and this package already requires 3.44.So the workaround outlived the problem, and cost:
opszaxisBoth are declared with no
weight:entries — adding them puts Flutter back into matching a file per weight and defeats the axis.Measured, not assumed
A variable font whose axis is ignored still lays out; it just lays out the same at every weight. That is exactly the failure the static weights were hiding, so the test measures rendered width rather than trusting the release notes:
A
flutter build webof the example confirms both faces reach the bundle and register under the package-qualified names the theme uses.Why the roles split where they do
Space Grotesk takes display and headline only. It is drawn for size — tight sidebearings, single-storey
g— which reads as character in a headline and as noise in a paragraph. Inter is drawn for UI at small sizes and keeps everything a player reads at length, including the title role, which labels list rows rather than heading a screen.The families are applied through the text theme rather than
ThemeData.fontFamily, so sizes, weights, tracking and line heights stay exactly as Material 3 defines them. That is what should carry this through to Material Expressive, which changes the scale and shapes rather than the mechanism.Defaults that look deliberate
Branding.seedColornow defaults to the EigenInteractive teal andBranding.displayFontFamilyto Space Grotesk. A scaffolded game looks intentional before anyone has thought about colour, instead of looking like Material's baseline purple, and each is one line to make its own.Worth recording for whoever picks a seed later: Material 3 treats it as a hue, not a colour. It pulls whatever you give it to tone 40 and rebuilds the ramp, so
#2f6b5e,Colors.tealandColors.tealAccentall arrive within a few percent of each other — and a near-black seed comes back chromatic, not neutral.The download script is gone rather than updated
fonts.google.com/download?family=…answers with the site's HTML rather than an archive — I checked — so anything automated here would be scraping a page free to change.fonts/README.mddocuments provenance and the update procedure instead, beside the OFL notices the licence requires to travel with the files.Verified
dart analyzeclean,flutter test197 passing (5 theme + 4 measurement tests new),flutter build web --releaseon the example.🤖 Generated with Claude Code