Performance: Mobile-Optimierungen für besseren PageSpeed

- Portrait-Bild von 1.2MB auf ~20KB optimiert (300px/600px + WebP)
- Hero-Animation entfernt für schnelleren LCP
- WebP srcset für alle Projekt-Bilder (6 Größen: 200-1200px)
- Korrekte sizes-Attribute für Mobile (350px statt 1200px)
- Supabase lazy laden via dynamischen Import
- Admin-Check verzögert mit requestIdleCallback
- Hauptbundle von 461KB auf 358KB reduziert

🤖 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 22:02:57 +01:00
co-authored by Claude Opus 4.5
parent 0145beeac5
commit 4b9e457f4a
145 changed files with 96 additions and 49 deletions
+30 -15
View File
@@ -7,7 +7,8 @@ import { existsSync } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const SIZES = [400, 800, 1200];
// Mehr Größen für bessere Mobile-Unterstützung
const SIZES = [200, 300, 400, 600, 800, 1200];
const INPUT_DIR = join(__dirname, '../source-images/projects');
const OUTPUT_DIR = join(__dirname, '../public/images/projects');
@@ -15,19 +16,16 @@ async function processImage(inputPath, projectSlug) {
const filename = basename(inputPath, extname(inputPath));
const ext = extname(inputPath).toLowerCase();
// Skip non-image files
if (!['.jpg', '.jpeg', '.png', '.webp'].includes(ext)) {
return;
}
const outputDir = join(OUTPUT_DIR, projectSlug);
// Create output directory if it doesn't exist
await mkdir(outputDir, { recursive: true });
console.log(`Processing: ${projectSlug}/${filename}${ext}`);
// Create optimized original size (cover.jpg)
// Optimierte Originalversion (cover.jpg)
const originalOutputPath = join(outputDir, `${filename}.jpg`);
try {
await sharp(inputPath)
@@ -38,7 +36,7 @@ async function processImage(inputPath, projectSlug) {
console.error(` Error creating optimized original:`, error.message);
}
// Create different sizes
// JPG-Versionen in verschiedenen Größen
for (const width of SIZES) {
const outputFilename = `${filename}-${width}w.jpg`;
const outputPath = join(outputDir, outputFilename);
@@ -58,18 +56,35 @@ async function processImage(inputPath, projectSlug) {
}
}
// Create WebP version of original size
const webpFilename = `${filename}.webp`;
const webpPath = join(outputDir, webpFilename);
// WebP-Versionen in verschiedenen Größen
for (const width of SIZES) {
const webpFilename = `${filename}-${width}w.webp`;
const webpPath = join(outputDir, webpFilename);
try {
await sharp(inputPath)
.resize(width, null, {
withoutEnlargement: true,
fit: 'inside'
})
.webp({ quality: 75 })
.toFile(webpPath);
console.log(` Created: ${webpFilename}`);
} catch (error) {
console.error(` Error creating ${webpFilename}:`, error.message);
}
}
// Volle WebP-Version
const webpFullPath = join(outputDir, `${filename}.webp`);
try {
await sharp(inputPath)
.webp({ quality: 80 })
.toFile(webpPath);
console.log(` Created: ${webpFilename}`);
.toFile(webpFullPath);
console.log(` Created: ${filename}.webp`);
} catch (error) {
console.error(` Error creating WebP:`, error.message);
console.error(` Error creating full WebP:`, error.message);
}
}
@@ -87,7 +102,6 @@ async function processDirectory(dirPath) {
const fullPath = join(dirPath, entry.name);
if (entry.isDirectory()) {
// Check for cover.jpg in this project directory
const coverPath = join(fullPath, 'cover.jpg');
if (existsSync(coverPath)) {
await processImage(coverPath, entry.name);
@@ -101,7 +115,8 @@ async function processDirectory(dirPath) {
async function main() {
console.log('Starting image optimization...');
console.log(`Source: ${INPUT_DIR}`);
console.log(`Output: ${OUTPUT_DIR}\n`);
console.log(`Output: ${OUTPUT_DIR}`);
console.log(`Sizes: ${SIZES.join(', ')}px\n`);
try {
await processDirectory(INPUT_DIR);