Skip to content

mattjbones/portfolio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

64 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

photos by matt

See WERF.md for werf reference (binary, config, templates, variables, filters, quirks).


Site structure

portfolio/
  site/
    _config.yml             # title: "photos by matt"
    _layouts/
      default.liquid        # HTML shell — header/nav/footer wrapper
      photo.liquid          # standalone full-bleed layout (no default wrapper)
      gallery.liquid        # wraps content in .gallery grid
    _includes/
      head.liquid           # meta, Google Fonts (Caveat), CSS link
      nav.liquid            # site title + nav links
      photo_card.liquid     # card with hover overlay (no visible caption)
    _posts/                 # one .md per photo, YYYY-MM-DD-slug.md
    _pages/
      index.html            # home gallery (all photos, uses gallery layout)
      about.md
      [tags].html           # auto-generates one page per tag
    css/
      main.css              # single file, no framework
    public/
      photos/
        originals/
          YYYY-MM-DD/       # scans grouped by date
        thumbs/
          YYYY-MM-DD/       # generated by generate-thumbs.sh, mirrors originals/
    _headers                # Cloudflare CDN cache policy
  uploads/
    YYYY-MM-DD/             # drop zone: originals + .jpg.xmp sidecars (gitignored)
  processed/
    YYYY-MM-DD/
      mains/                # 80% quality re-saves (temp, deleted after upload)
      thumbs/               # 50% quality re-saves (temp, deleted after upload)
  scripts/
    build.sh                # Cloudflare entry point (download werf, thumbs, build)
    start.sh                # local dev (download werf if stale, watch mode)
    new-photos.sh           # consolidated upload workflow (process → R2 → post stubs)
    generate-thumbs.sh      # legacy: thumbnail generation from site/public/photos/
    new-photo-posts.sh      # legacy: post stubs from site/public/photos/originals/
    extract-exif.sh         # optional EXIF reader (film scans rarely have useful data)
  WERF.md
  README.md

New photo workflow

TL;DR

  1. Put JPEGs + matching .jpg.xmp sidecars in uploads/YYYY-MM-DD/
  2. Run a safe local pass: ./scripts/new-photos.sh YYYY-MM-DD --dry-run
  3. If output looks good, run publish: ./scripts/new-photos.sh YYYY-MM-DD
  4. Open new files in site/_posts/ and fill remaining fields (description, tags, exposure_compensation)

Scripts

scripts/new-photos.sh — primary upload workflow (see New photo workflow):

  • Reads originals from uploads/YYYY-MM-DD/ (never touches site/)
  • Produces mains (80% quality, max 2000px wide) and thumbs (50% quality, max 800px wide) in processed/YYYY-MM-DD/
  • Parses darktable XMP sidecars (<file>.jpg.xmp) to pre-populate frontmatter
  • Uploads to R2 via wrangler r2 object put; tracks per-file failures without aborting
  • Creates site/_posts/YYYY-MM-DD-<slug>.md stubs (skips existing)
  • Deletes processed/YYYY-MM-DD/ on full success; uploads/YYYY-MM-DD/ is kept
  • Idempotent: skips already-processed files, skips existing post stubs
  • --dry-run flag to run in local mode (publishes to site/public/photos and uses /public/photos/... URLs)
  • --rewrite-urls flag to update image/thumb URLs in existing posts for that date
  • Prerequisites: ImageMagick (magick or convert), wrangler

scripts/generate-thumbs.sh (legacy, superseded) — generates thumbnails from site/public/photos/originals/

scripts/new-photo-posts.sh (legacy, superseded) — creates post stubs from site/public/photos/originals/

scripts/extract-exif.sh — optional; prints raw EXIF to stdout for a single image. Film scans rarely have useful EXIF.


Per-photo frontmatter schema

---
layout: photo
title: "2026-02-24 / 0001_37"
date: 2026-02-24
film_name: Kodak Gold 200
film_format: 35mm
film_speed: 200
film_type: colour negative
developed_by: AG Photolab
exposure_compensation: box  # box / +1 / -1 / +2 etc.
camera: Olympus OM-1
lens: Zuiko 50mm f/1.4
location: London

tags:                       # space-separated, drives auto-generated tag pages

image: https://pub-d1e192acd3c5456eb06f306d0bd48e3d.r2.dev/photos/originals/2026-02-24/0001_37.jpg
thumb: https://pub-d1e192acd3c5456eb06f306d0bd48e3d.r2.dev/photos/thumbs/2026-02-24/0001_37.jpg

description:
---

XMP note: new-photos.sh auto-populates film_name, film_format, film_speed, film_type, developed_by, camera, lens, and location from darktable XMP sidecar hierarchical subjects. Fields not present in the XMP are left blank for manual entry.


Design

Fonts

  • Caveat (Google Fonts, handwritten) — site title only
  • Georgia serif — photo card titles on hover overlay
  • System sans-serif — body, nav, meta

Gallery

  • CSS Grid, 6 equal columns, fixed row height (280px)
  • Varying column spans per card: span 2, 1, 1, 1, 2, 1 repeating — gives varied widths at uniform height
  • Last card always grid-column-end: -1 to fill remaining row space
  • object-fit: cover on all images
  • Hover: image scales up, gradient overlay fades in with title + meta in white

Photo page

  • Standalone layout (photo.liquid, not wrapped in default)
  • Image fills full viewport (100dvh), black background, object-fit: contain
  • Floating nav bar at top fades into image (gradient overlay)
  • Scroll down for title, metadata table, tags — dark/moody style

Color scheme

  • Light: #fafafa bg, #111 fg
  • Dark (prefers-color-scheme): #111 bg, #e8e8e8 fg
  • Photo page always dark (#000 bg)

Deployment on Cloudflare Pages

Setting Value
Pages project name photosbymatt
Production URL https://photosbymatt.pages.dev
Build command bash scripts/build.sh
Build output directory dist
Root directory /

scripts/build.sh downloads the werf Linux binary (tagged by date, requires GITHUB_PAT), generates thumbs, builds the site, then copies site/_headers to dist/_headers.

Cloudflare cache headers

/public/photos/*
  Cache-Control: public, max-age=31536000, immutable

/css/*
  Cache-Control: public, max-age=86400

Cloudflare Pages only applies custom headers from an output-root file named _headers. This repo keeps the source headers in site/_headers; the build script copies it to dist/_headers so image/CSS cache policies are actually applied.

max-age=31536000, immutable means images can be cached by browsers/CDN for up to one year, so replace image URLs (or filenames) when publishing updated versions of an image.


Open Items

  • Fill in metadata (title, film, camera, lens, location, tags) for the 19 real posts
  • Wire up Cloudflare Pages (manual, in dashboard)
  • Add favicon
  • About page content

About

photos by matt

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors