Build Guide

How this site was made.

A working document of the concept, the art direction behind every image, the CSS/JS techniques behind the motion, and how it ships. Written for anyone who wants to build the next one.

01

The brief

Ember Collective is the second of two church concepts in this set, deliberately built as the opposite of its sibling. Where the other church leans calm and luminous, Ember goes bold, dark, and kinetic — closer to a music label or streetwear drop than a traditional church homepage. The goal was a site a 24-year-old skeptic would screenshot and send to a friend, not one their parents would print out.

The tension to hold: bold without being cold. The visual language is loud — near-black backgrounds, ember particles, huge type — but every line of copy underneath it is warm, plain-spoken, and aimed squarely at someone who's scared church isn't for them anymore.

02

Palette & type

#0A0705
near-black
#15110C
charcoal
#FF4A1C
ember
#C31F0A
ember deep
#FFAB3D
amber
#FFD28A
amber soft
#F5EEE3
off-white

Two type families, used deliberately against each other: Unbounded (Google Fonts, weights 200–900) carries every headline and kinetic statement — it's geometric with real personality in its heavier cuts, which is what gives the hero its music-poster energy. Inter handles everything functional: body copy, nav, labels, form fields. Labels are set in Inter at 12px, uppercase, 700 weight, 0.22em tracking — that's the "grotesque label" texture you see on every eyebrow line (the small dot + amber caps above each heading).

03

Imagery: art direction

All photography was generated with Higgsfield's soul_cinematic model ("cinema-grade stills and concept art") specifically because it holds dramatic lighting and film grain better than general-purpose models. Each of the seven images — hero crowd, ember texture, silhouette portrait, community group, raised hands, night exterior, and speaker silhouette — was generated from a carefully art-directed brief specifying an explicit lens, lighting setup, color grade, composition, and mood, with a standing instruction to avoid readable text and religious iconography/logos — the brief was gathering-energy and warmth, not literal symbols. Each was iterated until it sat inside the brand. The full prompt library is proprietary and available to clients on request.

All seven came back as 2K PNGs (1.4–2.5MB each). They were re-encoded to quality-82 JPEGs with macOS sips before landing in assets/, dropping the folder from ~15MB to ~1.8MB total with no visible quality loss at web sizes.

04

The ember particle field

The ambient glow you see drifting behind every section is a single <canvas id="ember-field">, fixed to the viewport at z-index:0, sitting under all page content, with mix-blend-mode:screen so the embers light up against the dark background instead of muddying it. It's hand-rolled vanilla JS — no library — in js/particles.js.

Each particle is a plain object: position, radius, upward speed, a slow sideways drift derived from a running sine wave ("wobble"), a flicker phase, and one of four palette colors. Every frame, each particle is drawn twice: once as a soft radialGradient circle (the glow — cheaper on the GPU than shadowBlur at this particle count) and once as a tiny bright core on top, so it reads as a real ember rather than a blurry dot. When a particle drifts above the top of the viewport it's respawned at the bottom with fresh randomized properties — a cheap, infinite loop with no garbage-collection spikes.

Density scales with viewport area (roughly one particle per 15,000px², clamped 18–120) and is cut nearly in half under 700px width. The field also reacts gently to the cursor — particles within ~120px get pushed outward, so scrubbing your mouse across the hero visibly disturbs the embers. On prefers-reduced-motion: reduce the field renders one static, sparse frame instead of animating, and the whole loop pauses via the Page Visibility API when the tab isn't focused.

05

Kinetic type & scroll motion

Animation runs on GSAP 3 + ScrollTrigger loaded from a CDN — no bundler. The hero headline is split into per-word <span>s at runtime by splitHeroWords(), then GSAP staggers them in with yPercent:120, a slight rotation, and power4.out easing so each word lands with a bit of overshoot-free snap, like it's being stamped into place — a deliberate alternative to the generic "fade up" reveal.

Every other section uses a shared [data-reveal] pattern: elements start at opacity:0; translateY(36px) in CSS, and a single ScrollTrigger-driven loop in main.js animates them to their resting state as they cross 88% of the viewport, with a small staggered delay so grouped elements (stat rows, expect-list items) don't all land in the same frame. Background images on the "what to expect" and about sections move at a different scroll speed than the foreground copy via data-parallax attributes scrubbed to scroll position — real depth, not just a static hero image.

Two smaller, more "crafted" touches instead of relying purely on the reveal pattern: (1) every .eyebrow label gets a one-shot RGB-split glitch flicker the first time it scrolls into view — a 3-step steps() keyframe animation nudging red/amber text-shadow offsets — a nod to the streetwear/glitch aesthetic without overusing it; (2) the ticker strip under the hero is a duplicated flex track animated with a plain CSS @keyframes loop at translateX(-50%), the classic infinite-marquee technique lifted straight from music/label sites.

06

Layout system

The whole page runs on CSS Grid and custom properties, no framework. Editorial asymmetry comes from deliberately uneven grid fractions rather than 50/50 splits — the about section is 0.85fr 1.15fr, the community section is 1.2fr 0.8fr — plus an offset "tag" card on the about portrait that breaks its own frame with negative margins (bottom:-22px; left:-22px), a classic magazine-layout trick for confident cropping. Section headings sit inside a capped max-width rather than spanning full width, which keeps line lengths editorial instead of full-bleed and loud everywhere at once.

07

Deployment

This is a static site — plain HTML/CSS/JS, zero build step, zero dependencies beyond two CDN scripts (GSAP + ScrollTrigger) and a Google Fonts stylesheet link. It's deploy-ready as-is:

  1. Push the ember-collective/ folder to a Git repository (or drag-and-drop it directly).
  2. Connect it to Netlify as a new site — no build command needed, publish directory is the project root (where index.html lives).
  3. Netlify serves index.html at the root and this page at /guide automatically since folder-based routing maps straight to guide/index.html.
  4. Point a custom domain at the Netlify site and enable HTTPS — done through Netlify's dashboard, no code changes required.

Because there's no build step, there's also nothing to break between "it works on my machine" and production — what's in the folder is exactly what ships.

← Back to Ember Collective