Skip to content
Web Development1 June 2026 · 5 min read

Behind the build: Helia, a solar installer site that feels alive

Most solar installer sites are stock panels and a quote form. Helia is the fifth in a five-template marketplace — a Framer Motion + Boomerang-video build that earns attention without selling sustainability theater.

Behind the build: Helia, a solar installer site that feels alive

Key takeaways

  • Helia is the fifth and final template in a five-site marketplace, built as a solar installer site that reads as a living system rather than a static brochure.
  • It is the only template of the five to pull in Framer Motion (via the window.Motion UMD bundle), because the hero needs a boomerang-style forward-reverse video loop and the OrbitImages component animates concentric arcs of imagery.
  • The OrbitImages section rotates six concentric rings of images around a center logo, with each ring's images counter-rotated so they stay upright relative to the viewer while the ring orbits.
  • A QA bug split "infinite" into "infi nite" on a 375px viewport because the StaggeredFade per-character spans were display: inline-block; the fix was switching the chars to display: inline with real spaces.
  • Helia uses a lighter opacity-only WAAPI reveal (300ms fade, no transform) so the always-on background motion carries the energy instead of twitchy per-section rise-up animations.

Solar installer websites have a problem: they all look the same. A wide hero photo of glistening panels on a tile roof, three "trusted by" badges, a calculator that asks for your zip code, a quote form below the fold. The category sells a product that physically moves the sun's output into a homeowner's wall outlet — and every site about it looks static.

Helia is the fifth and final template in a five-site marketplace I shipped this week. The brief: a solar installer site that reads as a living system, not a brochure.

Stack

Static HTML per page (Helia Landing.html, Helia Solar.html, Helia Storage.html...). React 18 UMD. In-browser Babel. Tailwind via CDN. The new piece for this template: Framer Motion, loaded via the window.Motion UMD bundle.

Why bring in a motion library for one site when the other four ship without it? Because Helia's hero needs a boomerang-style video loop — play forward, play reverse, repeat — and the OrbitImages component needs to animate concentric arcs of imagery. WAAPI plus CSS keyframes could do it, but Motion's animate + useReveal patterns made the orbit logic one component instead of three.

The hero

The hero is a full-bleed BoomerangVideoBg of an aerial solar farm shot at golden hour. A green-tinted gradient overlay softens it for legibility. A pill at the top reads "Powering tomorrow · 24.6 MW live" — a fake live counter, but the format is real and the same pattern works wired to a metrics endpoint.

Below: a serif headline ("Renewable power for tomorrow, infinite clean solutions"), a calm subtitle, two CTAs stacked on mobile. The primary is a soft gradient pill ("Explore options") with a play icon; the secondary is a clean white pill ("Start network") with an arrow.

OrbitImages

The "powered by" section is six concentric rings of imagery rotating around a center logo. Each ring is a Motion.div with animate={{ rotate: 360 }} and a per-ring duration. The trick: the images inside each ring are themselves rotated counter to the parent rotation, so they stay upright relative to the viewer while their ring orbits.

CSS variables (--gx, --gy) feed a radial glow that follows the cursor inside the center logo card — pure onMouseMove updating two custom properties.

Two bugs I caught in QA

1. The "infi nite" word break. The hero headline uses a StaggeredFade component that splits the string into per-character spans for the entrance animation. Those spans were display: inline-block, which meant any non-breaking space between them broke at the word boundary instead of staying glued. "infinite" wrapped as "infi nite" on the 375-wide viewport. Fix: switch the chars to display: inline and use real spaces — the inline-block was inherited from an older variant of the same component that needed independent transforms.

2. The helia/ subdir ghost references. When I flattened the source layout (originally everything lived in helia/), 16 HTML files still pointed at helia/lib.jsx, helia/primitives.jsx, and so on. The first deploy 404'd half the scripts. A sed pass over every HTML file stripped the prefix and the next deploy was clean.

Reveal animation

Helia uses a lighter reveal than the other four templates — WAAPI element.animate([{opacity: 0}, {opacity: 1}], {duration: 300, ...}). Opacity only, no transform. The reason: the hero is so dense with motion already (boomerang loop, orbit rings, gradient drift) that adding rise-up sections to every block read as twitchy. Cutting the per-section motion to a 300ms fade let the always-on background motion carry the energy.

What I would change

The OrbitImages performance dips below 60fps on the lowest-end Android I tested (a 3-year-old Moto G). Six rings × ~8 images = 48 transformed elements at 30Hz. A future pass would pre-bake a sprite atlas and animate a single canvas — fewer reflows, identical visual.

Live

helia-template-05.netlify.app


Part of the Building Web Templates: Design, Engineering, and Selling guide.

Free resource

Free SaaS MVP Scope Template

A Notion document with the full feature checklist, MVP vs. nice-to-have table, pre-build questions, and cost signals — so you walk into any developer call knowing exactly what to ask for.

Get the template →
DL

Dusko Licanin

Full-Stack Developer · Banja Luka, Bosnia

Full-stack developer shipping SaaS MVPs, web apps, and mobile apps 2× faster than agencies using AI-augmented workflows. Live portfolio: BookBed, Callidus, Pizzeria Bestek.