Performance: Inter-Font auf 60KB Subset reduziert (vorher 344KB)

- Font Subsetting mit fonttools für Latin + German Zeichen
- 83% Reduktion der Font-Datei
- PageSpeed API Script hinzugefügt

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-05 23:05:09 +01:00
co-authored by Claude Opus 4.5
parent 61c7c523ab
commit 090d640e9a
4 changed files with 190 additions and 4 deletions
+5 -4
View File
@@ -107,13 +107,14 @@
} }
} }
/* Font-Loading Optimierung */ /* Font-Loading Optimierung - Subset für Latin + German */
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: normal; font-style: normal;
font-weight: 100 900; font-weight: 100 900;
font-display: swap; font-display: swap;
src: url('/fonts/inter-var.woff2') format('woff2-variations'); src: url('/fonts/inter-subset.woff2') format('woff2-variations');
unicode-range: U+0020-007E, U+00A0-00FF, U+0100-017F, U+2013-2014, U+2018-201A, U+201C-201E, U+2026, U+20AC;
} }
body { body {
@@ -121,8 +122,8 @@
} }
</style> </style>
<!-- Preload Font --> <!-- Preload Font (60KB subset statt 344KB vollständig) -->
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/fonts/inter-subset.woff2" as="font" type="font/woff2" crossorigin>
<!-- Preload wichtiger Assets --> <!-- Preload wichtiger Assets -->
<link rel="preload" href="/portrait-224.webp" as="image" type="image/webp" fetchpriority="high" media="(max-width: 640px)" /> <link rel="preload" href="/portrait-224.webp" as="image" type="image/webp" fetchpriority="high" media="(max-width: 640px)" />
Binary file not shown.
Binary file not shown.
+185
View File
@@ -0,0 +1,185 @@
#!/usr/bin/env node
/**
* PageSpeed Insights API Check
* Prüft die Performance einer URL mit der Google PageSpeed Insights API
*/
import https from 'https';
// Disable SSL verification for local testing (network proxy issue)
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
const API_KEY = 'AIzaSyA4qo0LfLy_ps-SyTQTQpFrFRjDnAUn_K4';
const URL_TO_TEST = 'https://www.damjan-savic.com/';
function httpsGet(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch (e) {
reject(new Error(`JSON Parse Error: ${e.message}`));
}
});
}).on('error', reject);
});
}
async function runPageSpeedTest(url, strategy = 'mobile') {
const apiUrl = `https://www.pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodeURIComponent(url)}&key=${API_KEY}&strategy=${strategy}&category=performance&category=accessibility&category=best-practices&category=seo`;
console.log(`\n🔍 Analysiere ${url} (${strategy})...\n`);
console.log('⏳ Bitte warten, die Analyse kann 30-60 Sekunden dauern...\n');
try {
const data = await httpsGet(apiUrl);
if (data.error) {
throw new Error(`API Error: ${data.error.message}`);
}
return data;
} catch (error) {
console.error('❌ Fehler bei der API-Anfrage:', error.message);
throw error;
}
}
function formatScore(score) {
const percentage = Math.round(score * 100);
if (percentage >= 90) return `\x1b[32m${percentage}\x1b[0m`; // Grün
if (percentage >= 50) return `\x1b[33m${percentage}\x1b[0m`; // Gelb
return `\x1b[31m${percentage}\x1b[0m`; // Rot
}
function formatTime(ms) {
if (ms < 1000) return `${Math.round(ms)}ms`;
return `${(ms / 1000).toFixed(1)}s`;
}
function displayResults(data, strategy) {
const lighthouse = data.lighthouseResult;
const categories = lighthouse.categories;
const audits = lighthouse.audits;
console.log('═'.repeat(60));
console.log(`📊 PageSpeed Insights Ergebnis (${strategy.toUpperCase()})`);
console.log('═'.repeat(60));
// Scores
console.log('\n📈 SCORES:');
console.log('─'.repeat(40));
console.log(` Performance: ${formatScore(categories.performance.score)}`);
console.log(` Barrierefreiheit: ${formatScore(categories.accessibility.score)}`);
console.log(` Best Practices: ${formatScore(categories['best-practices'].score)}`);
console.log(` SEO: ${formatScore(categories.seo.score)}`);
// Core Web Vitals
console.log('\n⚡ CORE WEB VITALS:');
console.log('─'.repeat(40));
const fcp = audits['first-contentful-paint'];
const lcp = audits['largest-contentful-paint'];
const tbt = audits['total-blocking-time'];
const cls = audits['cumulative-layout-shift'];
const si = audits['speed-index'];
console.log(` First Contentful Paint (FCP): ${formatTime(fcp.numericValue)}`);
console.log(` Largest Contentful Paint (LCP): ${formatTime(lcp.numericValue)}`);
console.log(` Total Blocking Time (TBT): ${formatTime(tbt.numericValue)}`);
console.log(` Cumulative Layout Shift (CLS): ${cls.displayValue}`);
console.log(` Speed Index: ${formatTime(si.numericValue)}`);
// Opportunities
const opportunities = Object.values(audits)
.filter(audit => audit.details?.type === 'opportunity' && audit.details?.overallSavingsMs > 0)
.sort((a, b) => (b.details?.overallSavingsMs || 0) - (a.details?.overallSavingsMs || 0))
.slice(0, 5);
if (opportunities.length > 0) {
console.log('\n💡 OPTIMIERUNGSMÖGLICHKEITEN:');
console.log('─'.repeat(40));
opportunities.forEach(opp => {
const savings = opp.details?.overallSavingsMs;
console.log(`${opp.title}`);
if (savings) {
console.log(` Potenzielle Einsparung: ${formatTime(savings)}`);
}
});
}
// Diagnostics
const diagnostics = Object.values(audits)
.filter(audit => audit.details?.type === 'table' && audit.score !== null && audit.score < 0.9)
.slice(0, 5);
if (diagnostics.length > 0) {
console.log('\n🔧 DIAGNOSE:');
console.log('─'.repeat(40));
diagnostics.forEach(diag => {
const scoreIcon = diag.score >= 0.5 ? '⚠️' : '❌';
console.log(` ${scoreIcon} ${diag.title}`);
});
}
// Passed Audits Count
const passedAudits = Object.values(audits).filter(a => a.score === 1).length;
const totalAudits = Object.values(audits).filter(a => a.score !== null).length;
console.log('\n✅ BESTANDENE PRÜFUNGEN:');
console.log('─'.repeat(40));
console.log(` ${passedAudits} von ${totalAudits} Prüfungen bestanden`);
console.log('\n' + '═'.repeat(60));
return {
performance: Math.round(categories.performance.score * 100),
accessibility: Math.round(categories.accessibility.score * 100),
bestPractices: Math.round(categories['best-practices'].score * 100),
seo: Math.round(categories.seo.score * 100),
lcp: lcp.numericValue,
fcp: fcp.numericValue,
tbt: tbt.numericValue,
cls: parseFloat(cls.displayValue)
};
}
async function main() {
console.log('\n🚀 PageSpeed Insights API Test');
console.log('================================\n');
console.log(`URL: ${URL_TO_TEST}`);
console.log(`API Key: ${API_KEY.substring(0, 10)}...`);
try {
// Mobile Test
const mobileData = await runPageSpeedTest(URL_TO_TEST, 'mobile');
const mobileResults = displayResults(mobileData, 'mobile');
// Desktop Test
console.log('\n\n');
const desktopData = await runPageSpeedTest(URL_TO_TEST, 'desktop');
const desktopResults = displayResults(desktopData, 'desktop');
// Summary
console.log('\n\n📋 ZUSAMMENFASSUNG:');
console.log('═'.repeat(60));
console.log(`\n 📱 Mobile:`);
console.log(` Performance: ${formatScore(mobileResults.performance / 100)}`);
console.log(` LCP: ${formatTime(mobileResults.lcp)}`);
console.log(` TBT: ${formatTime(mobileResults.tbt)}`);
console.log(`\n 🖥️ Desktop:`);
console.log(` Performance: ${formatScore(desktopResults.performance / 100)}`);
console.log(` LCP: ${formatTime(desktopResults.lcp)}`);
console.log(` TBT: ${formatTime(desktopResults.tbt)}`);
console.log('\n' + '═'.repeat(60));
} catch (error) {
console.error('\n❌ Test fehlgeschlagen:', error.message);
process.exit(1);
}
}
main();