# Laravel Google Fonts This Laravel package enables self-hosting Google Fonts with minimal friction, eliminating the need for external DNS lookups and reducing privacy concerns. When fonts are requested for the first time, the package automatically scrapes the CSS from Google Fonts, downloads the font files, stores them locally, and serves them inline or via local URLs. The package handles WOFF2 font formats, supports multiple font configurations, and provides automatic fallback to Google's CDN if local hosting fails. The core functionality revolves around a Blade directive `@googlefonts` that intelligently manages font loading. Fonts are stored in Laravel's filesystem (default: public disk), and the package automatically localizes all font URLs in the CSS. It supports CSP nonce attributes for security, preload tags for performance optimization, and customizable user agents for controlling which font formats are downloaded from Google's servers. ## APIs and Key Functions ### @googlefonts Blade Directive Renders Google Fonts in Blade templates by loading configured fonts from local storage or fetching them from Google Fonts API on first use. ```blade {{-- resources/views/layouts/app.blade.php --}} My Application {{-- Load the default font (Inter) inline --}} @googlefonts {{-- Load a named font configuration (IBM Plex Mono for code) --}} @googlefonts('code') {{-- Load with CSP nonce for Content Security Policy --}} @googlefonts(['nonce' => csp_nonce()]) {{-- Load named font with nonce --}} @googlefonts(['font' => 'code', 'nonce' => csp_nonce()])

Welcome

