diff --git a/README.md b/README.md index 5b40bef..ef84f87 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,16 @@ A self-hosted photography portfolio powered by [Immich](https://immich.app). Tur Immich Folio acts as a **secure reverse proxy** between your visitors and your private Immich instance. Your Immich server stays on your local network, completely invisible to the outside world. +

+ Homepage — Editorial theme + Photo grid — Editorial theme +

+ ## Features ### Gallery & Layout -- **Split hero homepage** — full-height hero with site title, subtitle, and navigation links on the left; hero image on the right +- **Configurable hero layouts** — split (title + image side-by-side), fullbleed (edge-to-edge hero image), or minimal (centered title card) - **Hero image carousel** — single image or crossfade carousel of multiple Immich assets - **Masonry photo grid** — responsive layout with natural aspect ratios and configurable columns, gap, and aspect ratio - **Uniform grid mode** — switch to a fixed-aspect uniform grid per-subpage or globally @@ -86,12 +91,31 @@ All gallery structure — hero images, albums, subpages, grid layout, footer — ### Theming -Customize the visual identity of your gallery with built-in presets or fine-grained control over colors, fonts, corners, photo frames, and more. +Four built-in presets with distinct visual identities — or mix and match with fine-grained control over colors, fonts, corners, photo frames, and hero layout. ```yaml -theme: minimal # or: studio, editorial, classic +theme: studio # or: minimal, editorial, classic ``` + + + + + + + + + + + + + + + + + +
Studio
Leica-inspired, red dot
Minimal
Swiss brutalist, fullbleed hero
Studio themeMinimal theme
Editorial
Cinematic magazine, warm charcoal
Classic
Gilded gallery, gold accents
Editorial themeClassic theme
+ → **[Theming Guide](docs/theming.md)** ## Docker diff --git a/app/globals.css b/app/globals.css index f7f3a0e..5f3ac7d 100644 --- a/app/globals.css +++ b/app/globals.css @@ -279,10 +279,12 @@ flex-direction: column; justify-content: flex-end; padding: 24px; - background: linear-gradient(to top, - rgba(0, 0, 0, 0.8) 0%, - rgba(0, 0, 0, 0.3) 40%, - transparent 100%); + background: linear-gradient( + to top, + rgba(0, 0, 0, 0.8) 0%, + rgba(0, 0, 0, 0.3) 40%, + transparent 100% + ); opacity: 0; transition: opacity var(--transition-normal); } @@ -412,10 +414,12 @@ /* ── Loading States ───────────────────────────────── */ .skeleton { - background: linear-gradient(90deg, - var(--bg-card) 0%, - var(--bg-card-hover) 50%, - var(--bg-card) 100%); + background: linear-gradient( + 90deg, + var(--bg-card) 0%, + var(--bg-card-hover) 50%, + var(--bg-card) 100% + ); background-size: 200% 100%; animation: skeleton-shimmer 1.5s infinite; border-radius: var(--radius-sm); @@ -792,7 +796,557 @@ color: var(--accent); } -/* ── Responsive ───────────────────────────────────── */ +/* ══════════════════════════════════════════════════════ + THEME: Minimal — Swiss Brutalist + ══════════════════════════════════════════════════════ */ + +/* Header: ultra-stripped, no border, just floating text */ +[data-preset='minimal'] .header { + border-bottom: none; + background: transparent; + backdrop-filter: none; + -webkit-backdrop-filter: none; +} + +[data-preset='minimal'] .header__nav-link { + font-size: 0.6rem; + font-weight: 400; + letter-spacing: 0.18em; + color: var(--text-muted); + border-bottom: none; +} + +[data-preset='minimal'] .header__nav-link:hover, +[data-preset='minimal'] .header__nav-link.active { + color: var(--text-primary); + border-bottom-color: transparent; +} + +/* Hero: fullbleed image, title overlaid bottom-left */ +[data-preset='minimal'] .hero { + grid-template-columns: 1fr; + position: relative; +} + +[data-preset='minimal'] .hero__left { + position: absolute; + bottom: 0; + left: 0; + z-index: 2; + padding: 32px 40px; + background: none; + align-items: flex-start; + justify-content: flex-end; +} + +[data-preset='minimal'] .hero__content { + text-align: left; +} + +[data-preset='minimal'] .hero__title { + font-family: var(--font-sans); + font-size: 0.7rem; + font-weight: 400; + letter-spacing: 0.2em; + text-transform: uppercase; + opacity: 0.6; +} + +[data-preset='minimal'] .hero__subtitle { + font-size: 0.6rem; + letter-spacing: 0.15em; + margin-bottom: 16px; + opacity: 0.4; +} + +[data-preset='minimal'] .hero__nav { + flex-direction: row; + gap: 16px; +} + +[data-preset='minimal'] .hero__nav-link { + font-size: 0.6rem; + letter-spacing: 0.15em; + text-transform: uppercase; + border-bottom: none; + opacity: 0.5; + transition: opacity var(--transition-fast); +} + +[data-preset='minimal'] .hero__nav-link:hover { + opacity: 1; + border-bottom-color: transparent; + color: var(--text-primary); +} + +[data-preset='minimal'] .hero__right { + grid-column: 1; + grid-row: 1; + height: 100vh; + height: 100svh; +} + +/* Photo grid: zero hover effects, clinical stillness */ +[data-preset='minimal'] .photo-grid { + column-gap: 2px; +} + +[data-preset='minimal'] .photo-grid__item { + margin-bottom: 2px; + border-radius: 0; + cursor: pointer; +} + +[data-preset='minimal'] .photo-grid__item:hover img { + filter: none; + transform: none; +} + +[data-preset='minimal'] .photo-grid__item:active img { + filter: brightness(0.8); + transform: none; +} + +/* Album cards: no overlay, text below */ +[data-preset='minimal'] .album-card { + border: none; + border-radius: 0; +} + +[data-preset='minimal'] .album-card:hover { + border-color: transparent; + transform: none; +} + +[data-preset='minimal'] .album-card:hover .album-card__image { + transform: none; + filter: none; +} + +[data-preset='minimal'] .album-card__overlay { + background: linear-gradient(to top, rgba(0, 0, 0, 0.4) 0%, transparent 30%); + opacity: 1; +} + +[data-preset='minimal'] .album-card__title { + font-family: var(--font-sans); + font-size: 0.7rem; + font-weight: 400; + letter-spacing: 0.15em; + text-transform: uppercase; +} + +[data-preset='minimal'] .album-card__count { + font-size: 0.6rem; + font-weight: 400; + letter-spacing: 0.1em; +} + +/* Section labels: numbered indices */ +[data-preset='minimal'] .section-label { + font-size: 0.6rem; + letter-spacing: 0.2em; + color: var(--text-muted); +} + +/* Footer: single line, ultra-minimal */ +[data-preset='minimal'] .footer { + border-top: none; +} + +[data-preset='minimal'] .footer__name { + font-size: 0.6rem; + letter-spacing: 0.15em; +} + +/* Album header */ +[data-preset='minimal'] .album-header__title { + font-family: var(--font-sans); + font-size: 0.85rem; + letter-spacing: 0.2em; + text-transform: uppercase; + font-weight: 400; +} + +[data-preset='minimal'] .album-header__meta { + font-size: 0.7rem; + letter-spacing: 0.1em; +} + +/* Subpage grid */ +[data-preset='minimal'] .subpage-grid { + gap: 2px; +} + +[data-preset='minimal'] .subpage-grid__item { + border: none; +} + +[data-preset='minimal'] .subpage-grid__item-title { + font-family: var(--font-sans); + font-size: 0.75rem; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +/* ══════════════════════════════════════════════════════ + THEME: Editorial — Cinematic Magazine + ══════════════════════════════════════════════════════ */ + +/* Header: transparent, magazine masthead feel */ +[data-preset='editorial'] .header { + background: transparent; + backdrop-filter: none; + -webkit-backdrop-filter: none; + border-bottom: 1px solid var(--border-subtle); +} + +[data-preset='editorial'] .header__nav-link { + font-family: var(--font-sans); + font-size: 0.65rem; + font-weight: 400; + letter-spacing: 0.18em; +} + +/* Hero: dramatic oversized serif title */ +[data-preset='editorial'] .hero__title { + font-size: 4.5rem; + font-weight: 300; + letter-spacing: 0.02em; + text-transform: none; + line-height: 1; +} + +[data-preset='editorial'] .hero__subtitle { + font-family: var(--font-caption); + font-size: 0.9rem; + font-weight: 400; + letter-spacing: 0.08em; + text-transform: none; + font-style: italic; + margin-bottom: 48px; + opacity: 0.7; +} + +[data-preset='editorial'] .hero__nav-link { + font-family: var(--font-caption); + font-size: 0.8rem; + font-style: italic; + font-weight: 400; + letter-spacing: 0.06em; + text-transform: none; + border-bottom: none; +} + +[data-preset='editorial'] .hero__nav-link:hover { + color: var(--accent); + border-bottom-color: transparent; +} + +/* Photo grid: generous spacing, cinematic transitions */ +[data-preset='editorial'] .photo-grid { + column-gap: 32px; +} + +[data-preset='editorial'] .photo-grid__item { + margin-bottom: 32px; +} + +[data-preset='editorial'] .photo-grid__item img { + transition: + filter 1.5s ease, + transform 1.5s ease; + filter: saturate(0.7) brightness(0.95); +} + +[data-preset='editorial'] .photo-grid__item:hover img { + filter: saturate(1) brightness(1); + transform: scale(1.01); +} + +/* Album cards: text below, editorial layout */ +[data-preset='editorial'] .album-card { + border: none; + border-radius: 0; + aspect-ratio: 16 / 9; +} + +[data-preset='editorial'] .album-card:hover { + transform: none; + border-color: transparent; +} + +[data-preset='editorial'] .album-card__overlay { + justify-content: flex-start; + padding: 28px; + background: linear-gradient(to bottom, rgba(0, 0, 0, 0.5) 0%, transparent 40%); +} + +[data-preset='editorial'] .album-card__title { + font-size: 2rem; + font-weight: 300; + letter-spacing: 0.02em; + text-transform: none; +} + +[data-preset='editorial'] .album-card__count { + font-family: var(--font-caption); + font-style: italic; + font-size: 0.8rem; +} + +[data-preset='editorial'] .album-card:hover .album-card__image { + transform: scale(1.015); + filter: brightness(0.8); + transition: + transform 1.5s ease, + filter 1.5s ease; +} + +/* Section labels: pull-quote style with left border */ +[data-preset='editorial'] .section-label { + font-family: var(--font-caption); + font-size: 0.8rem; + font-style: italic; + font-weight: 400; + letter-spacing: 0.08em; + text-transform: none; + color: var(--text-secondary); + padding-left: 16px; + border-left: 2px solid var(--accent); +} + +/* Album header: large cinematic title */ +[data-preset='editorial'] .album-header__title { + font-size: 3rem; + font-weight: 300; + letter-spacing: 0.02em; + text-transform: none; +} + +[data-preset='editorial'] .album-header__meta { + font-family: var(--font-caption); + font-style: italic; +} + +/* Footer: centered italic attribution */ +[data-preset='editorial'] .footer__name { + font-family: var(--font-caption); + font-style: italic; + text-transform: none; + letter-spacing: 0.06em; + font-size: 0.8rem; +} + +/* Subpage grid */ +[data-preset='editorial'] .subpage-grid { + gap: 2px; +} + +[data-preset='editorial'] .subpage-grid__item { + border: none; + aspect-ratio: 16 / 9; +} + +[data-preset='editorial'] .subpage-grid__item-title { + font-size: 1.4rem; + letter-spacing: 0.02em; + font-weight: 300; + text-transform: none; +} + +/* ══════════════════════════════════════════════════════ + THEME: Classic — Gilded Gallery + ══════════════════════════════════════════════════════ */ + +/* Header: centered nav with gold accent */ +[data-preset='classic'] .header { + background: rgba(18, 18, 16, 0.92); + border-bottom: 1px solid rgba(196, 154, 60, 0.15); +} + +[data-preset='classic'][data-theme='light'] .header { + background: rgba(253, 251, 245, 0.92); +} + +[data-preset='classic'] .header__nav { + justify-content: center; +} + +[data-preset='classic'] .header__nav-link { + font-size: 0.7rem; + font-weight: 400; + letter-spacing: 0.14em; + padding: 20px 18px; +} + +/* Hero: elegant centered card above banner */ +[data-preset='classic'] .hero__left { + padding: 80px 48px; +} + +[data-preset='classic'] .hero__title { + font-size: 2.8rem; + font-weight: 400; + letter-spacing: 0.12em; + text-transform: uppercase; + line-height: 1.2; +} + +[data-preset='classic'] .hero__subtitle { + font-family: var(--font-caption); + font-size: 0.85rem; + font-weight: 400; + letter-spacing: 0.15em; + font-style: italic; + margin-bottom: 12px; +} + +/* Decorative ornament beneath subtitle */ +[data-preset='classic'] .hero__subtitle::after { + content: '◆'; + display: block; + margin-top: 20px; + font-size: 0.5rem; + color: var(--accent); + letter-spacing: 0; +} + +[data-preset='classic'] .hero__nav-link { + font-family: var(--font-caption); + font-size: 0.8rem; + font-weight: 400; + letter-spacing: 0.1em; + font-style: italic; + text-transform: none; + border-bottom: none; +} + +[data-preset='classic'] .hero__nav-link:hover { + color: var(--accent); + border-bottom-color: transparent; +} + +/* Photo grid: warm passepartout, elegant hover */ +[data-preset='classic'] .photo-grid { + column-gap: var(--grid-gap, 20px); +} + +[data-preset='classic'] .photo-grid__item { + margin-bottom: var(--grid-gap, 20px); +} + +[data-preset='classic'] .photo-grid__item:hover img { + filter: brightness(0.9); + transform: scale(1.01); +} + +/* Warm passepartout override */ +[data-preset='classic'][data-photo-frame='passepartout'] .photo-grid__item { + background: #1a1816; + border-color: rgba(196, 154, 60, 0.12); +} + +[data-preset='classic'][data-photo-frame='passepartout'][data-theme='light'] .photo-grid__item { + background: #fffdf5; + border-color: rgba(196, 154, 60, 0.2); +} + +/* Album cards: warm rounded, gold hover */ +[data-preset='classic'] .album-card { + border-radius: 12px; + border: 1px solid rgba(196, 154, 60, 0.1); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); +} + +[data-preset='classic'][data-theme='light'] .album-card { + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + border-color: rgba(196, 154, 60, 0.15); +} + +[data-preset='classic'] .album-card:hover { + border-color: var(--accent); + transform: translateY(-3px); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); +} + +[data-preset='classic'] .album-card__title { + font-weight: 400; + letter-spacing: 0.06em; +} + +[data-preset='classic'] .album-card__count { + font-family: var(--font-caption); + font-style: italic; +} + +/* Section labels: decorative centered dividers */ +[data-preset='classic'] .section-label { + text-align: center; + font-family: var(--font-caption); + font-weight: 400; + letter-spacing: 0.25em; + font-size: 0.7rem; + color: var(--text-muted); +} + +[data-preset='classic'] .section-label::before { + content: '— '; + color: var(--accent); +} + +[data-preset='classic'] .section-label::after { + content: ' —'; + color: var(--accent); +} + +/* Album header: elegant serif */ +[data-preset='classic'] .album-header__title { + font-weight: 400; + letter-spacing: 0.1em; + font-size: 2.2rem; +} + +[data-preset='classic'] .album-header__meta { + font-family: var(--font-caption); + font-style: italic; +} + +/* Footer: ornamental */ +[data-preset='classic'] .footer { + border-top: 1px solid rgba(196, 154, 60, 0.15); +} + +[data-preset='classic'] .footer__name { + font-family: var(--font-caption); + font-style: italic; + text-transform: none; + letter-spacing: 0.08em; + font-size: 0.8rem; +} + +/* Scroll-to-top: gold border */ +[data-preset='classic'] .scroll-to-top { + border-radius: 12px; +} + +[data-preset='classic'] .scroll-to-top:hover { + border-color: var(--accent); + color: var(--accent); +} + +/* Subpage grid: warm borders */ +[data-preset='classic'] .subpage-grid__item { + border: 1px solid rgba(196, 154, 60, 0.1); + border-radius: 8px; + overflow: hidden; +} + +[data-preset='classic'] .subpage-grid__item-title { + font-weight: 400; + letter-spacing: 0.06em; +} + @media (max-width: 1024px) { .photo-grid { --grid-columns: 2; @@ -859,4 +1413,28 @@ [data-photo-frame='passepartout'] .photo-grid__item { padding: 6px 6px 18px; } -} \ No newline at end of file + + /* Minimal: adjust fullbleed hero for mobile */ + [data-preset='minimal'] .hero__left { + padding: 20px 24px; + } + + [data-preset='minimal'] .hero__right { + height: 70vh; + height: 70svh; + } + + /* Editorial: scale down oversized title */ + [data-preset='editorial'] .hero__title { + font-size: 2.5rem; + } + + [data-preset='editorial'] .album-header__title { + font-size: 2rem; + } + + /* Classic: scale title */ + [data-preset='classic'] .hero__title { + font-size: 2rem; + } +} diff --git a/app/layout.tsx b/app/layout.tsx index a6892e8..7700f45 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -57,6 +57,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) lang="en" suppressHydrationWarning style={themeVars as React.CSSProperties} + data-preset={theme.preset} data-grain={theme.grain} data-header-dot={theme.headerDot} data-photo-frame={theme.photoFrame} diff --git a/app/tokens.css b/app/tokens.css index 675fa55..1624e6f 100644 --- a/app/tokens.css +++ b/app/tokens.css @@ -54,6 +54,99 @@ --accent-dim: rgba(200, 0, 15, 0.08); } +/* ── Minimal: Swiss Brutalist Palette ────────────── */ +[data-preset='minimal'] { + --bg-primary: #000000; + --bg-secondary: #0a0a0a; + --bg-card: #111111; + --bg-card-hover: #1a1a1a; + --bg-overlay: rgba(0, 0, 0, 0.98); + + --text-primary: #ffffff; + --text-secondary: #888888; + --text-muted: #555555; + + --border-subtle: #1a1a1a; + --border-hover: #333333; +} + +[data-preset='minimal'][data-theme='light'] { + --bg-primary: #ffffff; + --bg-secondary: #f7f7f7; + --bg-card: #ffffff; + --bg-card-hover: #fafafa; + --bg-overlay: rgba(255, 255, 255, 0.98); + + --text-primary: #000000; + --text-secondary: #666666; + --text-muted: #999999; + + --border-subtle: #eeeeee; + --border-hover: #cccccc; +} + +/* ── Editorial: Cinematic Magazine Palette ────────── */ +[data-preset='editorial'] { + --bg-primary: #1a1714; + --bg-secondary: #141210; + --bg-card: #1e1b17; + --bg-card-hover: #252119; + --bg-overlay: rgba(20, 18, 16, 0.98); + + --text-primary: #ede8e0; + --text-secondary: #9a9086; + --text-muted: #6b6359; + + --border-subtle: #2a2620; + --border-hover: #3a3530; +} + +[data-preset='editorial'][data-theme='light'] { + --bg-primary: #faf8f4; + --bg-secondary: #f0ece4; + --bg-card: #ffffff; + --bg-card-hover: #f5f2ec; + --bg-overlay: rgba(250, 248, 244, 0.96); + + --text-primary: #1a1714; + --text-secondary: #5a5550; + --text-muted: #8a857f; + + --border-subtle: #e5e0d8; + --border-hover: #ccc7c0; +} + +/* ── Classic: Gilded Gallery Palette ─────────────── */ +[data-preset='classic'] { + --bg-primary: #121210; + --bg-secondary: #0e0e0c; + --bg-card: #181816; + --bg-card-hover: #1e1e1a; + --bg-overlay: rgba(14, 14, 12, 0.98); + + --text-primary: #f0ece4; + --text-secondary: #a09888; + --text-muted: #706858; + + --border-subtle: #2a2822; + --border-hover: #3a3830; +} + +[data-preset='classic'][data-theme='light'] { + --bg-primary: #fdfbf5; + --bg-secondary: #f5f0e6; + --bg-card: #ffffff; + --bg-card-hover: #faf6ee; + --bg-overlay: rgba(253, 251, 245, 0.96); + + --text-primary: #1a1810; + --text-secondary: #5a5545; + --text-muted: #8a8575; + + --border-subtle: #e5dfcf; + --border-hover: #d0c8b5; +} + /* ── Reset ────────────────────────────────────────── */ *, *::before, @@ -84,4 +177,4 @@ a { img { display: block; max-width: 100%; -} \ No newline at end of file +} diff --git a/content/gallery.yaml.example b/content/gallery.yaml.example index b2806ea..82bbc84 100644 --- a/content/gallery.yaml.example +++ b/content/gallery.yaml.example @@ -52,10 +52,10 @@ subpages: # ── Theme ───────────────────────────────────────────────────── # Control the visual identity of your gallery. Use a preset name: -# theme: studio # Leica-inspired, red accent, passepartout frames, film grain -# theme: minimal # Clean, no distractions, grey accent -# theme: editorial # Magazine feel, shadow frames -# theme: classic # Warm, golden accent, rounded corners +# theme: studio # Leica-inspired, red dot, passepartout frames, film grain +# theme: minimal # Swiss brutalist, fullbleed hero, true B&W, zero-gap grid +# theme: editorial # Cinematic magazine, warm charcoal, Bodoni serif, shadow frames +# theme: classic # Gilded gallery, gold accents, Cinzel headings, passepartout # # Or customize individual properties (defaults from the preset): # theme: diff --git a/docs/theming.md b/docs/theming.md index afec869..bcbf364 100644 --- a/docs/theming.md +++ b/docs/theming.md @@ -14,32 +14,42 @@ 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 | ✗ | +| Preset | Style | Accent | Fonts | Hero | Frame | Grain | +| ------------- | ------------------------------ | ------------ | -------------------------- | --------- | ------------ | ----- | +| **studio** | Leica-inspired, editorial | 🔴 `#e60012` | Playfair Display + DM Sans | Split | Passepartout | ✓ | +| **minimal** | Swiss brutalist, high contrast | ⚫ `#000000` | Geist + IBM Plex Mono | Fullbleed | None | ✗ | +| **editorial** | Cinematic magazine, warm tones | 🟤 `#8B2500` | Bodoni Moda + Newsreader | Split | Shadow | ✗ | +| **classic** | Gilded gallery, warm luxury | 🟡 `#c49a3c` | Cinzel + Crimson Pro | Minimal | Passepartout | ✗ | Default is `studio` if no theme is specified. +### What makes each theme unique + +**Studio** (default) — Leica-inspired with a red nav dot, passepartout photo frames, film grain, and a split hero layout. Clean editorial feel with Playfair Display headings. + +**Minimal** — Swiss Brutalist aesthetic inspired by Müller-Brockmann and Dieter Rams. Fullbleed hero image, true black/white palette, zero-gap photo grid with no hover effects, ultra-tiny navigation text. Every pixel earns its place. + +**Editorial** — Cinematic magazine feel inspired by Aperture and Magnum Photos. Oversized Bodoni Moda serif titles, warm charcoal backgrounds, generous grid spacing, slow cinematic transitions with desaturation-on-idle photos, and pull-quote section labels. + +**Classic** — Fine art gallery aesthetic with warm gold accents, Cinzel Roman capitals for headings, decorative ornamental dividers, warm passepartout frames, rounded corners, and an elegant gold header dot. Feels like a luxury exhibition catalog. + ## 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) + 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" + 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. @@ -61,6 +71,7 @@ Google Fonts names. Fonts are loaded automatically — just use the name as it a ### `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 @@ -84,38 +95,53 @@ When `true`, shows a small accent-colored dot in the navigation bar (inspired by ### `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 + +- **`split`** — title/nav on the left, hero image on the right (used by Studio and Editorial) +- **`fullbleed`** — hero image fills the entire viewport, title overlaid in the corner (used by Minimal) +- **`minimal`** — centered title card with decorative ornament, hero image as a banner below (used by Classic) ## Examples ### Minimal portfolio + ```yaml theme: preset: minimal - accent: "#0066cc" + accent: '#0066cc' fonts: - heading: "Outfit" - body: "Outfit" + heading: 'Outfit' + body: 'Outfit' ``` ### Film photography blog + ```yaml theme: preset: studio - accent: "#d4a017" + accent: '#d4a017' grain: true photoFrame: passepartout ``` ### Modern magazine + ```yaml theme: preset: editorial radius: 12 fonts: - heading: "Fraunces" - body: "Inter" - caption: "IBM Plex Mono" + heading: 'Fraunces' + body: 'Inter' + caption: 'IBM Plex Mono' +``` + +### Luxury gallery + +```yaml +theme: + preset: classic + accent: '#b8860b' + fonts: + heading: 'Playfair Display' + body: 'Lora' ``` diff --git a/lib/config.ts b/lib/config.ts index d731a58..cd472be 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -58,19 +58,19 @@ const THEME_PRESETS: Record = { }, minimal: { preset: 'minimal', - accent: '#666666', - fonts: { heading: 'Inter', body: 'Inter', caption: 'JetBrains Mono' }, + accent: '#000000', + fonts: { heading: 'Geist', body: 'Geist', caption: 'IBM Plex Mono' }, radius: 0, photoFrame: 'none', grain: false, headerDot: false, - heroStyle: 'split', + heroStyle: 'fullbleed', }, editorial: { preset: 'editorial', - accent: '#1a1a1a', - fonts: { heading: 'Libre Baskerville', body: 'Source Sans 3', caption: 'Source Sans 3' }, - radius: 4, + accent: '#8B2500', + fonts: { heading: 'Bodoni Moda', body: 'Newsreader', caption: 'Spectral' }, + radius: 0, photoFrame: 'shadow', grain: false, headerDot: false, @@ -78,13 +78,13 @@ const THEME_PRESETS: Record = { }, classic: { preset: 'classic', - accent: '#8b6914', - fonts: { heading: 'Cormorant Garamond', body: 'Lato', caption: 'Lato' }, - radius: 8, - photoFrame: 'none', + accent: '#c49a3c', + fonts: { heading: 'Cinzel', body: 'Crimson Pro', caption: 'Crimson Pro' }, + radius: 12, + photoFrame: 'passepartout', grain: false, - headerDot: false, - heroStyle: 'split', + headerDot: true, + heroStyle: 'minimal', }, };