# Theming Control the visual identity of your gallery from `content/settings.yaml`. Choose a built-in preset or customize individual properties β€” no CSS editing required. ## Quick Start Add a single line to your `settings.yaml`: ```yaml theme: minimal ``` That's it. Your entire gallery switches to the minimal theme. Leave it out and you get `studio-modern`, the default. ## Built-in Presets | Preset | Style | Accent | Fonts | Hero | Frame | Grain | | ----------------- | ------------------------------ | ------------ | --------------------------------- | ----------- | ------------ | ----- | | **studio-modern** | Leica precision, grotesque | πŸ”΄ `#e60012` | Archivo + IBM Plex Mono | Split | None | βœ— | | **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 | βœ— | | **noir** | Darkroom analog, film noir | 🟠 `#ff6b35` | Libre Baskerville + Source Sans 3 | Fullbleed | Passepartout | βœ“ | | **monograph** | Typographic, book-like | ⬛ `#333333` | Instrument Serif + Inter | Typographic | None | βœ— | **`studio-modern` is the default** if no theme is specified. ### Theme Gallery _From left to right, top to bottom: Studio Modern, Studio, Minimal, Editorial, Classic, Noir, Monograph_

Studio Modern theme Studio theme Minimal theme Editorial theme Classic theme Noir theme Monograph theme

### Grid View Examples _Same order as above._

Studio Modern grid Studio grid Minimal grid Editorial grid Classic grid Noir grid Monograph grid

### Light Mode Every preset ships with a light variant. Visitors switch with the toggle in the navigation bar; the choice is remembered in `localStorage`, and the presets keep their accent, fonts and framing in both modes.

Studio Modern in light mode Studio Modern grid in light mode

### What makes each theme unique **Studio Modern** (default) β€” The Leica language of `studio` rebuilt around the precision grotesque Archivo, with IBM Plex Mono for every piece of photographic metadata (EXIF chips, counters, indices). Hairline rules at 8% opacity, zero radius, an indexed hero nav with album counts, an always-visible caption bar under album covers, and a film-edge EXIF strip in the lightbox. Red is signal only β€” nav dot, active state, hover, indices, map markers. **Studio** β€” 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. **Noir** β€” Darkroom analog aesthetic with warm amber accents on a deep cool-black base. Sepia-tinted photos with vignette hover effects, film-edge EXIF labels in monospace, and a grain overlay. Inspired by wet-plate photography and film noir cinematography. **Monograph** β€” Type-first book design with no hero image. Features an 8rem serif title, numbered photo indices via CSS counters, slide-up EXIF captions, hairline dividers, and generous whitespace. Feels like an artist monograph. ## Custom Theme Start from any preset and override individual properties: ```yaml theme: preset: studio # base preset to extend (default: studio-modern) 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" | "stacked" | "typographic" | "mosaic" | "cover" ``` 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 (used by Studio, Editorial) - **`fullbleed`** β€” hero image fills the entire viewport, title overlaid in the corner (used by Minimal, Noir) - **`minimal`** β€” centered title card with decorative ornament, hero image as a banner below (used by Classic) - **`stacked`** β€” full-viewport hero image with title gradient-overlaid at the bottom, horizontal thumbnail navigation strip below - **`typographic`** β€” no hero image; massive centered title with numbered album navigation list (used by Monograph) - **`mosaic`** β€” asymmetric multi-image grid with frosted-glass title overlay centered on top - **`cover`** _(experimental)_ β€” a splash screen: one fullscreen image, the site title and subtitle, and a single **Enter** link into the first navigation entry. The rest of the homepage is not rendered β€” visitors arrive at a title page and click through.
Split Fullbleed Minimal
Split Hero Fullbleed Hero Minimal Hero
Stacked Typographic Mosaic
Stacked Hero Typographic Hero Mosaic Hero
### `grid.layout` Controls the photo grid layout on album pages: - **`masonry`** β€” Pinterest-style stacked columns, images shown at natural aspect ratios (default) - **`uniform`** β€” CSS Grid with fixed aspect ratio cells - **`showcase`** β€” first image displayed at full width (16:9), rest in standard grid - **`filmstrip`** β€” horizontal scroll of tall vertical image strips with scroll snapping - **`editorial-flow`** β€” alternating full-width (21:9) and side-by-side (4:3) image pairs - **`justified`** _(experimental)_ β€” row-based: every row fills the container width and all images in a row share one height, so aspect ratios stay intact and nothing is cropped. `columns` sets the density β€” more columns give shorter rows. The last row keeps its natural sizes rather than stretching. - **`essay`** β€” not a grid: renders the page as a photo essay. See **[Journal & Photo Essays](journal.md)**. Set it globally in `settings.yaml`, per subpage in `gallery.yaml`, or on a single album (experimental) β€” the three merge, most specific winning: ```yaml grid: layout: showcase ``` The same album in five of the grid layouts (theme `studio-modern`):
Masonry (default) Uniform
Masonry grid layout Uniform grid layout
Showcase Filmstrip
Showcase grid layout Filmstrip grid layout
Editorial Flow
Editorial flow grid layout
### Album cover spacing The album-cover grid on a subpage is sized by two CSS custom properties on `.subpage-grid`: | Property | Set by | | ------------------- | -------------------------------------------------------------------- | | `--subpage-columns` | the app, from the resolved `grid.columns` (site-wide or per subpage) | | `--subpage-gap` | the **theme preset** β€” a per-subpage `grid.gap` overrides it | The column count follows the configuration, the spacing belongs to the look. `monograph` sets 1px, `minimal` and `editorial` 2px, `studio-modern` 20px; `classic` and `noir` set nothing and get the 4px default from `globals.css`. A custom preset does the same: ```css [data-preset='mytheme'] .subpage-grid { --subpage-gap: 12px; } ``` Do not set `gap` directly there β€” an explicit per-page gap is passed as an inline custom property and would no longer win. ## 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' ``` ### Luxury gallery ```yaml theme: preset: classic accent: '#b8860b' fonts: heading: 'Playfair Display' body: 'Lora' ``` ### Dark analog portfolio ```yaml theme: preset: noir heroStyle: stacked ``` ### Type-forward monograph ```yaml theme: preset: monograph accent: '#444' fonts: heading: 'DM Serif Text' ``` ### Nature / travel journal ```yaml theme: preset: editorial heroStyle: mosaic grid: layout: editorial-flow ``` ## Upgrading & rolling back Adding a preset never requires a migration. Presets are additive, the `gallery.yaml` / `settings.yaml` schema is unchanged, and existing presets keep rendering exactly as before β€” pull the new version and restart. The one case that needs attention is a **rollback after switching presets**. The selected preset is stored in `content/settings.yaml`, so if you pick a preset that was added later and then downgrade to a version that predates it, config loading fails: ``` Unknown theme preset "studio-modern". Valid presets: studio, minimal, editorial, classic, noir, monograph ``` The message lists the presets the running version accepts. Set `theme.preset` to one of them: ```yaml theme: preset: studio # any preset the older version knows ``` `/admin` stays reachable when this happens, so you can also fix it from the settings editor after the downgrade rather than beforehand. The same error appears if you typo a preset name or copy an example for a preset that does not exist β€” it is not specific to downgrades.