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.
Choose Font Preset:
Select a preset...
{{preset.name}} - {{preset.description}}
Transition Effect:
Fade
Scale
Typewriter
Apply with {{selectedTransition | titlecase}} Effect
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}}
Apply {{preset.name}}
Popular Individual Fonts
Load individual fonts to test and preview them.
0">
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);
}
}
}