The root layout replaced the entire app with the SetupScreen whenever
the config was incomplete (missing gallery.yaml or Immich credentials)
— including /admin, which lives under the same root layout. That locked
users out of the very tool that fixes an incomplete configuration.
The middleware now forwards the request pathname via an x-pathname
header, and the root layout skips the setup screen for admin paths, so
/admin renders normally (login when ADMIN_PASSWORD is set, the
enable-hint otherwise) while every other route keeps showing the setup
instructions.
Fixes#326
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
💡 What: Added a "Skip to content" link that is visually hidden by default but appears when focused via keyboard. Pointed it to the main content area using `id="main-content"` and `tabIndex={-1}`.
🎯 Why: Keyboard users and screen readers must otherwise tab through the entire navigation menu on every page load before reaching the actual page content. This is tedious and a poor UX.
📸 Before/After: Before, keyboard focus started on "Home" in the header. After, the first tab press shows the "Skip to content" link at the top left of the screen.
♿ Accessibility: Massively improves WCAG compliance (Bypass Blocks) and keyboard navigation efficiency.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
ssr: false is not allowed with next/dynamic in Server Components.
Extracted the dynamic() call into DevToolbarLoader.tsx ('use client')
so layout.tsx stays a pure Server Component.
- /map page with Leaflet (CartoDB dark tiles)
- Photos clustered by city/country (not per-photo)
- /api/map endpoint with encrypted cover asset IDs
- Configurable via gallery.yaml (map: true/false)
- Conditional nav link in header
- CSP updated for Leaflet CDN and tile servers
- Theme-aware popups with cover thumbnails and album links
- Add configurable theme system with 4 presets (studio, minimal, editorial, classic)
- Theme controls fonts, colors, accent, photo frames, grain, and header dot
- Resolve themes from gallery.yaml: string shorthand or full object override
- Inject theme as CSS custom properties on <html> element
- Dynamic Google Fonts loading based on theme config
- OG image route reads accent/background from theme
- Refactor Studio overrides into conditional CSS via data-attributes
Documentation:
- Add docs/gallery-config.md with comprehensive configuration reference
- Add docs/theming.md with preset reference and custom theme guide
- Slim README to link to detailed docs
Fix CSS regression:
- Fix passepartout frame overflow:visible breaking next/image fill rendering
- Remove duplicate .photo-grid__item block that stripped overflow:hidden
Testing:
- Add e2e/photo-grid.spec.ts with 3 regression tests:
- Grid items render with real dimensions
- overflow:hidden CSS guard (catches the exact bug fixed)
- Images load with real pixel data (not blurhash)
Tooling:
- Add scripts/screenshots.ts for automated theme screenshot generation
- Handles dev server lifecycle, theme cycling, FadeIn + lazy image handling
- 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