const hello = 'world'; ``` ### Configuration Setup Define Google Fonts URLs and package behavior in the configuration file to control font loading, storage, and rendering. ```php [ 'default' => 'https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400;0,700;1,400;1,700&display=swap', 'code' => 'https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,700;1,400&display=swap', 'display' => 'https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700;900&display=swap', ], // Storage disk (must be publicly accessible) 'disk' => 'public', // Path prefix for font files 'path' => 'fonts', // Inline CSS to reduce HTTP requests (true) or use tag (false) 'inline' => true, // Generate preload tags for faster font loading 'preload' => false, // Fallback to Google's CDN on errors (disable in debug mode to see exceptions) 'fallback' => ! env('APP_DEBUG'), // User agent determines font format (WOFF2, WOFF, TTF, etc.) 'user_agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15', ]; ``` ### GoogleFonts::load() Method Loads a font configuration and returns a Fonts object with methods for rendering HTML tags or retrieving URLs. ```php googleFonts->load(); // Load named font $codeFonts = $this->googleFonts->load('code'); // Load with options array $secureFonts = $this->googleFonts->load([ 'font' => 'display', 'nonce' => 'random-nonce-value' ]); // Force re-download (useful for updates) $freshFonts = $this->googleFonts->load('default', forceDownload: true); return [ 'default_inline' => (string) $defaultFonts->inline(), 'code_link' => (string) $codeFonts->link(), 'display_url' => $secureFonts->url(), 'fallback' => (string) $freshFonts->fallback(), ]; } } ``` ### Fonts Rendering Methods The Fonts class provides multiple rendering options for including font stylesheets in HTML with different strategies. ```php load('default'); // Inline CSS (fastest, no additional HTTP request) $inlineHtml = $fonts->inline(); // Output: // External stylesheet link $linkHtml = $fonts->link(); // Output: // Fallback to Google's CDN (used when local hosting fails) $fallbackHtml = $fonts->fallback(); // Output: // Get direct URL to local CSS file $cssUrl = $fonts->url(); // Output: https://example.com/storage/fonts/952ee985ef/fonts.css // Automatic rendering based on config (inline or link) $autoHtml = $fonts->toHtml(); // Output depends on 'inline' config setting // With nonce for Content Security Policy $secureInline = app(GoogleFonts::class)->load(['font' => 'default', 'nonce' => 'abc123'])->inline(); // Output: echo $inlineHtml; ``` ### Artisan Command: google-fonts:fetch Pre-download all configured fonts to local storage before deployment or during build process to avoid first-request delays. ```bash # Fetch all fonts defined in config/google-fonts.php php artisan google-fonts:fetch # Example output: # Start fetching Google Fonts... # Fetching `default`... # Fetching `code`... # Fetching `display`... # All done! # Integrate into deployment script cat > deploy.sh << 'EOF' #!/bin/bash composer install --no-dev --optimize-autoloader php artisan config:cache php artisan route:cache php artisan view:cache php artisan google-fonts:fetch php artisan storage:link EOF # Use in CI/CD pipeline (GitHub Actions example) cat > .github/workflows/deploy.yml << 'EOF' name: Deploy on: [push] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install dependencies run: composer install - name: Cache Google Fonts run: php artisan google-fonts:fetch - name: Deploy to server run: ./deploy.sh EOF ``` ### Package Installation Install and configure the package in a Laravel application with optional configuration publishing. ```bash # Install via Composer composer require spatie/laravel-google-fonts # Publish configuration file (optional) php artisan vendor:publish --provider="Spatie\GoogleFonts\GoogleFontsServiceProvider" --tag="google-fonts-config" # Create storage link for public disk php artisan storage:link # Verify storage/app/public/fonts directory is writable ls -la storage/app/public/ # Test font loading in tinker php artisan tinker >>> app(\Spatie\GoogleFonts\GoogleFonts::class)->load('default'); >>> exit ``` ### Advanced Configuration Examples Customize font loading behavior for different environments, CDN hosting, and legacy browser support. ```php [ 'default' => 'https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap', 'headings' => 'https://fonts.googleapis.com/css2?family=Montserrat:wght@600;700;800&display=swap', ], // Use custom disk for CDN deployment 'disk' => env('GOOGLE_FONTS_DISK', 'public'), // Custom path for multi-tenant applications 'path' => 'assets/fonts/' . config('app.tenant_id'), // External links in production, inline in development 'inline' => env('APP_ENV') === 'local', // Enable preload in production only 'preload' => env('APP_ENV') === 'production', // Always fallback in production, throw exceptions in development 'fallback' => env('APP_ENV') === 'production', // Legacy browser support (WOFF instead of WOFF2) 'user_agent' => env('LEGACY_BROWSER_SUPPORT', false) ? 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15', ]; ``` ### Custom Disk Configuration for CDN Configure a custom filesystem disk to serve fonts from a CDN like AWS CloudFront or DigitalOcean Spaces. ```php [ // ... other disks 'fonts-cdn' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_FONTS_BUCKET'), 'url' => env('AWS_CLOUDFRONT_URL'), 'endpoint' => env('AWS_ENDPOINT'), 'visibility' => 'public', ], ], // config/google-fonts.php return [ 'disk' => 'fonts-cdn', 'path' => 'fonts', 'inline' => false, // Use links for CDN 'preload' => true, // Preload from CDN // ... other settings ]; ``` ### Programmatic Font Loading in Services Use the GoogleFonts service directly in application code for dynamic font management or API responses. ```php 'default', 'classic' => 'display', 'code' => 'code', default => 'default', }; return Cache::remember("theme_font_{$theme}", 3600, function () use ($fontKey) { try { $fonts = $this->googleFonts->load($fontKey); return (string) $fonts->inline(); } catch (\Exception $e) { report($e); return $this->googleFonts->load($fontKey)->fallback(); } }); } public function preloadAllThemeFonts(): void { $themes = ['modern', 'classic', 'code']; foreach ($themes as $theme) { $fontKey = match($theme) { 'modern' => 'default', 'classic' => 'display', 'code' => 'code', }; $this->googleFonts->load($fontKey, forceDownload: true); } } } ``` ### Testing Font Loading Example test cases demonstrating font loading verification and snapshot testing for rendered output. ```php load('default', forceDownload: true); // Verify CSS file exists Storage::disk('public')->assertExists('fonts/952ee985ef/fonts.css'); // Verify font files downloaded $files = Storage::disk('public')->allFiles('fonts/952ee985ef'); $woff2Files = array_filter($files, fn($f) => str_ends_with($f, '.woff2')); $this->assertGreaterThan(0, count($woff2Files)); // Verify HTML output $inlineHtml = (string) $fonts->inline(); $this->assertStringContainsString('