Optimized font loading for Angular with SSR support
npm install angular-fontsOptimized font loading for Angular with SSR support, inspired by @next/font.
- 🚀 Build-time optimization: Download and self-host fonts during build
- ⚡ Runtime service: Dynamic font loading when needed
- 🔄 SSR compatible: Works with Angular 17+ and @angular/ssr
- 🎨 Tailwind integration: CSS variables for both v4 (@theme) and v3 (config file)
- 📦 Google Fonts: 1000+ fonts available
- 🏠 Local fonts: Support for custom font files
- 🎯 Zero layout shift: Automatic fallback metrics
- ✂️ Font subsetting: Reduce file sizes by 80-90% with text/unicode range subsetting
- 🔧 Variable fonts: Full support for variable font axes (wght, slnt, wdth, custom)
- 🌐 Custom CDN: Self-host fonts on your own CDN infrastructure
- 🔄 Advanced retry: Configurable retry strategies with exponential backoff
``bash`
npm install angular-fontsor
pnpm add angular-fontsor
yarn add angular-fonts
`bash`
npm install angular-fontsor
pnpm add angular-fontsor
yarn add angular-fonts
`typescript
// src/fonts.ts
import { Inter, Roboto_Mono } from "angular-fonts/google";
export const inter = Inter({
weights: [400, 700],
subsets: ["latin"],
variable: "--font-inter",
});
export const robotoMono = Roboto_Mono({
subsets: ["latin"],
variable: "--font-roboto-mono",
});
`
`json`
// angular.json
{
"projects": {
"my-app": {
"architect": {
"font-optimize": {
"builder": "angular-fonts:optimize",
"options": {
"outputPath": "dist",
"projectRoot": "",
"sourceRoot": "src",
"fontFile": "src/fonts.ts",
"injectTailwind": "v4"
}
}
}
}
}
}
`bash`
ng run my-app:font-optimizeor add to your build script
npm run build # includes font optimization
`typescript
// app.component.ts
import { Component } from "@angular/core";
import { inter, robotoMono } from "./fonts";
@Component({
selector: "app-root",
template:
Code example,
host: {
"[class]": "fontClasses",
},
})
export class AppComponent {
fontClasses = ${inter.className} ${robotoMono.className};
}
`$3
`typescript
// fonts.ts
import { localFont } from "angular-fonts/local";// Single font file
export const customFont = localFont({
src: "./fonts/my-font.woff2",
variable: "--font-custom",
fallback: ["system-ui", "arial"],
// adjustFontFallback automatically generates fallback metrics (default: true)
});
// Multiple weights and styles
export const rubikFamily = localFont({
src: [
{ path: "./static/Rubik-Regular.ttf", weight: "400", style: "normal" },
{ path: "./static/Rubik-Bold.ttf", weight: "700", style: "normal" },
{ path: "./static/Rubik-Italic.ttf", weight: "400", style: "italic" },
],
variable: "--font-rubik",
display: "swap",
// Automatically uses Arial as fallback for sans-serif fonts
});
`$3
`typescript
// fonts.ts
import { Inter } from "angular-fonts/google";// Subset by specific text
export const interSubset = Inter({
weights: [400, 700],
subsets: ["latin"],
subset: {
text: "Hello World 123", // Only these characters
},
variable: "--font-inter-subset",
});
// Subset by unicode range
export const interRange = Inter({
weights: [400, 700],
subsets: ["latin"],
subset: {
unicodeRange: "U+0020-007F", // Basic Latin
},
variable: "--font-inter-range",
});
// Use helper for common character sets
import { COMMON_CHARACTER_SETS } from "angular-fonts/google";
export const interBasic = Inter({
weights: [400, 700],
subsets: ["latin"],
subset: {
text: COMMON_CHARACTER_SETS.basicLatin, // A-Z, a-z, 0-9, basic punctuation
},
variable: "--font-inter-basic",
});
`$3
`typescript
// fonts.ts
import { Inter } from "angular-fonts/google";// Basic variable font
export const interVariable = Inter({
weights: "variable", // Use variable font
subsets: ["latin"],
variable: "--font-inter-variable",
});
// Custom variable axes ranges
export const interCustomAxes = Inter({
weights: "variable",
subsets: ["latin"],
variableAxes: {
wght: [100, 900], // Weight range
slnt: [-10, 0], // Slant range
wdth: [75, 125], // Width range
},
variable: "--font-inter-custom",
});
// Additional variable axes (font-specific)
export const interAdvanced = Inter({
weights: "variable",
subsets: ["latin"],
axes: ["wght", "slnt"], // Specify which axes to include
variableAxes: {
wght: [200, 800],
slnt: [-10, 0],
},
variable: "--font-inter-advanced",
});
`$3
`typescript
// fonts.ts
import { Inter } from "angular-fonts/google";// Use custom CDN for self-hosting
export const interCDN = Inter({
weights: [400, 700],
subsets: ["latin"],
cdn: {
cssUrl: "https://my-cdn.com/fonts/css2",
fontUrl: "https://my-cdn.com/fonts/files",
},
variable: "--font-inter-cdn",
});
`$3
`typescript
// fonts.ts
import { Inter } from "angular-fonts/google";export const interResilient = Inter({
weights: [400, 700],
subsets: ["latin"],
retry: {
attempts: 5,
backoff: "exponential",
delay: 200,
timeout: 10000,
maxDelay: 5000,
},
onError: (error) => {
console.error("Font load failed:", error);
// Fallback to system font
},
onRetry: (attempt) => {
console.log(
Retry attempt ${attempt});
},
variable: "--font-inter-resilient",
});
`$3
#### Tailwind v4 (CSS-first)
`css
/ app/globals.css /
@import "tailwindcss";@theme {
--font-family-sans: var(--font-inter), system-ui, sans-serif;
--font-family-mono: var(--font-roboto-mono), ui-monospace, monospace;
--font-family-custom: var(--font-custom), serif;
}
`#### Tailwind v3 (config file)
`javascript
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ["var(--font-inter)", ...defaultTheme.fontFamily.sans],
mono: ["var(--font-roboto-mono)", ...defaultTheme.fontFamily.mono],
custom: ["var(--font-custom)", "serif"],
},
},
},
};
`Vite Plugin (AnalogJS Support)
For projects using Vite or AnalogJS, use the Vite plugin for seamless font optimization.
$3
`typescript
// vite.config.ts
import { defineConfig } from "vite";
import angular from "@analogjs/vite-plugin-angular";
import { angularFontPlugin } from "angular-fonts/vite";export default defineConfig({
plugins: [
angular(),
angularFontPlugin({
// All options are optional with smart defaults
injectHTML: true, // Auto-inject fonts into HTML (default: true)
injectTailwind: "v4", // Auto-inject Tailwind config (default: false)
}),
],
});
`$3
`typescript
angularFontPlugin({
// Font file path (relative to Vite root)
// If not provided, auto-discovers in:
// - src/fonts.ts
// - src/app/fonts.ts
// - src/lib/fonts.ts
// - src/config/fonts.ts
fontsFile?: string; // Output directory for fonts (default: "dist/assets")
outputDir?: string;
// Generate preload links file (default: true)
injectPreloads?: boolean;
// Generate CSS file (default: true)
injectCSS?: boolean;
// Enable font subsetting (default: true)
subsetting?: boolean;
// Auto-inject font CSS and preloads into HTML (default: true)
// Set to false to disable HTML injection entirely
injectHTML?: boolean;
// Path to index.html (relative to Vite root)
// If not provided, looks in src/index.html
// Set to false to disable HTML processing
indexHtml?: string | false;
// Tailwind integration
// - false: No Tailwind injection (default)
// - 'v3': Inject into tailwind.config.js
// - 'v4': Inject @theme into styles file
// - true: Auto-detect version (defaults to v4)
injectTailwind?: boolean | "v3" | "v4";
// Path to main styles file (relative to Vite root)
// Used for Tailwind v4 injection
// Auto-discovers: styles.css, styles.scss, global.css, etc.
stylesFile?: string;
// Path to Tailwind config (relative to Vite root)
// Used for Tailwind v3 injection
// Auto-discovers: tailwind.config.{js,ts,cjs,mjs}
tailwindFile?: string;
});
`$3
The Vite plugin provides automatic:
1. Font File Discovery: Checks 4 common locations for
fonts.ts
2. HTML Injection: Automatically injects font CSS and preload links into index.html using idempotent markers (won't duplicate on hot reload)
3. Tailwind Integration: Injects CSS variables into your Tailwind config (v3 or v4)
4. CSS Variables: Generates :root CSS variables for all fonts
5. Build Optimization: Processes fonts during Vite's build phase$3
`typescript
// vite.config.ts
import { defineConfig } from "vite";
import angular from "@analogjs/vite-plugin-angular";
import { angularFontPlugin } from "angular-fonts/vite";export default defineConfig({
plugins: [
angular(),
angularFontPlugin({
// Custom font file location
fontsFile: "src/config/fonts.ts",
// Custom HTML file
indexHtml: "src/index.html",
// Enable Tailwind v4 injection
injectTailwind: "v4",
stylesFile: "src/styles.css",
// Customize output
outputDir: "public/assets",
// Enable all optimizations
subsetting: true,
injectPreloads: true,
injectCSS: true,
injectHTML: true,
}),
],
});
`$3
`typescript
// vite.config.ts
import { defineConfig } from "vite";
import angular from "@analogjs/vite-plugin-angular";
import { angularFontPlugin } from "angular-fonts/vite";export default defineConfig({
plugins: [
angular(),
angularFontPlugin(), // That's it! Auto-discovers everything
],
});
`The plugin will:
- ✅ Auto-discover
src/fonts.ts (or alternatives)
- ✅ Auto-discover src/index.html
- ✅ Inject font CSS and preloads into HTML
- ✅ Generate optimized fonts in dist/assets$3
| Feature | Angular CLI Builder | Vite Plugin |
| ------------------ | ------------------- | --------------------- |
| Font Discovery | 4 locations checked | 4 locations checked |
| HTML Injection | ✅ Automatic | ✅ Automatic |
| Tailwind v4 | ✅ Supported | ✅ Supported |
| Tailwind v3 | ✅ Supported | ✅ Supported |
| CSS Variables | ✅ Generated | ✅ Generated |
| Preload Links | ✅ Injected | ✅ Injected |
| Configuration |
angular.json | vite.config.ts |
| Hot Reload | ❌ | ✅ Idempotent markers |
| Build Tool | Angular CLI | Vite |$3
The Vite plugin uses HTML comment markers to prevent duplicate injections during hot reloads:
`html
rel="preload"
href="assets/fonts/inter-400.woff2"
as="font"
type="font/woff2"
crossorigin
/>
`Running the plugin again will replace the content between markers, not add duplicates.
$3
`typescript
// Disable HTML injection (manual control)
angularFontPlugin({
injectHTML: false,
// Fonts still optimized, but you manually add to HTML
});// Disable Tailwind (use manual CSS variables)
angularFontPlugin({
injectTailwind: false,
// CSS variables still generated in fonts.css
});
// Minimal plugin (just optimize fonts)
angularFontPlugin({
injectHTML: false,
injectTailwind: false,
// Just generates fonts.css and font files
});
`Build-time Optimization
For optimal performance, use the Angular CLI builder to optimize fonts at build time.
$3
The font scanner looks for a single
fonts.ts file where all fonts are declared. This approach is:- ⚡️ Faster: Scans one file instead of entire project
- 📝 Explicit: Clear where fonts are declared
- 🎯 Organized: All font configuration in one place
Default locations checked (in order):
1.
src/fonts.ts
2. src/app/fonts.ts
3. src/lib/fonts.ts
4. src/config/fonts.tsSee
examples/fonts.ts.example for a complete example.$3
`json
{
"projects": {
"my-app": {
"architect": {
"font-optimize": {
"builder": "angular-fonts:optimize",
"options": {
"outputPath": "public",
"projectRoot": "",
"sourceRoot": "src",
"fontFile": "src/fonts.ts",
"injectTailwind": "v4"
}
}
}
}
}
}
`If
fontFile is not specified, the builder will search for fonts.ts in the default locations above.$3
`bash
ng build
`The builder will:
- Scan your
fonts.ts file for font declarations
- Download Google Fonts and copy local fonts to assets/fonts/
- Generate optimized CSS with local font paths
- Create preload links for critical fontsRuntime Usage
For dynamic font loading, use the services:
$3
`typescript
import { Component, inject } from "@angular/core";
import { GoogleFontService } from "angular-fonts/google";@Component({
selector: "app-dynamic",
template:
,
})
export class DynamicComponent {
private fontService = inject(GoogleFontService); font = this.fontService.loadFont("Inter", {
weights: [400, 700],
subsets: ["latin"],
});
}
`$3
`typescript
import { Component, inject } from "@angular/core";
import { LocalFontService } from "angular-fonts/local";@Component({
selector: "app-local",
template:
,
})
export class LocalComponent {
private fontService = inject(LocalFontService); font = this.fontService.loadFont({
src: "./fonts/my-font.woff2",
variable: "--font-local",
});
}
`API Reference
$3
`typescript
interface GoogleFontOptions {
weights?: number[] | "variable"; // Font weights (e.g., [400, 700] or "variable")
subsets?: string[]; // Font subsets (e.g., ['latin', 'latin-ext'])
styles?: string[]; // Font styles (e.g., ['normal', 'italic'])
display?: FontDisplay; // Font display strategy ('swap', 'block', 'fallback', 'optional')
preload?: boolean; // Whether to preload the font (default: true)
fallback?: string[]; // Fallback fonts (e.g., ['system-ui', 'sans-serif'])
adjustFontFallback?: boolean; // Generate fallback @font-face with metrics (default: true)
variable?: string; // CSS variable name for Tailwind (e.g., '--font-inter')
axes?: string[]; // Variable font axes (e.g., ['wght', 'ital']) // Advanced features
subset?: FontSubsetting; // Font subsetting configuration
variableAxes?: VariableFontAxes; // Custom variable font axes ranges
cdn?: CDNConfig; // Custom CDN configuration
retry?: RetryStrategy; // Retry strategy for network requests
onError?: (error: Error) => void; // Error callback
onRetry?: (attempt: number) => void; // Retry callback
}
interface FontSubsetting {
text?: string; // Specific text/characters to include (e.g., "Hello World 123")
unicodeRange?: string; // Custom unicode range (e.g., "U+0020-007F")
}
interface VariableFontAxes {
wght?: [number, number]; // Weight axis range [min, max]
slnt?: [number, number]; // Slant axis range [min, max]
wdth?: [number, number]; // Width axis range [min, max]
[axis: string]: [number, number] | undefined; // Custom axes (e.g., GRAD, opsz)
}
interface CDNConfig {
cssUrl?: string; // Base URL for CSS files (default: 'https://fonts.googleapis.com/css2')
fontUrl?: string; // Base URL for font files (default: 'https://fonts.gstatic.com')
}
interface RetryStrategy {
attempts?: number; // Number of retry attempts (default: 3)
backoff?: "linear" | "exponential"; // Backoff strategy (default: 'exponential')
delay?: number; // Initial delay in milliseconds (default: 100)
timeout?: number; // Request timeout in milliseconds (default: 5000)
maxDelay?: number; // Maximum delay in milliseconds (default: 5000)
}
`Note:
adjustFontFallback is enabled by default and automatically generates fallback font metrics to reduce layout shift during font loading.$3
`typescript
interface LocalFontOptions {
src:
| string
| Array<{
// Font source(s)
path: string;
weight?: string;
style?: string;
}>;
display?: FontDisplay; // Font display strategy ('swap', 'block', 'fallback', 'optional')
weight?: string; // Font weight
style?: string; // Font style
variable?: string; // CSS variable name for Tailwind (e.g., '--font-rubik')
preload?: boolean; // Whether to preload (default: true)
fallback?: string[]; // Fallback fonts (e.g., ['system-ui', 'sans-serif'])
adjustFontFallback?: "Arial" | "Times New Roman" | "Courier New" | false; // Generate fallback @font-face with metrics (default: auto-detected)
declarations?: Array<{
// Custom CSS declarations
prop: string;
value: string;
}>; // Advanced features
subset?: FontSubsetting; // Font subsetting configuration (requires fontkit)
retry?: RetryStrategy; // Retry strategy for file operations
onError?: (error: Error) => void; // Error callback
onRetry?: (attempt: number) => void; // Retry callback
fallbackFont?: string; // Fallback font to use on error (default: 'system-ui')
}
`Note:
adjustFontFallback for local fonts:- Default: Auto-detected based on font family name (sans-serif → Arial, serif → Times New Roman, mono → Courier New)
- Explicit: Specify
"Arial", "Times New Roman", or "Courier New" to override auto-detection
- Disabled: Set to false to skip fallback generation$3
`typescript
interface FontResult {
className: string; // CSS class name
style: {
// Inline style object
fontFamily: string;
fontWeight?: number;
fontStyle?: string;
};
variable?: string; // CSS variable for Tailwind
}
`Recent Improvements
- ✅ Font subsetting - Reduce file sizes by 80-90% with text/unicode range subsetting
- ✅ Variable font optimization - Full support for variable font axes (wght, slnt, wdth, custom)
- ✅ Custom CDN support - Self-host fonts on your own CDN infrastructure
- ✅ Advanced retry logic - Configurable retry strategies with exponential backoff and callbacks
- ✅ Automatic fallback metrics - Generates fallback @font-face declarations for both Google and local fonts to reduce CLS
- ✅ Local font fallback support - Auto-detects font category (sans/serif/mono) for optimal fallback selection
- ✅ Single font file scanning - Faster builds, explicit configuration
- ✅ 1000+ Google Fonts support - All fonts available via
font-data.json
- ✅ Better error messages - Helpful errors showing available options
- ✅ Network retry logic - Automatic retry with exponential backoff
- ✅ Improved font axes - Uses metadata for accurate weight ranges
- ✅ Better variant sorting - Handles complex "ital,wght" formats
- ✅ Build-time optimization - Fonts downloaded and self-hosted during build
- ✅ Browser-safe bundling - Build-time code separated from browser code to avoid Node.js dependencies in bundles
- ✅ Helper utilities - COMMON_CHARACTER_SETS and subsetting helpers for easier font optimizationAvailable Google Fonts
1000+ Google Fonts are available as individual functions, automatically generated from the latest Google Fonts metadata:
`typescript
import {
Inter,
Roboto,
Roboto_Mono,
Open_Sans,
Source_Sans_Pro,
Lato,
Montserrat,
Poppins,
Nunito,
Merriweather,
Playfair_Display,
Oswald,
Raleway,
Ubuntu,
Crimson_Text,
Fira_Sans,
PT_Sans,
PT_Serif,
Droid_Sans,
Droid_Serif,
Lora,
Libre_Baskerville,
Cabin,
Arimo,
Titillium_Web,
Work_Sans,
Cormorant_Garamond,
Libre_Franklin,
Encode_Sans,
IBM_Plex_Sans,
IBM_Plex_Serif,
IBM_Plex_Mono,
Space_Mono,
Inconsolata,
Fira_Code,
JetBrains_Mono,
Source_Code_Pro,
Cascadia_Code,
Victor_Mono,
Recursive,
Fraunces,
Bitter,
Crimson_Pro,
Literata,
Chivo,
Spectral,
Karla,
Rubik,
Quicksand,
Maven_Pro,
Exo_2,
Orbitron,
Rajdhani,
Righteous,
Bangers,
Fredoka_One,
Comfortaa,
Varela_Round,
Nunito_Sans,
Source_Sans_3,
Noto_Sans,
Noto_Serif,
// ... and many more!
} from "angular-fonts/google";
`Fallback Font Metrics (Zero Layout Shift)
This package automatically generates fallback font metrics to reduce Cumulative Layout Shift (CLS) during font loading. Both Google Fonts and local fonts support automatic fallback generation with size-adjust properties that make system fonts match your web font dimensions.
$3
For each font (Google or local), the package:
1. Analyzes the font family to determine the best fallback (Arial, Times New Roman, or Courier New)
2. Generates a fallback
@font-face with override metrics
3. Injects it into your fonts.css automaticallyFont Detection:
- Sans-serif fonts (Inter, Roboto, Open Sans, Rubik, etc.) → Arial fallback
- Serif fonts (Playfair Display, Merriweather, etc.) → Times New Roman fallback
- Monospace fonts (Fira Code, JetBrains Mono, etc.) → Courier New fallback
$3
`css
/ Your main font /
@font-face {
font-family: "Inter";
src: url(/assets/fonts/inter/...) format("woff2");
}/ Automatically generated fallback /
@font-face {
font-family: "Inter Fallback";
src: local("Arial");
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
size-adjust: 100%;
}
`$3
Fallback metrics are enabled by default for both Google and local fonts.
Google Fonts:
`typescript
export const inter = Inter({
subsets: ["latin"],
weights: [400, 700],
adjustFontFallback: false, // Disable fallback metrics
});
`Local Fonts:
`typescript
export const rubik = localFont({
src: "./fonts/Rubik-Regular.ttf",
// Auto-detected as sans-serif → uses Arial fallback
});export const customSerif = localFont({
src: "./fonts/MySerif.ttf",
adjustFontFallback: "Times New Roman", // Explicitly specify fallback
});
export const noFallback = localFont({
src: "./fonts/Special.woff2",
adjustFontFallback: false, // Disable fallback metrics
});
`$3
In your CSS or Tailwind config, reference the fallback font:
`css
.font-inter {
font-family: "Inter", "Inter Fallback", system-ui, sans-serif;
}
`This ensures the fallback font is used while the web font loads, minimizing layout shift.
Helper Utilities
$3
For font subsetting, use predefined character sets:
`typescript
import { COMMON_CHARACTER_SETS } from "angular-fonts/google";// Basic Latin characters (A-Z, a-z, 0-9, basic punctuation)
COMMON_CHARACTER_SETS.basicLatin;
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,!?;:"
// Extended Latin with accents
COMMON_CHARACTER_SETS.latinExtended;
// "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ"
// Numbers and common symbols
COMMON_CHARACTER_SETS.numbers;
// "0123456789+-=()[]{}.,!?;:'\"@#$%^&*"
// Common punctuation
COMMON_CHARACTER_SETS.punctuation;
// ".,!?;:'\"()[]{}/\\-_=+*&^%$#@~
|<>"$3
`typescript
import { extractUniqueCharacters } from "angular-fonts/google";// Extract unique characters from text
const uniqueChars = extractUniqueCharacters("Hello World!");
// "Helo Wrd!"
// Use in font subsetting
export const interUnique = Inter({
weights: [400, 700],
subsets: ["latin"],
subset: {
text: extractUniqueCharacters("Your specific text here"),
},
variable: "--font-inter-unique",
});
`Performance Tips
1. Use build-time optimization for production builds
2. Preload critical fonts by setting
preload: true
3. Use CSS variables for Tailwind integration
4. Limit font weights to only what you need
5. Use font subsets to reduce file sizes by 80-90%
6. Enable fallback metrics (default) to reduce layout shift
7. Use variable fonts when possible for better performance
8. Configure retry strategies for network resilience
9. Self-host fonts with custom CDN for faster delivery
10. Subset fonts using COMMON_CHARACTER_SETS for common use casesTroubleshooting
$3
- Check that the font name is correct
- Verify network connectivity for Google Fonts
- Ensure local font files exist and are accessible
$3
- Make sure
@angular/ssr is properly configured
- Check that font preloads are injected in server.ts
- Verify font CSS is included in SSR output$3
- Ensure CSS variables are added to your root element
- Check Tailwind configuration includes the font variables
- Verify font functions return the
variable propertyTesting
This package includes a comprehensive test suite with 81+ tests covering core functionality.
$3
`bash
Run all tests
pnpm testRun with coverage report
pnpm test:coverageRun in watch mode
pnpm test:watchRun only unit tests
pnpm test:unitRun only integration tests
pnpm test:integration
`$3
- Core utilities: 90% coverage
- Google Fonts utils: 76-100% coverage
- Overall: 32% coverage (target: 80%)
See TESTING.md for detailed testing documentation.
$3
Automated tests run on every push via GitHub Actions:
- Tests on Node.js 18.x and 20.x
- Coverage reports uploaded to Codecov
- Build verification
Development
$3
`bash
Build the package
pnpm buildBuild with font generation (automatic)
pnpm prebuild && pnpm build
`The build process automatically:
1. Generates font exports from
font-data.json (1890+ fonts)
2. Compiles TypeScript to JavaScript
3. Creates type definitions$3
When Google Fonts metadata is updated:
1. Update
src/google/font-data.json with new font metadata
2. Run pnpm generate:fonts to regenerate exports
3. All new fonts are immediately available for import`bash
Manually regenerate font exports
pnpm generate:fonts
`This creates ~3800 lines of TypeScript exports in
src/google/fonts.ts:`typescript
export const Inter = fontFunctions.Inter;
export const Roboto_Mono = fontFunctions["Roboto_Mono"];
// ... 1890+ more fonts
`See scripts/README.md for details on the generation process.
Contributing
Contributions are welcome! Please read our contributing guide for details.
When submitting PRs:
1. Add tests for new functionality
2. Ensure all tests pass:
pnpm test
3. Maintain or improve coverage: pnpm test:coverage
4. Follow existing code style
5. If adding fonts, run pnpm generate:fonts` to update exportsMIT License - see LICENSE for details.