import { Component, OnInit, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { Subject, takeUntil } from 'rxjs'; // Mock font combinations since ui-font-manager is not yet available const FONT_COMBINATIONS = { 'Modern Clean': { sans: 'Inter', serif: 'Playfair Display', mono: 'JetBrains Mono', display: 'Inter' }, 'Classic Editorial': { sans: 'Source Sans Pro', serif: 'Lora', mono: 'Source Code Pro', display: 'Source Sans Pro' }, 'Friendly Modern': { sans: 'Poppins', serif: 'Merriweather', mono: 'Fira Code', display: 'Poppins' }, 'Professional': { sans: 'Roboto', serif: 'EB Garamond', mono: 'Roboto Mono', display: 'Roboto' } }; @Component({ selector: 'app-font-manager-demo', standalone: true, imports: [CommonModule, FormsModule], template: `

UI Font Manager Demo

Dynamically load Google Fonts and switch between font themes with smooth transitions. Select a font preset to apply it globally to the entire application.

Font Preset Selection

Choose from curated font combinations. The selected preset will be applied to the entire application.

Currently Applied: {{currentPreset.name}}

Sans-serif: {{currentPreset.fonts.sans}}
Serif: {{currentPreset.fonts.serif}}
Monospace: {{currentPreset.fonts.mono}}
Display: {{currentPreset.fonts.display}}

Typography Preview

See how the selected fonts look in a real application context.

Display Heading (H1)

Sans-serif Heading (H2)

Serif Heading (H3)

This is a paragraph using the sans-serif font. It demonstrates how readable body text appears with the currently selected font combination. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

"This is a quote using the serif font, which often provides better readability for longer text passages and adds elegance to quoted content."
Current fonts: Sans (var(--font-family-sans)) | Serif (var(--font-family-serif)) | Mono (var(--font-family-mono)) | Display (var(--font-family-display))

Available Font Presets

{{preset.name}}

{{preset.description}}

Sans: {{preset.fonts.sans}}
Serif: {{preset.fonts.serif}}
Mono: {{preset.fonts.mono}}
Display: {{preset.fonts.display}}

Popular Individual Fonts

Load individual fonts to test and preview them.

Event Log

{{event.time}} {{event.message}}
`, styleUrl: './font-manager-demo.component.scss' }) export class FontManagerDemoComponent implements OnInit, OnDestroy { popularFonts = ['Inter', 'Roboto', 'Playfair Display', 'JetBrains Mono', 'Montserrat', 'Lora']; loadedFonts = new Set(); // Font preset management fontPresets = [ { key: 'modern-clean', name: 'Modern Clean', description: 'Contemporary and minimal design with Inter and Playfair Display', fonts: FONT_COMBINATIONS['Modern Clean'] }, { key: 'classic-editorial', name: 'Classic Editorial', description: 'Traditional publishing style with Source Sans Pro and Lora', fonts: FONT_COMBINATIONS['Classic Editorial'] }, { key: 'friendly-modern', name: 'Friendly Modern', description: 'Approachable and warm with Poppins and Merriweather', fonts: FONT_COMBINATIONS['Friendly Modern'] }, { key: 'professional', name: 'Professional', description: 'Corporate and reliable with Roboto and EB Garamond', fonts: FONT_COMBINATIONS['Professional'] }, { key: 'system', name: 'System Default', description: 'Use system fonts for optimal performance', fonts: { sans: 'system-ui', serif: 'ui-serif', mono: 'ui-monospace', display: 'system-ui' } } ]; selectedPreset: string = ''; selectedTransition: 'fade' | 'scale' | 'typewriter' = 'fade'; currentPreset: any = null; eventLog: Array<{time: string, message: string, type: 'success' | 'error' | 'info'}> = []; private destroy$ = new Subject(); constructor() {} ngOnInit(): void { this.logEvent('Component initialized', 'info'); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } onThemeChanged(themeName: string): void { this.logEvent(`Theme changed to: ${themeName}`, 'success'); } onFontLoaded(fontName: string): void { this.logEvent(`Font loaded: ${fontName}`, 'success'); this.loadedFonts.add(fontName); } onFontError(event: {fontName: string, error: string}): void { this.logEvent(`Font error - ${event.fontName}: ${event.error}`, 'error'); } loadSingleFont(fontName: string): void { this.logEvent(`Loading font: ${fontName}`, 'info'); // Load font via Google Fonts API this.loadGoogleFont(fontName); this.loadedFonts.add(fontName); this.logEvent(`Successfully loaded: ${fontName}`, 'success'); } applyTheme(themeName: string): void { this.logEvent(`Applying theme: ${themeName}`, 'info'); this.logEvent(`Successfully applied theme: ${themeName}`, 'success'); } isLoaded(fontName: string): boolean { return this.loadedFonts.has(fontName); } private loadGoogleFont(fontName: string): void { const link = document.createElement('link'); link.href = `https://fonts.googleapis.com/css2?family=${fontName.replace(' ', '+')}&display=swap`; link.rel = 'stylesheet'; document.head.appendChild(link); } onPresetChange(event: Event): void { const target = event.target as HTMLSelectElement; this.selectedPreset = target.value; if (this.selectedPreset) { const preset = this.fontPresets.find(p => p.key === this.selectedPreset); if (preset) { this.logEvent(`Selected preset: ${preset.name}`, 'info'); } } } applyPreset(presetKey: string): void { const preset = this.fontPresets.find(p => p.key === presetKey); if (!preset) { this.logEvent(`Preset not found: ${presetKey}`, 'error'); return; } this.selectedPreset = presetKey; this.logEvent(`Applying preset: ${preset.name}`, 'info'); // Apply fonts to document root for global effect this.applyFontsGlobally(preset.fonts); this.currentPreset = preset; } applyPresetWithTransition(): void { const preset = this.fontPresets.find(p => p.key === this.selectedPreset); if (!preset) return; this.logEvent(`Applying preset "${preset.name}" with ${this.selectedTransition} transition`, 'info'); // Apply fonts with transition effect const fontVariables = { '--font-family-sans': this.buildGlobalFontStack(preset.fonts.sans), '--font-family-serif': this.buildGlobalFontStack(preset.fonts.serif), '--font-family-mono': this.buildGlobalFontStack(preset.fonts.mono), '--font-family-display': this.buildGlobalFontStack(preset.fonts.display) }; this.applyFontsWithTransition(fontVariables, this.selectedTransition); this.currentPreset = preset; } private applyFontsGlobally(fonts: any): void { const root = document.documentElement; // Set CSS custom properties on the document root root.style.setProperty('--font-family-sans', this.buildGlobalFontStack(fonts.sans)); root.style.setProperty('--font-family-serif', this.buildGlobalFontStack(fonts.serif)); root.style.setProperty('--font-family-mono', this.buildGlobalFontStack(fonts.mono)); root.style.setProperty('--font-family-display', this.buildGlobalFontStack(fonts.display)); // Also load the fonts if they're Google Fonts if (fonts.sans && fonts.sans !== 'system-ui') { this.loadSingleFont(fonts.sans); } if (fonts.serif && fonts.serif !== 'ui-serif') { this.loadSingleFont(fonts.serif); } if (fonts.mono && fonts.mono !== 'ui-monospace') { this.loadSingleFont(fonts.mono); } if (fonts.display && fonts.display !== 'system-ui') { this.loadSingleFont(fonts.display); } this.logEvent(`Applied fonts globally to document root`, 'success'); } private applyFontsWithTransition(fontVariables: Record, transitionType: 'fade' | 'scale' | 'typewriter'): void { const root = document.documentElement; // Add transition class root.classList.add(`font-transition-${transitionType}`); // Apply font changes Object.entries(fontVariables).forEach(([property, value]) => { root.style.setProperty(property, value); }); // Remove transition class after animation setTimeout(() => { root.classList.remove(`font-transition-${transitionType}`); this.logEvent(`${transitionType} transition completed`, 'success'); }, 1000); } private buildGlobalFontStack(fontName: string): string { // Handle system fonts if (fontName === 'system-ui') { return 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'; } if (fontName === 'ui-serif') { return 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif'; } if (fontName === 'ui-monospace') { return 'ui-monospace, SFMono-Regular, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'; } // For Google Fonts, add appropriate fallbacks const fontMap: Record = { 'Inter': `"Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`, 'Roboto': `"Roboto", Arial, Helvetica, sans-serif`, 'Poppins': `"Poppins", Arial, Helvetica, sans-serif`, 'Source Sans Pro': `"Source Sans Pro", Arial, Helvetica, sans-serif`, 'Montserrat': `"Montserrat", Arial, Helvetica, sans-serif`, 'Playfair Display': `"Playfair Display", Georgia, "Times New Roman", serif`, 'Lora': `"Lora", Georgia, "Times New Roman", serif`, 'Merriweather': `"Merriweather", Georgia, "Times New Roman", serif`, 'EB Garamond': `"EB Garamond", Georgia, "Times New Roman", serif`, 'JetBrains Mono': `"JetBrains Mono", Consolas, Monaco, "Courier New", monospace`, 'Source Code Pro': `"Source Code Pro", Consolas, Monaco, "Courier New", monospace`, 'Fira Code': `"Fira Code", Consolas, Monaco, "Courier New", monospace`, 'Roboto Mono': `"Roboto Mono", Consolas, Monaco, "Courier New", monospace`, 'Oswald': `"Oswald", Impact, "Arial Black", sans-serif`, 'Raleway': `"Raleway", Arial, Helvetica, sans-serif` }; return fontMap[fontName] || `"${fontName}", sans-serif`; } private logEvent(message: string, type: 'success' | 'error' | 'info'): void { const time = new Date().toLocaleTimeString(); this.eventLog.push({ time, message, type }); // Keep only last 50 events if (this.eventLog.length > 50) { this.eventLog = this.eventLog.slice(-50); } } }