Skip to content
Web Development1 June 2026 · 6 min read

Behind the build: Veloce, a car dealer template that actually moves

Most car dealer websites look like 2008 called and wanted its inventory grid back. Veloce is the first of five templates I shipped this week — a GSAP-driven dark car dealer build with cinematic video plates, sticky scroll heroes, and three bugs I caught in QA.

Behind the build: Veloce, a car dealer template that actually moves

Key takeaways

  • Veloce is a car-dealer template built with no build step: static HTML pages, React 18 UMD from a CDN, in-browser Babel, GSAP, and Tailwind Play CDN, shipping under 2 MB compressed from CDN edge.
  • The hero is a vertical-scroll slider where GSAP drives a clip-path ellipse from 5% to 150% of the viewport, with a requestAnimationFrame debounce holding 60fps on mid-tier Android.
  • Each hero video plate is a Gemini Veo generation transcoded to 1280-wide H.264 at CRF 26 with +faststart, its watermark removed via ffmpeg delogo, landing the whole hero at about 7 MB.
  • The section reveal animation was toned down for mobile by cutting translateY from 30px to 8px and the transition from 0.85s to 0.45s so sections stopped blinking on scroll.
  • Three QA bugs were fixed: a bfcache page-exit overlay zombie (reset on pageshow/popstate), a single-image gallery rendering a dead thumbnail (hidden when images.length === 1), and a mobile hero stack order corrected with order-1/lg:order-2.

Most car dealer websites look like 2008 called and wanted its inventory grid back. Filters that don't filter. Stock photos of cars the dealer doesn't sell. A "Book service" page that's a form behind two redirects. I wanted to build the opposite — a template that treats the visitor like an adult and gets out of the way.

Veloce is the first of five templates I shipped this week, all under the same constraint: no build step, no React-Router, no SPA bloat. Drop the folder on Netlify and it works.

Stack picks (and what I cut)

The whole thing runs on:

  • Static HTML per page (Veloce.html, Inventory.html, Service.html, Financing.html, About.html, Vehicle.html)
  • React 18 UMD from a CDN
  • In-browser Babel for JSX
  • GSAP for hero scroll-driven motion
  • Tailwind via Play CDN

That setup gets a Tailwind warning in the console — "not for production". I read the warning and shipped anyway. The whole site is under 2 MB compressed and serves from CDN edge. Reverting to a Vite build would have added a config file, a node_modules, and a deploy step. Nothing the visitor would feel.

What I cut: a React-Router replacement (HTML pages link to each other the normal way), an SSR layer, and an image CMS. Five HTML files do not need a CMS.

The hero

Veloce's hero is a vertical-scroll slider — but instead of fading between three cinematic video plates, GSAP drives a clip-path ellipse that opens from 5% to 150% of the viewport. The effect: each new vehicle plate "blooms" out from the center.

const slideDist = (SCROLL_PER_SLIDE_VH / 100) * vh;
const scrolled = -wrapperRef.current.getBoundingClientRect().top;
const p = (scrolled - idx * slideDist) / slideDist;
const e = easeInOut(clamp01(p));
ref.style.clipPath = `ellipse(${5 + e * 150}% ${8 + e * 150}% at 50% 50%)`;

A requestAnimationFrame debounce keeps it at 60fps even on a mid-tier Android. The whole hero is one position: sticky wrapper that's 100vh + 300vh tall — three slide stops, then the page continues.

Video pipeline

Every hero plate is a Gemini Veo generation, transcoded down to 1280-wide H.264 at CRF 26 with +faststart for instant playback. The Veo sparkle watermark in the bottom-right of the 1920×1080 source gets blurred out with ffmpeg's delogo filter at x=1700 y=880 w=120 h=110. The whole hero — three video plates plus 22 product photos — lands at about 7 MB.

I trimmed the third hero plate from 10s to 4s after watching the rough cut. The studio shot did not have enough new information to earn the extra seconds.

Reveal animation: the un-blink

Every section enters the viewport with an IntersectionObserver one-shot — element starts at opacity: 0 and translateY(30px), gains an .in class on intersect, transitions to visible. Standard pattern.

On desktop it reads as cinematic. On mobile it reads as: every section blinks like an eye as you scroll. I cut the translate from 30px to 8px and the transition from 0.85s to 0.45s. The motion is still there. The blink is not.

Three bugs I caught in QA

1. The bfcache zombie. A page-exit overlay (a full-bleed black <div> that fades in on link click for the route transition) persisted in the bfcache. Hit browser-back from Vehicle.html to the homepage and the overlay was still there — black screen on every back navigation. Fix: listen for pageshow with event.persisted === true and popstate, force the overlay opacity back to 0.

2. The 1-image gallery. Some vehicle listings have one studio photo. The thumb-row component was rendering a single thumbnail that the user could click — to nothing. Hide the row when images.length === 1.

3. The hero stack order on mobile. The hero block puts the title in a right column and the kicker/subtitle/CTAs in a left column. On desktop, they sit side by side. On mobile, the flex direction collapses to a column — and the JSX order put the left block first. Result: kicker → subtitle → CTAs → title. Fix: order-1 on the title with lg:order-2, mirror inverse on the left block. Title leads on mobile, sits right on desktop.

Live

veloce-template-01.netlify.app — open it on mobile and scroll the hero.


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.