-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
feat(fonts): experimental release #12775
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
Merged
Merged
Changes from 65 commits
Commits
Show all changes
68 commits
Select commit
Hold shift + click to select a range
6391469
feat: setup branch
florian-lefebvre fdb3141
Merge branch 'main' into feat/fonts
florian-lefebvre 21b6f35
Merge branch 'main' into feat/fonts
florian-lefebvre 95673fc
feat(fonts): config (#12777)
florian-lefebvre c6cd69c
Merge branch 'main' into feat/fonts
florian-lefebvre 3c18360
feat(fonts): vite plugin (#13093)
florian-lefebvre 338757d
Merge branch 'main' into feat/fonts
florian-lefebvre 4899359
Merge branch 'main' into feat/fonts
florian-lefebvre c6bc00f
feat(fonts): rename component (#13207)
florian-lefebvre 363faf9
feat(fonts): add more providers (#13208)
florian-lefebvre 193a9c0
feat(fonts): local provider (#13209)
florian-lefebvre e97e42a
Merge branch 'main' into feat/fonts
florian-lefebvre e8aead8
feat(fonts): improve providers (#13263)
florian-lefebvre 918ee30
feat(fonts): improve type handling (#13270)
florian-lefebvre 8bc05a0
feat(fonts): improve cache (#13273)
florian-lefebvre 287b789
refactor(fonts): url proxy (#13274)
florian-lefebvre 868e2ab
feat(fonts): dev headers (#13275)
florian-lefebvre 8e89e80
feat(fonts): better local provider (#13276)
florian-lefebvre c515886
Merge branch 'main' into feat/fonts
florian-lefebvre 08eff8c
Merge branch 'main' into feat/fonts
florian-lefebvre 4eef143
feat(fonts): fallbacks (#13331)
florian-lefebvre a84a8b5
Merge branch 'main' into feat/fonts
florian-lefebvre d8e0743
feat(fonts): refactor loading logic (#13353)
florian-lefebvre f62a7b7
Merge branch 'main' into feat/fonts
florian-lefebvre 75b1f5b
feat(fonts): css vars (#13362)
florian-lefebvre 0f3419c
feat(fonts): as prop (#13366)
florian-lefebvre 4a784be
Merge branch 'main' into feat/fonts
florian-lefebvre 1f3fc18
Merge branch 'main' into feat/fonts
florian-lefebvre 7ce73a1
feat(fonts): resolve config todos (#13401)
florian-lefebvre 784976d
feat(fonts): use capsize instead of fontaine (#13411)
florian-lefebvre f01739d
feat(fonts): family name validation (#13417)
florian-lefebvre 22ed30c
fix: zod error path
florian-lefebvre ef6f9ce
fix(fonts): vite plugin cleanup (#13424)
florian-lefebvre b70d8c4
feat(fonts): allow disabling automatic fallback generation (#13442)
florian-lefebvre c3845c8
feat(fonts): add errors (#13425)
florian-lefebvre e42de26
Merge branch 'main' into feat/fonts
florian-lefebvre 93ffd30
feat(fonts): typegen for <Font /> family prop (#13465)
florian-lefebvre b6af656
Merge branch 'main' into feat/fonts
florian-lefebvre 2665ebc
Update packages/astro/src/core/errors/errors-data.ts
florian-lefebvre dc24bdd
Merge branch 'main' into feat/fonts
florian-lefebvre b470183
Apply suggestions from code review
florian-lefebvre 3fcdba0
fix(fonts): config family types for inferred provider (#13469)
florian-lefebvre 100cd48
Merge branch 'main' into feat/fonts
florian-lefebvre fedde31
Merge branch 'main' into feat/fonts
florian-lefebvre af0e0e1
fix(fonts): throw error on unsuccessful responses (#13517)
florian-lefebvre 4250554
fix(fonts): read local fonts files to extract metrics (#13518)
florian-lefebvre d893932
feat(fonts): set default fallbacks (#13516)
florian-lefebvre 6cd4c14
Merge branch 'main' into feat/fonts
florian-lefebvre d7b2b51
feat(fonts): update config shape (#13515)
florian-lefebvre 0dbe842
feat(fonts): rename provider type helper (#13533)
florian-lefebvre 78c88b2
feat(fonts): simpler logging (#13535)
florian-lefebvre 0730611
fix(fonts): do not add quotes to family names (#13534)
florian-lefebvre 95b9fef
Merge branch 'main' into feat/fonts
florian-lefebvre c22561c
feat(fonts): new cssVariable property (#13544)
florian-lefebvre b350235
Merge branch 'main' into feat/fonts
florian-lefebvre 118a707
feat(fonts): update local family shape (#13553)
florian-lefebvre 7d50c96
Merge branch 'main' into feat/fonts
florian-lefebvre 0362529
feat(fonts): improve local font files deletion (#13559)
florian-lefebvre 8ae9be0
feat(fonts): update local to support entrypoints src and techs (#13556)
florian-lefebvre 5e47be6
Merge branch 'main' into feat/fonts
florian-lefebvre 8454fc9
fix(fonts): patch unifont (#13574)
florian-lefebvre 854eca9
feat(fonts): remove default provider (#13572)
florian-lefebvre c694f23
Merge branch 'main' into feat/fonts
florian-lefebvre d44e4f1
feat(fonts): T&D feedback (#13597)
florian-lefebvre be114f2
feat(fonts): jsdocs (#13600)
florian-lefebvre 413af84
Update .changeset/happy-spies-punch.md
florian-lefebvre 0705f78
Merge remote-tracking branch 'origin/main' into feat/fonts
ematipico 4ee7308
rebase
ematipico File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| 'astro': minor | ||
| --- | ||
|
|
||
| Adds first-party support for fonts | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,6 +83,9 @@ | |
| "workerd", | ||
| "@biomejs/biome", | ||
| "sharp" | ||
| ] | ||
| ], | ||
| "patchedDependencies": { | ||
| "[email protected]": "patches/[email protected]" | ||
| } | ||
| } | ||
| } | ||
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| --- | ||
| import { AstroError, AstroErrorData } from '../dist/core/errors/index.js'; | ||
|
|
||
| // TODO: remove dynamic import when fonts are stabilized | ||
| const { fontsData } = await import('virtual:astro:assets/fonts/internal').catch(() => { | ||
| throw new AstroError(AstroErrorData.ExperimentalFontsNotEnabled); | ||
| }); | ||
|
|
||
| interface Props { | ||
| /** The `cssVariable` registered in your Astro configuration. */ | ||
| cssVariable: import('astro:assets').FontFamily; | ||
| /** Whether it should output [preload links](https://web.dev/learn/performance/optimize-web-fonts#preload) or not. */ | ||
| preload?: boolean; | ||
| } | ||
|
|
||
| const { cssVariable, preload = false } = Astro.props; | ||
| const data = fontsData.get(cssVariable); | ||
| if (!data) { | ||
| throw new AstroError({ | ||
| ...AstroErrorData.FontFamilyNotFound, | ||
| message: AstroErrorData.FontFamilyNotFound.message(cssVariable), | ||
| }); | ||
| } | ||
| --- | ||
|
|
||
| <style set:html={data.css}></style> | ||
| { | ||
| preload && | ||
| data.preloadData.map(({ url, type }) => ( | ||
| <link rel="preload" href={url} as="font" type={`font/${type}`} crossorigin /> | ||
| )) | ||
| } |
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # fonts | ||
|
|
||
| The vite plugin orchestrates the fonts logic: | ||
|
|
||
| - Retrieves data from the config | ||
| - Initializes font providers | ||
| - Fetches fonts data | ||
| - In dev, serves a middleware that dynamically loads and caches fonts data | ||
| - In build, download fonts data (from cache if possible) | ||
|
|
||
| The `<Font />` component is the only aspect not managed in the vite plugin, since it's exported from `astro:assets`. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| import { z } from 'zod'; | ||
| import { LOCAL_PROVIDER_NAME } from './constants.js'; | ||
|
|
||
| const weightSchema = z.union([z.string(), z.number()]); | ||
| const styleSchema = z.enum(['normal', 'italic', 'oblique']); | ||
|
|
||
| const familyPropertiesSchema = z.object({ | ||
| /** | ||
| * A [font weight](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights: | ||
| * | ||
| * ```js | ||
| * weight: "100 900" | ||
| * ``` | ||
| */ | ||
| weight: weightSchema, | ||
| /** | ||
| * A [font style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style). | ||
| */ | ||
| style: styleSchema, | ||
| /** | ||
| * @default `"swap"` | ||
| * | ||
| * A [font display](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display). | ||
| */ | ||
| display: z.enum(['auto', 'block', 'swap', 'fallback', 'optional']).optional(), | ||
| /** | ||
| * A [unicode range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range). | ||
| */ | ||
| unicodeRange: z.array(z.string()).nonempty().optional(), | ||
| /** | ||
| * A [font stretch](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch). | ||
| */ | ||
| stretch: z.string().optional(), | ||
| /** | ||
| * Font [feature settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings). | ||
| */ | ||
| featureSettings: z.string().optional(), | ||
| /** | ||
| * Font [variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-variation-settings). | ||
| */ | ||
| variationSettings: z.string().optional(), | ||
| }); | ||
|
|
||
| const fallbacksSchema = z.object({ | ||
| /** | ||
| * @default `["sans-serif"]` | ||
| * | ||
| * An array of fonts to use when your chosen font is unavailable, or loading. Fallback fonts will be chosen in the order listed. The first available font will be used: | ||
| * | ||
| * ```js | ||
| * fallbacks: ["CustomFont", "serif"] | ||
| * ``` | ||
| * | ||
| * To disable fallback fonts completely, configure an empty array: | ||
| * | ||
| * ```js | ||
| * fallbacks: [] | ||
| * ``` | ||
| * | ||
|
|
||
| * If the last font in the `fallbacks` array is a [generic family name](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family#generic-name), an [optimized fallback](https://developer.chrome.com/blog/font-fallbacks) using font metrics will be generated. To disable this optimization, set `optimizedFallbacks` to false. | ||
| */ | ||
| fallbacks: z.array(z.string()).nonempty().optional(), | ||
| /** | ||
| * @default `true` | ||
| * | ||
| * Whether or not to enable optimized fallback generation. You may disable this default optimization to have full control over `fallbacks`. | ||
| */ | ||
| optimizedFallbacks: z.boolean().optional(), | ||
| }); | ||
|
|
||
| export const requiredFamilyAttributesSchema = z.object({ | ||
| /** | ||
| * The font family name, as identified by your font provider. | ||
| */ | ||
| name: z.string(), | ||
| /** | ||
| * A valid [ident](https://developer.mozilla.org/en-US/docs/Web/CSS/ident) in the form of a CSS variable (i.e. starting with `--`). | ||
| */ | ||
| cssVariable: z.string(), | ||
| }); | ||
|
|
||
| const entrypointSchema = z.union([z.string(), z.instanceof(URL)]); | ||
|
|
||
| export const fontProviderSchema = z | ||
| .object({ | ||
| /** | ||
| * URL, path relative to the root or package import. | ||
| */ | ||
| entrypoint: entrypointSchema, | ||
| /** | ||
| * Optional serializable object passed to the unifont provider. | ||
| */ | ||
| config: z.record(z.string(), z.any()).optional(), | ||
| }) | ||
| .strict(); | ||
|
|
||
| export const localFontFamilySchema = requiredFamilyAttributesSchema | ||
| .merge(fallbacksSchema) | ||
| .merge( | ||
| z.object({ | ||
| /** | ||
| * The source of your font files. Set to `"local"` to use local font files. | ||
| */ | ||
| provider: z.literal(LOCAL_PROVIDER_NAME), | ||
| /** | ||
| * Each variant represents a [`@font-face` declaration](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/). | ||
| */ | ||
| variants: z | ||
| .array( | ||
| familyPropertiesSchema.merge( | ||
| z | ||
| .object({ | ||
| /** | ||
| * Font [sources](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src). It can be a path relative to the root, a package import or a URL. URLs are particularly useful if you inject local fonts through an integration. | ||
| */ | ||
| src: z | ||
| .array( | ||
| z.union([ | ||
| entrypointSchema, | ||
| z.object({ url: entrypointSchema, tech: z.string().optional() }).strict(), | ||
| ]), | ||
| ) | ||
| .nonempty(), | ||
| // TODO: find a way to support subsets (through fontkit?) | ||
| }) | ||
| .strict(), | ||
| ), | ||
| ) | ||
| .nonempty(), | ||
| }), | ||
| ) | ||
| .strict(); | ||
|
|
||
| export const remoteFontFamilySchema = requiredFamilyAttributesSchema | ||
| .merge( | ||
| familyPropertiesSchema.omit({ | ||
| weight: true, | ||
| style: true, | ||
| }), | ||
| ) | ||
| .merge(fallbacksSchema) | ||
| .merge( | ||
| z.object({ | ||
| /** | ||
| * The source of your font files. You can use a built-in provider or write your own custom provider. | ||
| */ | ||
| provider: fontProviderSchema, | ||
| /** | ||
| * @default `[400]` | ||
| * | ||
| * An array of [font weights](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights: | ||
| * | ||
| * ```js | ||
| * weight: "100 900" | ||
| * ``` | ||
| */ | ||
| weights: z.array(weightSchema).nonempty().optional(), | ||
| /** | ||
| * @default `["normal", "italic"]` | ||
| * | ||
| * An array of [font styles](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style). | ||
| */ | ||
| styles: z.array(styleSchema).nonempty().optional(), | ||
| // TODO: better link | ||
| /** | ||
| * @default `["cyrillic-ext", "cyrillic", "greek-ext", "greek", "vietnamese", "latin-ext", "latin"]` | ||
| * | ||
| * An array of [font subsets](https://fonts.google.com/knowledge/glossary/subsetting): | ||
| */ | ||
| subsets: z.array(z.string()).nonempty().optional(), | ||
| }), | ||
| ) | ||
| .strict(); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import type { ResolvedRemoteFontFamily } from './types.js'; | ||
|
|
||
| export const LOCAL_PROVIDER_NAME = 'local'; | ||
|
|
||
| export const DEFAULTS = { | ||
| weights: ['400'], | ||
| styles: ['normal', 'italic'], | ||
| subsets: ['cyrillic-ext', 'cyrillic', 'greek-ext', 'greek', 'vietnamese', 'latin-ext', 'latin'], | ||
| // Technically serif is the browser default but most websites these days use sans-serif | ||
| fallbacks: ['sans-serif'], | ||
| optimizedFallbacks: true, | ||
| } satisfies Partial<ResolvedRemoteFontFamily>; | ||
|
|
||
| export const VIRTUAL_MODULE_ID = 'virtual:astro:assets/fonts/internal'; | ||
| export const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID; | ||
|
|
||
| // Requires a trailing slash | ||
| export const URL_PREFIX = '/_astro/fonts/'; | ||
| export const CACHE_DIR = './fonts/'; | ||
|
|
||
| export const FONT_TYPES = ['woff2', 'woff', 'otf', 'ttf', 'eot'] as const; | ||
|
|
||
| // Source: https://github.com/nuxt/fonts/blob/3a3eb6dfecc472242b3011b25f3fcbae237d0acc/src/module.ts#L55-L75 | ||
| export const DEFAULT_FALLBACKS: Record<string, Array<string>> = { | ||
| serif: ['Times New Roman'], | ||
| 'sans-serif': ['Arial'], | ||
| monospace: ['Courier New'], | ||
| cursive: [], | ||
| fantasy: [], | ||
| 'system-ui': ['BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial'], | ||
| 'ui-serif': ['Times New Roman'], | ||
| 'ui-sans-serif': ['Arial'], | ||
| 'ui-monospace': ['Courier New'], | ||
| 'ui-rounded': [], | ||
| emoji: [], | ||
| math: [], | ||
| fangsong: [], | ||
| }; | ||
|
|
||
| export const FONTS_TYPES_FILE = 'fonts.d.ts'; |
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.