diff --git a/admin/lib/schema.js b/admin/lib/schema.js index 0e67280..e86119c 100644 --- a/admin/lib/schema.js +++ b/admin/lib/schema.js @@ -173,6 +173,8 @@ module.exports = [ { key: 'image', label: 'Bildschirmfoto', type: 'image' }, { key: 'mobileImage', label: 'Handy-Ansicht (Hochformat, leer = kein Handy)', type: 'image' }, { key: 'video', label: 'Video im Laptop (Pfad zu einer .mp4, leer = Standbild)', type: 'text' }, + { key: 'fullImage', label: 'Ganze Seite, Computer (Pfad, öffnet sich beim Anklicken; leer = nicht vergrößerbar)', type: 'text' }, + { key: 'fullMobileImage', label: 'Ganze Seite, Handy (Pfad, öffnet sich beim Anklicken)', type: 'text' }, { key: 'kategorie', label: 'Kategorie', type: 'text' }, { key: 'titel', label: 'Titel / Name', type: 'text' }, { key: 'aufgabe', label: 'Die Aufgabe (leer = nur Text zeigen)', type: 'textarea' }, diff --git a/content/site.json b/content/site.json index 816142a..537a02e 100644 --- a/content/site.json +++ b/content/site.json @@ -186,7 +186,9 @@ "nameHidden": false, "image": "/mappe/elektro-rabe.webp", "mobileImage": "/mappe/elektro-rabe-mobil.webp", - "video": "/mappe/elektro-rabe.mp4" + "video": "/mappe/elektro-rabe.mp4", + "fullImage": "/mappe/elektro-rabe-ganz.webp", + "fullMobileImage": "/mappe/elektro-rabe-mobil-ganz.webp" }, { "kategorie": "Konzeptarbeit · Tanzstudio", @@ -196,7 +198,9 @@ "nameHidden": false, "image": "/mappe/maison-verte.webp", "mobileImage": "/mappe/maison-verte-mobil.webp", - "video": "/mappe/maison-verte.mp4" + "video": "/mappe/maison-verte.mp4", + "fullImage": "/mappe/maison-verte-ganz.webp", + "fullMobileImage": "/mappe/maison-verte-mobil-ganz.jpg" }, { "kategorie": "Konzeptarbeit · Kosmetikstudio", @@ -206,7 +210,9 @@ "nameHidden": false, "image": "/mappe/atelier-rosenhain.webp", "mobileImage": "/mappe/atelier-rosenhain-mobil.webp", - "video": "" + "video": "", + "fullImage": "/mappe/atelier-rosenhain-ganz.webp", + "fullMobileImage": "/mappe/atelier-rosenhain-mobil-ganz.webp" } ] }, diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..637f7e9 Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..9bddc92 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg index 85d4278..9b82c7f 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1,4 +1 @@ - - - L - + diff --git a/public/icon-192.png b/public/icon-192.png new file mode 100644 index 0000000..d16ef5a Binary files /dev/null and b/public/icon-192.png differ diff --git a/public/icon-48.png b/public/icon-48.png new file mode 100644 index 0000000..ce56132 Binary files /dev/null and b/public/icon-48.png differ diff --git a/public/icon-512.png b/public/icon-512.png new file mode 100644 index 0000000..ce37050 Binary files /dev/null and b/public/icon-512.png differ diff --git a/public/icon-96.png b/public/icon-96.png new file mode 100644 index 0000000..32d30d0 Binary files /dev/null and b/public/icon-96.png differ diff --git a/public/mappe/atelier-rosenhain-ganz.webp b/public/mappe/atelier-rosenhain-ganz.webp new file mode 100644 index 0000000..74c6cb4 Binary files /dev/null and b/public/mappe/atelier-rosenhain-ganz.webp differ diff --git a/public/mappe/atelier-rosenhain-mobil-ganz.webp b/public/mappe/atelier-rosenhain-mobil-ganz.webp new file mode 100644 index 0000000..45fa622 Binary files /dev/null and b/public/mappe/atelier-rosenhain-mobil-ganz.webp differ diff --git a/public/mappe/elektro-rabe-ganz.webp b/public/mappe/elektro-rabe-ganz.webp new file mode 100644 index 0000000..881a3f0 Binary files /dev/null and b/public/mappe/elektro-rabe-ganz.webp differ diff --git a/public/mappe/elektro-rabe-mobil-ganz.webp b/public/mappe/elektro-rabe-mobil-ganz.webp new file mode 100644 index 0000000..7020cdf Binary files /dev/null and b/public/mappe/elektro-rabe-mobil-ganz.webp differ diff --git a/public/mappe/maison-verte-ganz.webp b/public/mappe/maison-verte-ganz.webp new file mode 100644 index 0000000..33b3a34 Binary files /dev/null and b/public/mappe/maison-verte-ganz.webp differ diff --git a/public/mappe/maison-verte-mobil-ganz.jpg b/public/mappe/maison-verte-mobil-ganz.jpg new file mode 100644 index 0000000..97938db Binary files /dev/null and b/public/mappe/maison-verte-mobil-ganz.jpg differ diff --git a/public/og-image.jpg b/public/og-image.jpg new file mode 100644 index 0000000..142dee4 Binary files /dev/null and b/public/og-image.jpg differ diff --git a/scripts/icons-bauen.mjs b/scripts/icons-bauen.mjs new file mode 100644 index 0000000..a0fef6b --- /dev/null +++ b/scripts/icons-bauen.mjs @@ -0,0 +1,56 @@ +// Baut Favicon, App-Icons und das Vorschaubild für geteilte Links aus den +// Marken-SVGs in public/. Aufruf: node scripts/icons-bauen.mjs +// Quelle des Favicons: das "la" auf Petrol aus dem Prototyp lara-webdesign.html. +import sharp from 'sharp'; +import { readFileSync, writeFileSync } from 'node:fs'; + +const pub = (f) => new URL(`../public/${f}`, import.meta.url); +const papier = { r: 252, g: 250, b: 246, alpha: 1 }; + +const favicon = readFileSync(pub('favicon.svg')); +const logo = readFileSync(pub('logo.svg')); + +const png = (svg, size) => + sharp(svg, { density: 72 * Math.ceil(size / 100) * 4 }).resize(size, size).png().toBuffer(); + +// Transparente Icons +for (const size of [48, 96, 192, 512]) { + writeFileSync(pub(`icon-${size}.png`), await png(favicon, size)); +} + +// Apple-Icon braucht deckenden Grund (iOS füllt Transparenz schwarz) +const appleInner = await png(favicon, 148); +await sharp({ create: { width: 180, height: 180, channels: 4, background: papier } }) + .composite([{ input: appleInner, gravity: 'center' }]) + .png() + .toFile(new URL('../public/apple-touch-icon.png', import.meta.url).pathname.replace(/^\/(\w:)/, '$1')); + +// favicon.ico mit 16/32/48 als PNG-Einträge +const sizes = [16, 32, 48]; +const bufs = await Promise.all(sizes.map((s) => png(favicon, s))); +const header = Buffer.alloc(6 + 16 * sizes.length); +header.writeUInt16LE(0, 0); +header.writeUInt16LE(1, 2); +header.writeUInt16LE(sizes.length, 4); +let offset = header.length; +sizes.forEach((s, i) => { + const e = 6 + 16 * i; + header.writeUInt8(s, e); + header.writeUInt8(s, e + 1); + header.writeUInt16LE(1, e + 4); + header.writeUInt16LE(32, e + 6); + header.writeUInt32LE(bufs[i].length, e + 8); + header.writeUInt32LE(offset, e + 12); + offset += bufs[i].length; +}); +writeFileSync(pub('favicon.ico'), Buffer.concat([header, ...bufs])); + +// Vorschaubild für WhatsApp, Signal, Facebook, LinkedIn & Co. (1200 x 630) +const logoPng = await sharp(logo, { density: 300 }).resize({ width: 860 }).png().toBuffer(); +await sharp({ create: { width: 1200, height: 630, channels: 4, background: papier } }) + .composite([{ input: logoPng, gravity: 'center' }]) + .flatten({ background: papier }) + .jpeg({ quality: 88 }) + .toFile(new URL('../public/og-image.jpg', import.meta.url).pathname.replace(/^\/(\w:)/, '$1')); + +console.log('Icons gebaut.'); diff --git a/src/components/Devices.astro b/src/components/Devices.astro index 51bb0eb..7fd0c65 100644 --- a/src/components/Devices.astro +++ b/src/components/Devices.astro @@ -3,17 +3,36 @@ * Laptop mit optional angelehntem Handy, für Mappe und Hero. * Ein Video im Laptop spielt nur, solange es sichtbar ist (Skript unten), * und bei "Bewegung reduzieren" gar nicht - dann bleibt das Standbild. + * Mit `screenZoom`/`phoneZoom` wird das Gerät anklickbar und öffnet die ganze + * Seite in der Großansicht (, einmal pro Seite eingebunden). */ +export interface ZoomSource { + title: string; + desktop?: string; + mobile?: string; +} + interface Props { screen: string; video?: string; phone?: string; alt?: string; eager?: boolean; + screenZoom?: ZoomSource; + phoneZoom?: ZoomSource; } -const { screen, video, phone, alt = '', eager = false } = Astro.props; +const { screen, video, phone, alt = '', eager = false, screenZoom, phoneZoom } = Astro.props; const loading = eager ? 'eager' : 'lazy'; +const zoomAttrs = (z: ZoomSource, mode: 'desktop' | 'mobile') => ({ + 'data-zoom': mode, + 'data-zoom-title': z.title, + 'data-zoom-desktop': z.desktop || undefined, + 'data-zoom-mobile': z.mobile || undefined, + 'aria-label': `${z.title} ${mode === 'desktop' ? 'am Computer' : 'auf dem Handy'} vergrößert ansehen`, +}); +const laptopZoom = screenZoom && (screenZoom.desktop || screenZoom.mobile) ? screenZoom : undefined; +const handyZoom = phoneZoom && (phoneZoom.mobile || phoneZoom.desktop) ? phoneZoom : undefined; ---
@@ -30,12 +49,21 @@ const loading = eager ? 'eager' : 'lazy';
+ {laptopZoom && ( + + )} {phone && ( -