Skip to content
All projects
Marketing SiteStatic WebsitesStatus: Live

Star Photography

Portfolio marketing site with accessible gallery lightbox, smart promo popup, and async booking forms

Screenshot of Star Photography

Portfolio marketing site for a fictional portrait/wedding photographer — keyboard-accessible lightbox, IntersectionObserver reveals, exit-intent promo popup with localStorage memory, and async Netlify form submissions. No frameworks, no build step.

Problem

The brief I set myself: a gallery-first photography marketing site that converts to bookings, handles image-heavy pages performantly, and runs entirely on a static stack with zero framework dependencies.

Goal

Ship a visually-driven marketing site with an accessible lightbox, smart promo capture, and optimized imagery — proving that a fully vanilla stack can still hit modern UX expectations.

What I Built

Hand-written HTML, CSS and JavaScript — no framework — with a keyboard-accessible portfolio lightbox (focus trap, arrow navigation, escape-to-close), IntersectionObserver-driven scroll reveals, a promo popup with time/scroll/exit-intent triggers plus 7-day localStorage dismissal memory, async Netlify form submissions via the Fetch API, and responsive Cloudinary images with `f_auto,q_auto` transforms and srcset.

Key Features

  • Portfolio gallery with keyboard-accessible lightbox (focus trap, arrow keys, escape-to-close)
  • Promo popup with three trigger paths: idle time, scroll depth, and exit intent
  • 7-day localStorage dismissal memory so the popup never nags the same visitor
  • IntersectionObserver scroll reveals — no scroll listeners, no jank
  • Responsive Cloudinary imagery with srcset + f_auto/q_auto auto-optimization
  • Mobile menu with body scroll lock and focus handling
  • Async form submission via fetch (no page reload) to Netlify Forms
  • Services and pricing, testimonials, newsletter signup, and contact form

Challenges

  1. Building a fully accessible lightbox from scratch — focus trap, keyboard nav, scroll lock — without a library
  2. Tuning the promo popup to feel helpful, not annoying: three trigger modes plus dismissal memory required careful UX thresholds
  3. Handling a gallery-heavy page performantly — Cloudinary transforms + srcset instead of shipping full-size originals
  4. Making Netlify Forms submit asynchronously via fetch without losing the build-time form registration handshake

Outcome

A gallery-heavy site that still loads fast: Cloudinary transforms and srcset instead of full-size originals, plus a hand-built lightbox that survives keyboard-only navigation. No framework, no build step — production-grade image handling on a vanilla static stack.