feat: theming system, documentation, and photo grid regression tests

- 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
This commit is contained in:
ralksta
2026-02-15 12:21:02 +01:00
parent a0ee0ff433
commit a9737ad8e7
12 changed files with 810 additions and 140 deletions

113
docs/gallery-config.md Normal file
View File

@@ -0,0 +1,113 @@
# Gallery Configuration
All gallery structure is defined in `content/gallery.yaml`. Copy the example to get started:
```bash
cp content/gallery.yaml.example content/gallery.yaml
```
## Hero Images
Single image or crossfade carousel on the homepage:
```yaml
# Single hero image
hero: 00000000-0000-0000-0000-000000000000
# Carousel (crossfade between multiple images)
hero:
- 00000000-0000-0000-0000-000000000000
- 11111111-1111-1111-1111-111111111111
- 22222222-2222-2222-2222-222222222222
```
## Standalone Albums
Albums shown directly on the homepage as cards:
```yaml
albums:
- 11111111-1111-1111-1111-111111111111
- 22222222-2222-2222-2222-222222222222
```
## Subpages
Group multiple albums into named collections. URLs are auto-generated from the name:
```yaml
subpages:
- name: Japan # → /japan
albums:
- 33333333-3333-3333-3333-333333333333 # Tokyo
- 44444444-4444-4444-4444-444444444444 # Kyoto
- name: Wedding – Smith # → /wedding-smith
password: clientpass123 # password-protected
albums:
- 55555555-5555-5555-5555-555555555555
```
## Grid Layout
Configure the photo grid globally or per-subpage:
```yaml
# Global defaults
grid:
columns: 3 # number of columns (default: 3)
gap: 12 # gap in pixels (default: 12)
aspectRatio: "1" # "1", "3/2", "auto", etc. (default: "1")
layout: masonry # "masonry" or "uniform" (default: "masonry")
subpages:
- name: Japan
grid: # per-subpage override
columns: 4
layout: uniform
aspectRatio: "3/2"
albums:
- ...
```
## EXIF on Hover
Show camera/lens/settings when hovering over photos (enabled by default):
```yaml
exifOnHover: false # set to false to disable
```
## Footer
Optional minimal footer with social links:
```yaml
footer:
name: John Doe
instagram: johndoe
email: hello@example.com
website: https://example.com
```
## About Page
Create `content/about.md` with YAML frontmatter:
```markdown
---
portrait: asset-uuid-for-your-portrait
name: Your Name
location: City, Country
gear:
- Camera Body
- Favorite Lens
---
Your bio text here. Supports full Markdown.
```
## Finding UUIDs
- **Album UUIDs**: In Immich, go to Albums → click an album → the UUID is in the URL bar
- **Asset UUIDs**: Click any photo → the UUID is in the URL bar

121
docs/theming.md Normal file
View File

@@ -0,0 +1,121 @@
# Theming
Control the visual identity of your gallery from `content/gallery.yaml`. Choose a built-in preset or customize individual properties — no CSS editing required.
## Quick Start
Add a single line to your `gallery.yaml`:
```yaml
theme: minimal
```
That's it. Your entire gallery switches to the minimal theme.
## Built-in Presets
| Preset | Look | Accent | Fonts | Corners | Frame | Grain |
|--------|------|--------|-------|---------|-------|-------|
| **studio** | Leica-inspired, editorial | 🔴 `#e60012` | Playfair Display + DM Sans | Sharp | Passepartout | ✓ |
| **minimal** | Clean, no distractions | ⚪ `#666666` | Inter | Sharp | None | ✗ |
| **editorial** | Magazine feel | ⚫ `#1a1a1a` | Libre Baskerville + Source Sans | Slight | Shadow | ✗ |
| **classic** | Warm, inviting | 🟡 `#8b6914` | Cormorant Garamond + Lato | Rounded | None | ✗ |
Default is `studio` if no theme is specified.
## Custom Theme
Start from any preset and override individual properties:
```yaml
theme:
preset: studio # base preset to extend (default: studio)
accent: "#2563eb" # brand/accent color (any hex)
fonts:
heading: "Inter" # Google Fonts name for headings
body: "Inter" # body text
caption: "JetBrains Mono" # EXIF captions
radius: 8 # border-radius in px (0 = sharp corners)
photoFrame: none # "none" | "passepartout" | "shadow"
grain: false # film grain overlay on photos
headerDot: false # accent-colored dot in the nav bar
heroStyle: split # "split" | "fullbleed" | "minimal"
```
All properties are optional — omitted values fall back to the preset defaults.
## Properties Reference
### `accent`
The brand color used for hover effects, navigation highlights, the header dot, and OG social preview images.
### `fonts`
Google Fonts names. Fonts are loaded automatically — just use the name as it appears on [fonts.google.com](https://fonts.google.com). Three font slots:
- **heading** — album titles, hero title, section labels
- **body** — navigation, descriptions, UI text
- **caption** — EXIF metadata, photo captions
### `radius`
Border radius in pixels applied to photo grid items and album cards:
- `0` — sharp, editorial corners
- `4-8` — subtle rounding
- `12+` — soft, modern feel
### `photoFrame`
How photos are framed in the grid:
- **`none`** — photos fill the grid cell directly
- **`passepartout`** — museum-style mat border around each photo with print-style EXIF captions below
- **`shadow`** — subtle drop shadow behind each photo
### `grain`
When `true`, a subtle film grain texture overlays each photo in the grid. Adds an analog, filmic character.
### `headerDot`
When `true`, shows a small accent-colored dot in the navigation bar (inspired by the Leica red dot).
### `heroStyle`
Controls the homepage hero layout:
- **`split`** — title/nav on the left, hero image on the right (default)
- **`fullbleed`** — hero image fills the entire viewport
- **`minimal`** — text-only, no hero image
## Examples
### Minimal portfolio
```yaml
theme:
preset: minimal
accent: "#0066cc"
fonts:
heading: "Outfit"
body: "Outfit"
```
### Film photography blog
```yaml
theme:
preset: studio
accent: "#d4a017"
grain: true
photoFrame: passepartout
```
### Modern magazine
```yaml
theme:
preset: editorial
radius: 12
fonts:
heading: "Fraunces"
body: "Inter"
caption: "IBM Plex Mono"
```