
Overzicht
UI-richting
- Cinematische eerste indruk: een full-screen videohero die automatisch afspeelt, met een GSAP-parallax gekoppeld aan de scroll, en copy die het gevoel verkoopt ("Gedreven door passie") voor ze een specsblad verkoopt.
- Editoriale rust: een ingetogen zwart-witpalet, grote letters en veel ademruimte zodat de wagens — en niet de chrome van de interface — de pagina dragen.
- Motion met bedoeling: Lenis smooth scrolling, Motion-entrance-animaties en een pinned-scroll-sectie "Onze aanpak" die de aanpak van de dealer onthult terwijl je erdoor scrolt. Alles respecteert
prefers-reduced-motion. - Flow door de site: een videohero, een filosofiestatement, de aanpak-sectie, een strook met uitgelichte wagens en een donkere call-to-action — één scroll die een koper van sfeer naar voorraad naar contact brengt.

Kernfuncties
- Filterbare voorraad met sortering op nieuwste, prijs en bouwjaar, en filters op merk en brandstof — server-side aangestuurd zodat de listings snel en SEO-vriendelijk blijven.
- Rijke detailpagina's per wagen: volledige specs (kilometerstand, vermogen, aandrijving, inschrijving, VIN, uitrusting) en een fotogalerij met klikbare lightbox.
- Levensloop van een listing: elke wagen heeft een status — draft, beschikbaar, gereserveerd, verkocht — zodat het publiek alleen ziet wat publiek mag zijn.
- Admin in eigen beheer: een dealer-vriendelijk dashboard "Aanbod beheren" om listings aan te maken, te bewerken en te verwijderen zonder ontwikkelaar in de keten.
- Fotogalerijen die je sleept om te herschikken, met directe upload naar Supabase Storage, en één verplichte hoofdfoto per wagen.
- Eén gedeelde adminlogin — de juiste hoeveelheid auth voor een kleine dealer, zonder overhead voor gebruikersbeheer.

Architectuur
- Publiek lezen, geauthenticeerd schrijven, afgedwongen in de database met Postgres Row Level Security: iedereen kan gepubliceerde listings lezen, alleen de geauthenticeerde dealer kan ze wijzigen. Beveiliging zit in de datalaag, niet alleen in de UI.
- Data ophalen vanuit de server via React Server Components en server actions — de site rendert op de server, de admin muteert via getypeerde actions.
- Getypeerde datalaag: één
cars-module omvat elke query (lijst, uitgelicht, per slug, admin-views) en zet Supabase Storage-paden op één plek om naar publieke image-URL's. - Schema dat het domein modelleert: beperkte enums voor brandstof, transmissie, carrosserie en aandrijving; een
car_images-tabel met een uniciteitsbeperking die garandeert dat er precies één hoofdfoto per wagen is; automatisch bijwerkende timestamps via een trigger.
Gebruikte technologieën
- Framework: Next.js 16 (App Router, React Server Components, server actions), React 19, TypeScript.
- Styling & UI: Tailwind CSS v4, shadcn/ui op Base UI-primitives,
class-variance-authority,lucide-react. - Motion: GSAP met ScrollTrigger voor parallax en pinned scroll, Lenis voor smooth scrolling, Motion voor entrance-animaties.
- Backend: Supabase — Postgres met Row Level Security, Auth (SSR-sessiebeheer via
@supabase/ssr) en Storage voor autofoto's met public-read- en authenticated-write-policies.
Uitdagingen en lessen
- Twee producten in één: een site die op emotie is geoptimaliseerd en een admin die op snelheid van datainvoer is geoptimaliseerd, zijn tegengestelde designproblemen. Ze in één codebase houden met één gedeelde design language — zonder dat de admin als een afterthought voelt — was de centrale spanning.
- Beveiliging waar ze hoort: toegangscontrole naar beneden duwen in RLS-policies in plaats van ze alleen in de app te bewaken, betekent dat een verkeerd geconfigureerde pagina geen draft-listings kan lekken. De regel "precies één hoofdfoto per wagen" leeft als een databasebeperking, niet als een hoopvol stukje clientcode.
- Motion die niet in de weg zit: de cinematische hero en de pinned-scroll-secties moesten premium aanvoelen op een snelle machine en elders toch rustig blijven (en veilig met reduced motion).
