fix(images): stop publishing EXIF/GPS originals, responsive images, crawlable gallery

- Homepage images: <Picture> from src/assets (AVIF/WebP/JPG srcset, width/
  height, first image eager + fetchpriority high, rest lazy), Swedish alt
  texts. (The duplicate EXIF-laden copies in public/3d-scroll were deleted
  in 30e385e; README links now point at src/assets.)
- Portfolio: originals are no longer referenced (no .src access), so Astro
  does not copy them to dist/. Small/Medium/Large downloads and the lightbox
  srcset are EXIF-free JPGs from getImage. Old -s/-m files are no longer
  imported.
- Alt texts: Swedish fallback from date (EXIF or filename), never camera
  model or "undefined"; portfolio-meta.ts overrides per image.
- Gallery items are <a href> to the large image (lightbox on click);
  download options are buttons; no empty H3, no H4 for the password box.
- Stats beacon unchanged; item keys computed at build time (same format).
- Script moved inside the layout so it is no longer rendered after </html>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvE3C43Uwtv2vtnoFBLDw7
This commit is contained in:
Linus Olofsson
2026-10-27 19:13:24 +01:00
co-authored by Claude Opus 5.5
parent 4f1b468f75
commit 1e921dfe37
3 changed files with 452 additions and 369 deletions
+27 -7
View File
@@ -1,13 +1,22 @@
---
// src/components/ThreeDScroll.astro
// Bilderna ligger i src/assets så att Astro skalar om dem (AVIF/WebP/JPG, srcset)
// och rensar bort EXIF (GPS, serienummer m.m.). Originalen publiceras inte.
import { Picture } from 'astro:assets';
import img1 from '../assets/images/3d-scroll/img1.jpg';
import img2 from '../assets/images/3d-scroll/img2.jpg';
import img3 from '../assets/images/3d-scroll/img3.jpg';
import img4 from '../assets/images/3d-scroll/img4.jpg';
import img5 from '../assets/images/3d-scroll/img5.jpg';
const images = [
{ src: "/3d-scroll/img1.jpg", alt: "Architecture Dark" },
{ src: "/3d-scroll/img2.jpg", alt: "Shadows Light" },
{ src: "/3d-scroll/img3.jpg", alt: "Portrait High End" },
{ src: "/3d-scroll/img4.jpg", alt: "Minimalist Landscape" },
{ src: "/3d-scroll/img5.jpg", alt: "Foggy Forest" },
{ src: img1, alt: 'Närbild av en gammal läderinbunden bok med slitna pappersark i varmt motljus' },
{ src: img2, alt: 'Veteranbil med tända strålkastare på en stadsgata i kvällsljus' },
{ src: img3, alt: 'Överblommade tistlar bland höga grässtrån i dämpade bruna toner' },
{ src: img4, alt: 'Järnvägsspår som slingrar sig mellan träd i motljus, i sepiaton' },
{ src: img5, alt: 'Svartvit bild av en man som bär ett litet barn bland såpbubblor i motljus' },
];
const last = images.length - 1;
---
<div id="scroll-container" class="relative h-[400vh] w-full">
@@ -20,9 +29,20 @@ const images = [
class="gallery-item absolute transform-style-3d will-change-transform flex items-center justify-center"
data-index={index}
>
<img
src={img.src}
{/* Första bilden: eager + hög prioritet. Den sista ligger överst innan man
scrollat (alla bilder står på samma plats tills scriptet flyttar dem),
så den laddas också direkt. Övriga laddas lat. */}
<Picture
src={img.src}
alt={img.alt}
widths={[640, 1024, 1600, 2048]}
sizes="80vw"
formats={['avif', 'webp']}
fallbackFormat="jpg"
quality={78}
loading={index === 0 || index === last ? 'eager' : 'lazy'}
fetchpriority={index === 0 ? 'high' : 'auto'}
decoding="async"
class="floating-animation max-w-[80vw] max-h-[70vh] w-auto h-auto object-contain rounded-lg shadow-[0_20px_60px_-15px_rgba(0,0,0,0.8)] border border-white/5 grayscale hover:grayscale-0 transition-all duration-700 ease-out"
/>
</div>