Website djnizz.at: Hero- und About-Sektion mit Docker-Deployment
Astro 7 mit Tailwind 4 und Node-Adapter. Alle Seiten werden vorgerendert, der Node-Server ist dabei, damit spaeter einzelne Routen (Booking-Formular) ohne Umbau am Deployment dynamisch werden koennen. Inhalt: - Fullscreen-Hero mit freigestelltem Render, animierten Lasern und Genre-Laufband; Typografie als HTML statt eingebranntem Bildtext - About-Sektion mit Genre-Karten und Eckdaten - Impressum und Datenschutzerklaerung - Zentrale Inhaltsdatei src/data/site.ts Technik: - Selbst gehostete Schriften, keine Cookies, kein Tracking, keine externen Requests - Docker Multi-Stage (238 MB) hinter Caddy mit automatischem HTTPS, Security-Headern und IP-maskierten Logs - scripts/cutout.mjs stellt den Render ueber eine Sobel-Kantenkarte frei und entfernt eingeschlossene Hintergrundflaechen Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Leitet die abgeleiteten Bild-Assets aus den Quelldateien ab.
|
||||
* Einmalig ausfuehren, wenn sich Banner oder Logo aendern:
|
||||
* node scripts/assets.mjs
|
||||
*/
|
||||
import sharp from 'sharp';
|
||||
import { statSync } from 'node:fs';
|
||||
|
||||
const BANNER = 'src/assets/hero-banner.jpg';
|
||||
const LOGO = 'src/assets/logo-white.png';
|
||||
const BRAND_BG = { r: 11, g: 16, b: 32, alpha: 1 };
|
||||
|
||||
// 1) Club-/Stage-Ausschnitt des Banners: alles links des eingebrannten
|
||||
// "DJ NIZZ"-Schriftzugs (dessen linke Kante liegt bei x = 909) und ohne
|
||||
// die Figur. Dient als Hintergrundebene im Hero, Typo und Figur kommen
|
||||
// als HTML darueber. Breiter schneiden lassen die Geisterbuchstaben
|
||||
// des Schriftzugs im Hintergrund auftauchen.
|
||||
await sharp(BANNER)
|
||||
.extract({ left: 0, top: 0, width: 880, height: 1536 })
|
||||
.jpeg({ quality: 86, mozjpeg: true })
|
||||
.toFile('src/assets/hero-club.jpg');
|
||||
|
||||
// 2) Open-Graph-Bild 1200x630 aus dem kompletten Banner
|
||||
await sharp(BANNER)
|
||||
.extract({ left: 0, top: 45, width: 2752, height: 1445 })
|
||||
.resize(1200, 630)
|
||||
.jpeg({ quality: 88, mozjpeg: true })
|
||||
.toFile('public/og-image.jpg');
|
||||
|
||||
// 3) Favicons: das Logo ist weiss auf transparent und waere im hellen
|
||||
// Browser-Tab unsichtbar -> auf Markenblau setzen.
|
||||
const trimmed = await sharp(LOGO)
|
||||
.trim()
|
||||
.resize(880, 880, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } })
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
// sharp wendet resize() intern vor composite() an, unabhaengig von der
|
||||
// Aufrufreihenfolge. Daher erst die 1024er Kachel bauen, dann separat skalieren.
|
||||
const icon = await sharp({ create: { width: 1024, height: 1024, channels: 4, background: BRAND_BG } })
|
||||
.composite([{ input: trimmed, gravity: 'center' }])
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
for (const size of [32, 180, 192, 512]) {
|
||||
const out = size === 180 ? 'public/apple-touch-icon.png' : `public/favicon-${size}.png`;
|
||||
await sharp(icon).resize(size, size).png({ compressionLevel: 9 }).toFile(out);
|
||||
}
|
||||
|
||||
const files = [
|
||||
'src/assets/hero-banner.jpg',
|
||||
'src/assets/hero-club.jpg',
|
||||
'src/assets/nizz-cutout.png',
|
||||
'src/assets/logo-white.png',
|
||||
'public/og-image.jpg',
|
||||
'public/favicon-32.png',
|
||||
'public/favicon-192.png',
|
||||
'public/favicon-512.png',
|
||||
'public/apple-touch-icon.png',
|
||||
];
|
||||
for (const f of files) {
|
||||
const m = await sharp(f).metadata();
|
||||
console.log(f.padEnd(32), `${m.width}x${m.height}`.padEnd(12), `${(statSync(f).size / 1024).toFixed(0)} KB`);
|
||||
}
|
||||
Reference in New Issue
Block a user