Harbor Light ← Back to the site

Build Guide

How Harbor Light Church was built.

A specific, unglamorous write-up of the concept, the art direction behind every image, the real CSS and JS techniques behind the motion, and how this folder ships as a static site. No framework, no build step — just HTML, CSS, and GSAP.

Plain HTML / CSS / JS GSAP 3 + ScrollTrigger Higgsfield MCP imagery Deploys to Netlify
01

The concept

Harbor Light is one of two churches in this showcase set, and the brief was explicit that it needed to be visually distinct from its counterpart — that one goes bold and contemporary; this one goes calm and luminous. So the whole site is organized around one idea: light through glass. Not a literal stained-glass window (that reads as "church clip art" fast), but the effect of light — dawn/dusk gradient washes, a soft prism-of-color accent used only a few times, and photography that leans on natural window light rather than a stage and a worship band.

The copy voice follows the same instinct: warm and direct, never corporate. Section headings like "Come as you are, leave lighter" and body copy like "no awkward stand-up-and-introduce-yourself moments, we promise" are doing the work of making the place feel like somewhere a first-time visitor would actually want to walk into.

02

Palette & type

Every color in the system is deliberately desaturated. The brief called for "luminous rather than saturated," so nothing here is a pure, punchy hue — golds, blues, and blush are all pulled toward pale/dusty versions of themselves.

Warm White#FBF6EE
Gold#CF9F4F
Dusty Blue#7F95A8
Blush#E3AF9F
Ink#2C2722
Blue Deep#445A6E

Type is a two-family pairing: Fraunces (a variable, optical-size serif with real warmth in its italic cut) carries every headline, and Inter handles body copy, labels, and UI text so it never competes with the serif. The "leave lighter" hero line deliberately uses Fraunces' italic — it's the single most expressive type moment on the page, so it only happens once.

03

Generating the imagery

Every photo on the site was generated through the Higgsfield MCP using Nano Banana Pro at 2K resolution, then converted to WebP (cwebp -q 82 -m 6) to bring the asset folder down from roughly 73MB of PNG to about 2.3MB total. Each image was art-directed specifically for this palette — the warm gold, dusty blue, and blush tones were named in every brief on purpose, so the generated photography would sit naturally inside the CSS color system instead of fighting it.

Every shot — the golden-hour sanctuary hero, the dusk exterior, the community circle, the prism close-up, the clasped hands, the still-life journal, the golden-hour gathering, and the kids-and-family scene — was generated from a carefully art-directed brief specifying the lighting, lens character, color grade, composition, and mood, then iterated until it matched the brand. Several were regenerated when a first pass drifted off-brief — the sanctuary hero, for instance, initially came back with literal religious iconography the brief explicitly avoided, and was re-directed until the room read as pure architectural light and space. The full prompt library behind the set is proprietary and available to clients on request.

04

Motion & build techniques

Everything runs on GSAP 3 loaded straight from the jsDelivr CDN, plus the ScrollTrigger plugin — no bundler, no npm install. A single js/main.js file registers ScrollTrigger once and wires up every animation declaratively.

CSS keyframes

Gradient-sky drift

The hero's .hero-sky layer is an oversized --sky-dawn linear-gradient animated purely in CSS — background-position drifting between two points over 26s, ease-in-out alternate. No JS needed for this one; it's the cheapest possible always-on ambient motion.

GSAP scrub

Hero parallax

A ScrollTrigger-scrubbed timeline (scrub: 0.6) moves the hero photo yPercent/scale and fades the text block to 20% opacity as you scroll past it, so the hero dissolves into the welcome strip instead of hard-cutting.

Blend mode

Light-ray sweep

repeating-conic-gradient thin gold wedges with mix-blend-mode: screen, tweened back and forth on background-position — an animated light-ray texture rather than a static PNG overlay.

Radial glow

Prism accent motif

Blurred, low-opacity radial-gradient circles (.prism-glow) placed behind section heads, each given an independent slow drift (sine.inOut, 10-14s, yoyo). This is the "stained-glass prism motif used sparingly" from the brief — refracted color as atmosphere, never a literal window.

ScrollTrigger

Reveal-on-scroll system

Every content block carries a .reveal class (opacity:0; translateY(34px)); one ScrollTrigger per element handles single blocks, and a data-stagger wrapper batches grouped items (service-time cards, ministry cards, event rows) with gsap.utils.toArray() + stagger: 0.12 — declarative, no per-section animation code duplicated.

Count-up

Stat animation

The "12 yrs" tag in the About section tweens a plain JS number object (gsap.to(obj, {val:12, onUpdate...})) and writes it into the DOM on each frame, triggered once when it scrolls into view.

Nav state

Scroll-aware navigation

One ScrollTrigger.create({start:60, onUpdate}) toggles an .is-scrolled class that swaps the nav from transparent/white-on-photo to a blurred paper background with ink text. The "Contact" pill button gets the same light/dark swap explicitly — worth calling out because it was a real bug caught in QA: the ghost-button style defaulted to dark ink text even when the nav was transparent over a photo, which made it unreadable at the top of the page.

05

Responsive approach

Layout is CSS Grid/Flexbox throughout, with breakpoints at 1080px (two-column sections collapse to one) and 720px (nav collapses to a full-screen mobile menu, grids go to one column, service-time cards and ministry cards stack). Below 720px the desktop-only decorative elements — the vertical "SCROLL" cue, the event location column — are hidden rather than squeezed, since they're not load-bearing content.

The mobile menu itself is a simple full-screen panel using a CSS transform: translateY(-100%) → translateY(0) transition — deliberately not GSAP, since a single toggle didn't need a JS animation library.

06

Deployment

This is a fully static site — no server-side code, no build step, no environment variables. To ship it:

  1. Drag the harborlight-church/ folder straight into Netlify's manual deploy dropzone, or
  2. Connect the repo in Netlify and set the base directory to harborlight-church/ with an empty build command (there's nothing to compile) and publish directory .

Every asset path in index.html and guide/index.html is relative, so the folder also works opened directly from disk or served from any static host without configuration.