feat: photography features & image optimizations

- Hero carousel: gallery.yaml hero accepts single UUID or array, crossfade component
- Full-bleed single albums: subpages with 1 album skip grid, render photos directly
- EXIF on hover: camera/lens/focal length overlay on grid items (toggle via exifOnHover)
- Customizable grid: columns/gap/aspectRatio/layout (masonry|uniform) via gallery.yaml
- Per-subpage grid overrides: each subpage can override global grid config
- True masonry: per-image aspect ratios from EXIF dimensions
- next/image migration: homepage hero, album covers, grid thumbnails, about portrait
- ThumbHash blur placeholders and dominant color backgrounds
- Custom next/image loader via image proxy with width/quality params
- Lightbox fade-in transition with dominant color background
- Password-protected subpages, health check, rate limiting, OG images
This commit is contained in:
ralksta
2026-02-14 10:30:27 +01:00
parent 3c69860a64
commit b7217c4a76
31 changed files with 1998 additions and 162 deletions

View File

@@ -8,9 +8,24 @@ import Link from 'next/link';
import './globals.css';
import { SubpageNav } from '@/components/SubpageNav';
const siteTitle = process.env.SITE_TITLE || 'Gallery';
const siteDescription = 'A curated photography portfolio';
export const metadata: Metadata = {
title: process.env.SITE_TITLE || 'Gallery',
description: 'A curated photography portfolio',
title: siteTitle,
description: siteDescription,
openGraph: {
title: siteTitle,
description: siteDescription,
type: 'website',
images: [`/api/og?title=${encodeURIComponent(siteTitle)}`],
},
twitter: {
card: 'summary_large_image',
title: siteTitle,
description: siteDescription,
images: [`/api/og?title=${encodeURIComponent(siteTitle)}`],
},
};
export default function RootLayout({
@@ -27,6 +42,9 @@ export default function RootLayout({
Home
</Link>
<SubpageNav />
<Link href="/about" className="header__nav-link">
About
</Link>
</nav>
</header>
<main className="main">{children}</main>