Quick Start
Open index.html in a browser, or serve the folder statically. No build step,
no framework. CDN scripts for GSAP 3.15.0 (core + ScrollTrigger) and Lenis
1.3.17; the page degrades to a plain readable document if they are blocked.
Sections (each independently deletable)
- Hero (
data-hero) — paper field, plain headline, one photograph at the edge, the frame read in a hairline fact table. - Contact sheet (
data-strip) — six frames in one sideways run. Scrolling moves through a pinned section with direct scrub; a counter tracks progress. Click any frame for the viewer with camera settings, arrow keys move between frames, Esc closes, and every frame has an order button that adds to the basket. Without JavaScript the rail scrolls normally with snap. - Wall (
data-wall) — ten prints at mixed sizes on one dark ground. Scrolling moves across it through a short pinned section, dragging pans, zoom controls go to 180%, every print opens the viewer, and a small prompt invites the first drag. Static mosaic without JavaScript or motion. - About (
data-about) — two-column statement and a ruled facts table, portrait at the edge. - Prints (
data-prints) — editions as a ruled table, then the estimate form: shoot, days stepper, season and extras update a live price with a written summary, then name, email and submit returns a reference number. Real radio buttons and checkboxes underneath keep it a working plain form with JavaScript off. Nothing is sent anywhere until you point it at Formspree, Netlify Forms or your own inbox.
Delete any <section> and the rest keeps working; the nav, sections overlay
and footer do not depend on section scripts.
Palette
Paper #FAFAF8, card #FFFFFF, ink #131313, fjord #2F454B. Repaint those
four values at the top of assets/style.css and the site reskins. Every
translucent value resolves from the --*-rgb triples. Measured pairs: ink
on paper 15.8:1, dim body variant 6.5:1, faint meta 4.7:1, fjord on paper
8.4:1 (numerals, rules, selected states; paper on fjord the same
reversed for selected type). Fjord never washes a photograph.
Type: Inter Tight throughout. Medium sentence-case headlines, plain small
labels. Swap the single --font-sans token to re-voice the page.
Images
Bundled photos are optimised WebP files (assets/img/, about 1.4MB total),
graded as one set from Pexels sources. The originals are never redistributed.
- Swap photos: replace the files in
assets/img/, or edit the source URLs inassets/img-manifest.jsonand re-runbun scripts/build-template-assets.ts photographer-template. - Full-width photos want 1600px wide or more; small tiles are baked at 800px.
- Credits (Pexels licence, free to use, no attribution required):
- Harbour mouth, the Mount: https://www.pexels.com/photo/12773530/
- Storm shore, Porthcurno: https://www.pexels.com/photo/9764167/
- Pine stand in mist, Tehidy: https://www.pexels.com/photo/12944764/
- Working deck, PZ 115: https://www.pexels.com/photo/19862769/
- Black cliffs from the water: https://www.pexels.com/photo/33912069/
- Chapel in the mist: https://www.pexels.com/photo/33649232/
- Portrait in natural light: https://www.pexels.com/photo/18051189/
- Hands holding camera: https://www.pexels.com/photo/13827131/
- Breakers on rock: https://www.pexels.com/photo/9076557/
Accessibility
Keyboard: every frame, viewer control, builder control, menu control and field is reachable; arrows move through the viewer, Escape closes viewer, menu and basket. Reduced motion gets no pinned sections, a resting hero, a viewer with no flight, a static wall mosaic with working zoom, and a form that adds up without counting. The basket, the wall drag and the zoom are click-state, so they work in every motion branch. Without JavaScript: headline with photograph and fact table, a scrollable captioned rail, four stacked prints, the editions as a plain price table, and the form as a plain set of fields with its rates listed.
Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.