React + Vite site with a live Three.js "showroom" hero: a stylised concept car on a reflective floor under red spotlights, camera drifting gently with the mouse.
npm install
npm run devThen open the local URL Vite prints (usually http://localhost:5173).
To build for production / hosting:
npm run build
npm run preview # sanity-check the production build locallyThis project wasn't built or run inside this sandbox — the sandbox has no network
access, so npm install couldn't be executed here. Run it locally (or in Claude Code)
to install dependencies and preview it before it goes live.
This repo is set up to auto-deploy on every push to main:
-
One-time setup on GitHub: go to the repo → Settings → Pages → under "Build and deployment", set Source to GitHub Actions.
-
Push to
main. The workflow at.github/workflows/deploy.ymlwill install dependencies, runnpm run build, and publishdist/to GitHub Pages automatically. Check the Actions tab to watch it run.Note: there's no
package-lock.jsoncommitted yet (this sandbox has no network access, so it couldn't be generated here). The workflow usesnpm installinstead ofnpm cito work without one. Once you runnpm installlocally, a lockfile will appear — commit it, and optionally switch the workflow back tonpm ci+cache: npmin the setup-node step for faster, more reproducible builds. -
Once it finishes, the site is live at:
https://codevenientlab.github.io/Flawless-Motoring/
If you ever rename the repo, update the base path in vite.config.js to match
(it currently assumes /Flawless-Motoring/) — otherwise images and assets will 404
under the new path.
If you'd rather use a custom domain (e.g. flawlessmotoring.co.za) instead of the
github.io subpath: add a CNAME file in public/ with just the domain name in it,
set base: '/' in vite.config.js, and point your domain's DNS at GitHub Pages per
their custom domain docs.
public/images/logo-mark.png (icon only) and logo-full.png (icon + wordmark) are
processed from the supplied logo — background removed via a luminance-to-alpha
conversion, so both are true transparent PNGs that drop cleanly onto any background,
not a black rectangle. logo-mark.png is used in the navbar, the footer, and as the
favicon (public/favicon.png, generated from the same mark). logo-full.png isn't
wired into the site anywhere yet — it's there if you want it for something like a
loading screen, email signature, or social profile image.
The hero now uses the supplied public/images/hero-headlights.webp photo (slow zoom-out
on load, plus a one-time white flash timed to the headlights, mimicking them switching
on) instead of the 3D scene. The original Three.js showroom is still in the repo at
src/components/Showroom.jsx — nothing currently imports it, so it's not part of the
build, but it's there if you want to bring it back later (e.g. behind the hero image, or
on another page). If you do restore it, the three / @react-three/* packages are
still in package.json; otherwise you can remove them to trim the install.
The navbar is now just the centered "FLAWLESS MOTORING" wordmark — no nav links, no CTA button. Visitors reach Inventory/About/Contact by scrolling.
- Inventory (
src/data/inventory.js) — 6 vehicles using the real photos supplied (inpublic/images/): BMW M5 Competition, M4 Competition, X5 M Competition, M4 GTS, 750i Individual, and a VW Golf GTI. Pricing, mileage, and year are still placeholders — confirm and update before launch. - Contact / sourcing form (
Contact.jsx) — tailored for a buyer's-agent workflow: name, phone, email, what they're looking for, budget range, condition preference, location, timeline, and trade-in status. It's UI-only; wirehandleSubmitup to a real endpoint or email service (e.g. Formspree, Resend, a small backend) to receive submissions. - Contact details — phone, email, and coverage area are placeholders.
- About stats ("350+ vehicles delivered", etc.) in
About.jsxare placeholder numbers — swap for real ones. - Instagram link points to the real @flawless_motoring handle.
Inspired by high-end dealer sites (e.g. pharoahgroup.com) that lean on smooth scroll, scroll-triggered reveals, and a "culture, not just cars" section:
- Smooth inertial scroll —
Lenis, wired up inSmoothScroll.jsx, also intercepts in-page#anchorlinks so nav clicks glide instead of jumping. - Scroll reveals —
Reveal.jsxfades/slides content in the first time it enters the viewport (IntersectionObserver, no extra animation library). Used on the inventory cards (staggered), About, and Contact. - Hero entrance — headline lines rise in with a staggered delay on load.
- Magnetic buttons —
MagneticButton.jsxmakes the hero/nav CTAs pull toward the cursor on hover. - Animated counters —
AnimatedCounter.jsxcounts the About stats up when they scroll into view. - Culture Wall —
CultureWall.jsx, a dual-direction outlined-text marquee section positioning the brand as a lifestyle, not just a listings page.
src/
components/
Showroom.jsx 3D scene (the "wow" hero background)
Hero.jsx Hero copy + CTA over the 3D scene
Navbar.jsx
SpecStrip.jsx Scrolling ticker
Inventory.jsx / InventoryCard.jsx
About.jsx
Contact.jsx
Footer.jsx
data/inventory.js
App.jsx
index.css Design tokens (colors, type)
Deep red & black, motorsport-inspired. Display type is Rajdhani (technical/angular),
body is Inter, and specs/labels use IBM Plex Mono for a spec-sheet feel. Colors and
type are defined as CSS variables at the top of src/index.css — change them there to
retheme the whole site.
The 3D scene is lazy-loaded and only mounts in the hero. If you add real car photos later, compress them (WebP, ~200KB) so the site stays fast alongside the 3D canvas.