Naar hoofdinhoud
Documentatie

Zet de configurator op je eigen site

Eén regel HTML. Geen framework, geen build, geen cookie. De widget tekent zich in een shadow root, dus jouw stylesheet en de onze kunnen elkaar niet raken.

In één minuut geïnstalleerd

Plak dit in de pagina, precies op de plek waar de widget moet verschijnen. Meer is er niet. De slug staat in het portaal onder Instellingen → Aanvraagformulieren, naast de knop die deze regel voor je genereert. Bouw je in React, Next.js of Vue, gebruik dan placeholders — zie verderop.

<script src="https://app.zinevu.com/w.js" async
        data-zv="JOUW_FORMULIER_SLUG"
        data-widget="size"
        data-url="https://offerte.jouwbedrijf.nl/q/JOUW_FORMULIER_SLUG"></script>

data-url is het adres waar de bezoeker heen gaat. Laat het staan: het is ook het vangnet als de widget de instellingen even niet kan ophalen — dan toont hij een werkende knop in plaats van een gat in je pagina.

  • async — de widget vertraagt je pagina niet en blokkeert het renderen nooit.
  • Twee globals — Zinevu en het interne __zinevuWidget — en niets in je <head>: geen stylesheet, geen extra tag.
  • Elke fout wordt opgeslikt. Een kapotte widget mag een website niet meenemen.
  • Wat de dealer in het portaal wijzigt — een vraag hernoemd, een optie uit — staat live op je site. Je hoeft de snippet nooit opnieuw te plakken.

Negen widgets

Kies met data-widget. Ze delen dezelfde kaart en dezelfde attributen; ze verschillen in wat ze vragen en wat ze teruggeven.

Maten

size

Vraagt breedte en diepte en stuurt de bezoeker naar het formulier met die maten al ingevuld.

De standaardkeuze. Werkt in een zijbalk, tussen twee alinea's of als hero-blok op een productpagina.

<script src="https://app.zinevu.com/w.js" async
        data-zv="SLUG" data-widget="size"
        data-url="https://offerte.jouwbedrijf.nl/q/SLUG"></script>

Richtprijs

price

Zelfde maatvraag, maar toont ook een indicatieprijs voor de standaarduitvoering op die maat.

Op een pagina waar bezoekers afhaken omdat ze geen idee hebben van de orde van grootte.

Werkt alleen als de dealer publieke prijzen heeft aangezet voor dit formulier. Staat die schakelaar uit, dan valt de widget netjes terug op de knop — hij toont nooit een verzonnen getal.

<script src="https://app.zinevu.com/w.js" async
        data-zv="SLUG" data-widget="price"
        data-url="https://offerte.jouwbedrijf.nl/q/SLUG"></script>

Maatcheck

match

Vraagt een maat en zegt of die uit voorraadpanelen te bouwen is of maatwerk wordt.

Glazen schuifwanden. Een bezoeker die een voorraadmaat heeft, kan direct door naar je webshop.

Alleen beschikbaar voor formulieren van het type glazen schuifwand.

<script src="https://app.zinevu.com/w.js" async
        data-zv="SLUG" data-widget="match"
        data-shop="https://jouwbedrijf.nl/p/schuifwand?b={width}&h={height}"
        data-url="https://offerte.jouwbedrijf.nl/q/SLUG"></script>

Eén vraag

question

Toont één vraag uit het formulier — bijvoorbeeld het dakmateriaal — en geeft het antwoord mee aan de funnel.

Op een pagina die over precies dat onderwerp gaat. De bezoeker begint met een keuze die hij toch al aan het maken was.

<script src="https://app.zinevu.com/w.js" async
        data-zv="SLUG" data-widget="question" data-step="roof"
        data-url="https://offerte.jouwbedrijf.nl/q/SLUG"></script>

Knop

cta

Een knop of balk die naar de funnel gaat. Vraagt niets.

Site-breed, in een footer of onder elke productpagina. Dit is de widget die op elke pagina mag staan.

Met data-url doet deze widget geen enkele netwerkaanvraag — hij tekent meteen. Met data-mode="floating" wordt het een zwevende knop rechtsonder, die in een footer op elke pagina kan staan zonder iets te verschuiven.

<script src="https://app.zinevu.com/w.js" async
        data-zv="SLUG" data-widget="cta" data-open="drawer"
        data-url="https://offerte.jouwbedrijf.nl/q/SLUG"></script>

Volledig formulier

form

De hele funnel in een kader op je eigen pagina. De bezoeker verlaat je site niet.

Een landingspagina die maar één doel heeft, of je eigen funnelpagina voor data-open="page". Geef hem ruimte — dit is geen zijbalk-widget.

Het kader groeit standaard mee met de funnel. Met data-height="fill" wordt het schermhoog, met een getal of CSS-lengte krijgt het een vaste hoogte. Met data-target="3d" is het kader standaard schermhoog, en de funnel kan zelf om schermhoog vragen zolang je geen vaste hoogte gaf. Heeft je site een vaste header, geef dan data-offset mee met de hoogte ervan in pixels.

<script src="https://app.zinevu.com/w.js" async
        data-zv="SLUG" data-widget="form"
        data-url="https://offerte.jouwbedrijf.nl/q/SLUG"></script>

Alle vragen op één pagina

Met data-target="onepage" staat in het kader hetzelfde formulier met alle vragen onder elkaar. Elke vraag is ingeklapt tot het antwoord dat erin staat — met de andere opties ernaast — dus het kader blijft kort en de bezoeker ziet in één oogopslag wat er gekozen is. Geef het kader minstens 1000px breed, dan komt de tekening naast de vragen te staan in plaats van erboven.

<script src="https://app.zinevu.com/w.js" async
        data-zv="SLUG" data-widget="form" data-target="onepage"
        data-url="https://offerte.jouwbedrijf.nl/q/SLUG/direct/veranda"></script>

Afspraak

book

Stuurt naar de showroom-agenda, waar de bezoeker zelf een moment kiest.

Op de contact- of showroompagina.

De enige widget zonder data-zv — de afsprakenpagina is geen aanvraagformulier en heeft een eigen adres (portaal → Instellingen → Showroom). data-url is hier verplicht.

<script src="https://app.zinevu.com/w.js" async
        data-widget="book"
        data-url="https://offerte.jouwbedrijf.nl/afspraak/SHOWROOM_SLUG"></script>

Afspraak inplannen

appointment

De afsprakenagenda voor gratis inmeten, een adviesgesprek aan huis, een terugbelafspraak of een showroombezoek. De bezoeker kiest een dag en tijd; voor een huisbezoek eerst de postcode, zodat alleen dagen verschijnen waarop jullie in de buurt zijn. Elke boeking komt als lead in het portaal.

Op de werkwijze-, contact- of landingspagina — of met data-mode="floating" in de footer, zodat de knop op elke pagina staat.

Zonder data-zv. De link komt uit portaal → Instellingen → Afspraken (publieke boekingslink aanzetten per afspraaktype); portaal → Marketing → Widgets bouwt de code. Na een boeking volgt een dataLayer-event zinevu_appointment_booked met alleen het afspraaktype en de status (zie Events).

<script src="https://app.zinevu.com/w.js" async
        data-widget="appointment" data-mode="popup"
        data-url="https://offerte.jouwbedrijf.nl/afspraak/AFSPRAAK_SLUG"></script>

Live chat

chat

Een chatknop in de hoek van de pagina. De bezoeker stelt zijn vraag, jullie antwoorden vanuit het portaal of de app — en zien meteen op welke pagina hij staat.

Site-breed. Deze snippet hoort één keer in je layout of je header, net als een analytics-tag; hij tekent niets op de plek van de code zelf.

Zet de chat eerst aan bij portaal → Instellingen → Live chat en vink daar „Eigen website” aan — anders verschijnt er niets. De begroeting, de openingstijden en de talen staan in diezelfde instellingen, niet in de code: één wijziging daar geldt meteen voor je hele site. Tot de bezoeker de knop indrukt, slaat de widget niets op.

<script src="https://app.zinevu.com/w.js" async
        data-zv="SLUG" data-widget="chat"></script>

Alle attributen

Alles wordt gelezen van de scripttag zelf, of van het placeholder-element. Onbekende waarden worden genegeerd in plaats van geraden — een typefout in data-theme geeft het lichte thema, een onbekende data-open gewoon navigate, geen kapotte widget.

AttribuutGeldt voorWaardeWeggelatenWat het doet
data-zinevuplaceholder-elementgeen waarde nodighet element blijft een gewoon elementMaakt van een element een widget, zodra w.js één keer op de pagina staat. Placeholders die later verschijnen worden vanzelf gemount.
data-zvVerplicht. Zonder dit tekent de widget niets en zegt waarom in de console.alle behalve book en appointment16–40 letters en cijfers · autode widget tekent niets — behalve een form-placeholder op je funnelpagina, die de slug uit zv_form in het adres neemtDe slug van het aanvraagformulier. Te vinden in het portaal bij het formulier zelf. auto (of weglaten) alleen op een form-placeholder op je eigen funnelpagina.
data-widgetallesize · price · match · question · cta · form · book · appointment · chatsizeWelke widget getekend wordt.
data-urlalle · verplicht bij book en appointmentvolledige URLde widget vraagt zelf het adres opWaar de bezoeker heen gaat. Ook het vangnet als de instellingen niet opgehaald kunnen worden, en bij cta en form scheelt het een netwerkaanvraag.
data-mountalleen losse scripttagCSS-selectorde plek van de scripttag zelfZet de widget in een bestaand element. Matcht de selector niets, dan tekent de widget niet en zegt waarom in de console.
data-localealleauto · nl · en · de · fr · trlosse scripttag: nl · placeholder: autoDe taal van de widget én van de funnel waarnaar hij doorstuurt. Met auto leest de widget in welke taal je pagina staat — een taalwisselaar, WPML, Polylang, GTranslate, Google Translate — en verandert mee zodra de bezoeker wisselt. Welke talen er echt zijn, bepaalt de dealer (portaal → instellingen → aanvraagformulieren): vraagt je pagina om een taal die niet aanstaat, dan komt de widget terug in een taal die dat wel is.
data-targetalle3d · onepage · quickform · een placeholder op je funnelpagina volgt zv_targetStuurt door naar de 3D-configurator, met onepage naar hetzelfde formulier met alle vragen op één pagina, of met quick naar de snelle offerte (maat, budget, gegevens — beantwoord met pakketten binnen het budget), in plaats van naar het vlakke stappenformulier. Draait het formulier geen snelle offerte, dan gaat quick naar het gewone formulier.
data-refalleeigen tekst, max 120 tekensgeenJouw eigen kenmerk voor dit bezoek. Het reist mee naar de aanvraag en komt terug op de zinevu:lead-gebeurtenis, zodat je pagina weet wélk bezoek een aanvraag werd — iets wat wij je niet vertellen, omdat wij over de bezoeker niets zeggen. Wij lezen het nooit, we dragen het alleen. Staat het er al op (een knop eerder in de route gaf het mee), dan blijft dat eerste kenmerk staan.
data-stepalle · vereist bij questionstapsleutelde funnel opent op stap éénOpent de funnel bij een bepaalde vraag. Bij question bepaalt het welke vraag de widget toont.
data-themealledarklichtDonkere variant, voor een donkere sectie op je site.
data-accentalle#rrggbbde accentkleur van het formulierOverschrijft de accentkleur. Alleen zes hexcijfers; de tekstkleur op de knop wordt zelf berekend, dus een lichte huisstijlkleur krijgt zwarte letters in plaats van onleesbaar wit.
data-layoutalleherocardZet naast de kaart een tweede kolom voor een kop, een uitleg en een geruststelling. Alleen zichtbaar als je ook data-title, data-intro of data-note meegeeft.
data-titleallevrije tekstde standaardtekstDe kop boven de widget.
data-labelallevrije tekstde standaardtekstHet opschrift van de knop.
data-introlayout herovrije tekstgeen tweede kolomDe zin die de bezoeker overhaalt om de widget te gebruiken.
data-notelayout herovrije tekst—De kleine regel eronder: levertijd, vrijblijvend, garantie.
data-openalle behalve form · bij book en appointment alleen drawer en modalnavigate · page · drawer · modalnavigateWaar de funnel opent: het funnel-adres in hetzelfde tabblad, je eigen funnelpagina (met data-page), een paneel van de zijkant of een gecentreerd paneel. Een paneel is bewust nooit de standaard. Zie Waar de funnel opent.
data-pagedata-open="page"pad of URL van je funnelpaginadata-open valt terug op navigate, met een melding in de consoleJe eigen pagina met een form-placeholder erop. De bezoeker gaat erheen met zijn antwoorden, zv_form en zv_target in het adres.
data-modalzoals data-open1uitDe oude schrijfwijze van data-open="modal". Werkt nog steeds; staat data-open er ook, dan gaat dat voor.
data-modeappointment · ctainline · popup · floating (cta: floating)appointment: inline · cta: een kaart met knopHoe de widget verschijnt: in de pagina, als knop die een paneel opent, of als zwevende knop rechtsonder die in een footer op elke pagina kan staan. Een zwevende afsprakenknop opent een paneel; een zwevende cta volgt data-open.
data-positionchatright · leftrechtsonderIn welke hoek de chatknop staat. Met data-side en data-bottom (pixels, standaard 20) schuif je hem weg van iets anders dat al in die hoek staat — een cookiebalk of een terug-naar-boven-knop.
data-heightform · book en appointment in de paginaauto · fill · getal (px) · CSS-lengteauto — schermhoog bij data-target="3d"auto groeit mee met de funnel. fill maakt het kader schermhoog, min data-offset. Een vaste hoogte blijft staan, ook als de funnel zelf om schermhoog vraagt.
data-offsetform · book en appointment in de paginagetal (px)0De hoogte van de vaste header van je site. Een schermhoog kader wordt zoveel korter, en als de funnel naar zijn kader scrolt, stopt dat zoveel onder de bovenrand.
data-name · data-email · data-postcodeappointmentvrije tekstleegVult de agenda alvast in met wat je eigen pagina al weet, bijvoorbeeld van een ingelogde klant. Er wordt niets van de bezoeker uitgelezen.
data-aralleen met data-target="3d"1uitOpent de 3D-configurator meteen in AR, zodat de bezoeker het product op ware grootte in zijn eigen tuin ziet. Bij het vlakke formulier genegeerd.
data-shopmatchURL met {width} {height} {panels} {pane_width} {pane_height}geen webshoplinkWaar een bezoeker met een voorraadmaat naartoe mag. Verschijnt alleen als de maat écht uit voorraad te bouwen is.
data-shop-labelmatchvrije tekstde standaardtekstHet opschrift van die link.
data-cartmatchURL op je eigen domeingeen winkelwagenknopJe eigen eindpunt dat de maat ontvangt en antwoordt waar de bezoeker heen moet. Alles wat bepaalt of het artikel mag bestaan en wat het kost, gebeurt daar — server-side, op jouw domein.
data-productmatchproduct-idgeen winkelwagenknopWelk artikel je eindpunt in de wagen legt. Zonder data-cart én data-product verschijnt de knop niet.
data-originalleorigin, zoals https://app.zinevu.comhet adres waar w.js vandaan komt (anders https://app.zinevu.com)Geavanceerd: haalt instellingen, richtprijs en maatcheck op bij een andere API-origin. Normaal laat je dit weg.

Waar de widget terechtkomt

Standaard verschijnt de widget precies waar de scripttag staat. Dat is wat de installatie één plakactie maakt — je zet de regel waar je het blok wilt zien.

  • Plak je de regel in een "header scripts"-veld — de manier waarop de helft van de WordPress-sites externe tags laadt — dan komt hij in <head> terecht. De widget weigert daar te tekenen en zegt in de console wat je moet doen. Hij verplaatst zichzelf bewust niet: een headersnippet staat op élke pagina, dus een widget die zichzelf verhuist zou site-breed boven je navigatie verschijnen.
  • Wil je hem toch vanuit de header laden, geef dan data-mount mee met een selector van het element waar hij in moet — of laad w.js in de header en zet een <div data-zinevu>-placeholder in de pagina.
  • De widget leeft in een shadow root. Jouw CSS bereikt hem niet en die van ons bereikt jouw pagina niet — een reset of een agressieve * { } in je thema kan de widget niet slopen. Wat je wél mag aanpassen, gaat via de --zinevu-*-variabelen en ::part() (zie Eigen huisstijl).
<!-- vanuit een headerveld, met een doel op de pagina -->
<div id="offerte-blok"></div>

<script src="https://app.zinevu.com/w.js" async
        data-zv="SLUG" data-widget="size"
        data-mount="#offerte-blok"
        data-url="https://offerte.jouwbedrijf.nl/q/SLUG"></script>

React, Next.js, Vue en andere single-page sites

Op een site die zijn eigen markup rendert en navigeert zonder te herladen, kan de scripttag niet zelf de widget zijn. React ontdubbelt identieke async-scripts, dus een tweede tag op dezelfde pagina draait nooit, en een navigatie aan de clientkant draait de eerste niet opnieuw. Laad de loader daar één keer, en zet een placeholder waar een widget moet komen.

  • Een kale scripttag — zonder data-zv of data-widget — tekent niets op zijn eigen plek. Hij is alleen de loader: elk element met data-zinevu wordt een widget.
  • Placeholders die later verschijnen — na een navigatie aan de clientkant, in een tab die pas bij een klik rendert, in een blok dat je CMS injecteert — worden automatisch gemount. Verdwijnt een placeholder terwijl zijn paneel open staat, dan wordt dat paneel gesloten, zodat de volgende pagina gewoon scrollt.
  • Alle attributen uit de tabel werken ook op een placeholder, behalve data-mount: de placeholder ís de plek.
  • Zonder data-locale volgt een placeholder de taal van je pagina, alsof er data-locale="auto" stond. Een losse scripttag blijft standaard nl, tenzij je auto invult.
  • Beide manieren mogen door elkaar op één pagina staan. Ze delen één API, één cache voor de instellingen en één koppeling met de frames.
<!-- één keer, bijvoorbeeld in je root-layout -->
<script src="https://app.zinevu.com/w.js" async></script>

<!-- overal waar een widget moet verschijnen -->
<div data-zinevu data-zv="SLUG" data-widget="price"></div>

<div data-zinevu data-zv="SLUG" data-widget="question"
     data-step="roof" data-open="drawer"></div>

window.Zinevu

Dezelfde placeholders, met de hand. Handig als je zelf bepaalt wanneer een component in beeld komt, of de funnel wilt openen vanuit je eigen knop. De opties zijn de data-attributen zonder het voorvoegsel: { zv, widget, target: "3d", open: "drawer" }. Een naam in camelCase wordt met streepjes geschreven (shopLabel → data-shop-label) en true wordt "1".

  • Zinevu.mount(el, opties) — tekent een widget in el. De opties worden als data-attributen op het element gezet, zodat een widget altijd hetzelfde leest, hoe hij er ook kwam. Staat er al een widget in, dan wordt die vervangen.
  • Zinevu.unmount(el) — haalt de widget weer weg, en sluit een paneel dat hij open had.
  • Zinevu.scan(root) — mount elk [data-zinevu] onder root dat nog geen widget is; zonder root het hele document. Zelden nodig: de loader let zelf op nieuwe placeholders.
  • Zinevu.open({ zv, target, open, … }) — opent de funnel meteen in een paneel over de pagina, zonder dat er iets getekend wordt. open is drawer (standaard) of modal. Met url erbij hoeft er niets opgehaald te worden.
  • Zinevu.consent({ statistics, marketing }) — geeft door wat jouw cookiebanner besliste. Zie Privacy en cookies.
  • Zinevu.tags({ gtm: "GTM-XXXX", ga4: "G-XXXX", pixel: "123456" }) — zegt welke meettags jouw pagina zelf al draait. Staat in Zinevu dezelfde GA4-property of dezelfde Meta-pixel ingesteld, dan telt het frame de bezoeker niet nog een keer: de paginaweergave laat het aan jouw pagina, de aanvraag meldt het wel. Meestal niet nodig — wat al op de pagina staat vindt de loader zelf; zet het als jouw tags pas ná je cookiebanner laden.
  • Zinevu.setLocale("en") — zegt in welke taal je pagina nu staat. Elke widget met data-locale="auto" wordt meteen opnieuw getekend, ook een formulier in een frame. Nodig als je taalwisselaar de pagina niet herlaadt (Weglot, WPML via AJAX, je eigen React-router); wisselaars die wél herladen hoeven niets te doen. Zonder argument kijkt de widget weer zelf. Zinevu.locale() zegt wat hij nu leest.
  • De loader is async, dus window.Zinevu bestaat pas als hij geladen is. Wil je daar niet op wachten, gebruik dan een placeholder: die wordt gemount zodra de loader er is. Heet er op je pagina al iets Zinevu, dan laat de loader dat staan.
// React: mounten en weer opruimen
useEffect(() => {
  const el = ref.current;
  window.Zinevu?.mount(el, { zv: "SLUG", widget: "size", target: "3d" });
  return () => window.Zinevu?.unmount(el);
}, []);

// je eigen knop opent de 3D-configurator in een zijpaneel
button.addEventListener("click", () => {
  Zinevu.open({ zv: "SLUG", target: "3d", open: "drawer" });
});

Waar de funnel opent

Wat er gebeurt als de bezoeker een widget heeft ingevuld, kies je met data-open. Het geldt voor elke widget die doorstuurt — size, price, match, question en cta. Bij book en appointment maken drawer en modal er een knop van die een paneel opent.

  • navigate (standaard) — het adres van de funnel, in hetzelfde tabblad. Wat elke link op je site al doet.
  • page — je éígen funnelpagina (data-page="/offerte") met daarop een form-placeholder. De bezoeker blijft op jouw domein, de adresbalk is van jou, terug en herladen werken. Zonder data-page valt de widget terug op navigate en zegt waarom in de console.
  • drawer — een paneel dat van de zijkant over je pagina schuift; op een telefoon schermvullend.
  • modal — hetzelfde paneel, gecentreerd. data-modal="1" is de oude schrijfwijze en werkt nog steeds. Een paneel sluit met de knop, Escape of een klik op de achtergrond, en geeft scrollen en focus terug aan je pagina.
  • De overdracht naar je funnelpagina draagt de antwoorden als zv_*-parameters, plus zv_form (welk formulier) en zv_target (3d als de bezoeker de 3D-configurator koos). Klik-id's en UTM's gaan mee zoals bij navigate.
  • Een form-placeholder zonder data-zv, of met data-zv="auto", neemt de slug uit zv_form — zo bedient één funnelpagina al je formulieren. Dat lezen doet alleen een placeholder: een losse scripttag op je funnelpagina negeert zv_form, zv_target en de antwoorden.
<!-- productpagina: de maatbox stuurt naar je eigen funnelpagina -->
<div data-zinevu data-zv="SLUG" data-widget="size"
     data-open="page" data-page="/offerte"></div>

<!-- /offerte: één placeholder voor elk formulier,
     72px vaste header boven de pagina -->
<div data-zinevu data-widget="form" data-zv="auto"
     data-offset="72"></div>

Opent iemand je funnelpagina rechtstreeks, zonder zv_form in het adres, dan weet een auto-placeholder niet welk formulier bedoeld is en tekent hij niets. Staat er maar één formulier op je site, geef dan gewoon de slug mee.

Eigen huisstijl

De shadow root houdt je stylesheet buiten, op twee gedeclareerde deuren na. CSS custom properties gaan wél door een shadow-grens heen: zet ze op de placeholder, op een sectie of op :root. Voor wat die niet kunnen zeggen zijn er ::part()-namen. Zet je niets, dan ziet de widget eruit zoals altijd.

  • --zinevu-accent — kleur van knop en focus. Jouw waarde gaat vóór de kleur die bij het formulier is opgeslagen; data-accent op de snippet gaat weer vóór allebei.
  • --zinevu-on-accent — tekstkleur op de accentkleur (anders berekend).
  • --zinevu-fg · --zinevu-muted — tekst en secundaire tekst.
  • --zinevu-surface · --zinevu-field · --zinevu-panel — de kaart, de binnenkant van een invoerveld of tegel, en de hero-sectie achter de kaart.
  • --zinevu-line · --zinevu-line-strong — haarlijnen.
  • --zinevu-shadow — de schaduw van de kaart (none voor vlak).
  • --zinevu-radius · --zinevu-control-radius — hoeken van kaart en paneel; hoeken van invoervelden, knoppen en tegels.
  • --zinevu-font · --zinevu-heading-font · --zinevu-heading-weight — het lettertype (anders dat van je pagina), het lettertype van koppen en het bedrag, en het gewicht van koppen.
  • --zinevu-card-max-width — de maximale breedte van de kaart.
  • ::part() — root, card, title, field, field-label, input, button, button-secondary, button-floating, button-label, button-arrow, result, amount, basis, note, tile, tile-image, tile-label, hero, hero-title, hero-intro, hero-note, frame, overlay, panel, panel-bar, panel-title, close.
.offerte-sectie {
  --zinevu-accent: #0f766e;
  --zinevu-radius: 8px;
  --zinevu-control-radius: 6px;
  --zinevu-shadow: none;
  --zinevu-heading-font: Georgia, serif;
}

/* ::part() hangt aan het host-element */
[data-zinevu]::part(button),
[data-zinevu-widget]::part(button) {
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

Het host-element is [data-zinevu] bij een placeholder, [data-zinevu-widget] bij een losse scripttag en [data-zinevu-open] bij Zinevu.open(). Zet de variabelen in je stylesheet, niet pas later met JavaScript: of jouw accentkleur voorgaat, wordt bepaald op het moment dat de widget tekent.

Events voor je eigen analytics

De widget meet zelf niets en verstuurt niets. Wat er gebeurt, meldt hij aan jouw pagina — twee keer, voor de twee soorten sites: een dataLayer-push voor een Tag Manager-container, en een DOM-event op document met een detail voor een site die zijn eigen analytics bedraadt. Jouw tags, en de consentregels waaronder die draaien, bepalen wat er daarna gebeurt.

  • zinevu_open · zinevu:open — een widget stuurde de bezoeker naar de funnel of opende een paneel. dataLayer: zv_widget, zv_form_type, zv_target (form of 3d), zv_open. detail: widget, form_type, target, open.
  • zinevu_price · zinevu:price — de richtprijs-widget gaf antwoord. dataLayer: zv_form_type, zv_priceable. detail: form_type, priceable. Alleen óf er een prijs getoond werd, nooit het bedrag: dat is de prijslijst van de dealer, en die hoort niet in je analytics.
  • zinevu_lead · zinevu:lead — er is een aanvraag verstuurd vanuit een ingebedde funnel. dataLayer: zv_form_type, zv_surface. detail: form_type, surface.
  • zinevu_appointment_booked · zinevu:appointment_booked — er is geboekt op een ingebedde afsprakenpagina. dataLayer: zv_appointment_kind, zv_appointment_status. detail: kind, status.
  • Draait jouw Tag Manager-container ook in Zinevu, dan draait hij twee keer op deze pagina: op je site en in het frame. Het frame zet daarom zv_embedded (true) en zv_container_on_host in zijn eigen dataLayer, vóór de container laadt. Maak er een Data Layer Variable van en hang die als blokkerende trigger aan de tags die een paginaweergave tellen — je Google-tag bijvoorbeeld. Tags die een aanvraag tellen laat je met rust: die gebeurt alleen in het frame.
  • lead en appointment_booked komen alleen uit een funnel die op jouw pagina staat: de form-widget, een paneel, of je funnelpagina bij data-open="page". Met navigate verlaat de bezoeker je site voordat hij iets verstuurt.
// Tag Manager: trigger op Custom Event "zinevu_lead"
// dataLayer.push({ event: "zinevu_lead", zv_form_type: "…", zv_surface: "…" })

// Of zelf, met gtag:
document.addEventListener("zinevu:lead", (e) => {
  gtag("event", "generate_lead", { form_type: e.detail.form_type });
});

Welk product en welke plek — nooit wie, nooit een bedrag.

Per platform

Het is één scripttag, dus overal waar je HTML kwijt kunt werkt het. Dit zijn de plekken waar dat per systeem zit.

WordPress

  1. 1Met de Zinevu-plug-in: download hem op https://app.zinevu.com/zinevu-widgets.zip — hij staat niet in de WordPress-plug-indirectory — upload hem via Plug-ins → Nieuwe plug-in → Plug-in uploaden, plak je scriptregel één keer bij Instellingen → Zinevu Widgets, en plaats daarna het blok "Zinevu widget" in de editor. Je kiest het type en de opties in het blok zelf.
  2. 2Zonder plug-in: voeg in de blokeditor een blok "Aangepaste HTML" toe en plak de scriptregel erin.
  3. 3In een klassiek thema: plak de regel in het paginasjabloon op de plek waar de widget hoort.

Let op bij optimalisatie-plug-ins. Zet de widget uit in "defer JS" of "combine scripts": de tag heeft zijn eigen plek in de pagina nodig en verliest die als hij verplaatst of samengevoegd wordt.

Shopify

  1. 1Ga naar Online Store → Themes → Edit code.
  2. 2Maak een nieuwe section, plak de scriptregel erin en sla op.
  3. 3Voeg de section toe aan het sjabloon waar de widget moet staan.

Wil je de maatcheck naar je eigen wagen laten schrijven, laat data-cart dan naar een eindpunt op je eigen domein wijzen. De prijs en de voorraad horen daar bepaald te worden, niet in de browser.

Wix

  1. 1Voeg een element "Embed a widget" (Custom Embed → Embed a Widget) toe op de plek waar de widget hoort.
  2. 2Plak de scriptregel in het HTML-veld.
  3. 3Geef het element genoeg hoogte — Wix knipt af op de hoogte die je instelt.

Webflow

  1. 1Sleep een Embed-element naar de gewenste plek.
  2. 2Plak de scriptregel erin.
  3. 3Publiceren; in de designer blijft het element leeg, dat hoort zo.

Losse HTML of een ander CMS

  1. 1Plak de scriptregel in de body, op de plek waar de widget moet verschijnen.
  2. 2Staat je CMS alleen scripts in de header toe, gebruik dan data-mount met een selector.

React, Next.js of Vue

  1. 1Laad w.js één keer als kale scripttag, zonder data-zv, bijvoorbeeld in je root-layout of index.html.
  2. 2Zet <div data-zinevu data-zv="SLUG" data-widget="…"> in de component waar de widget hoort.
  3. 3Navigeren aan de clientkant vraagt niets extra: nieuwe placeholders worden vanzelf gemount.

Wil je zelf bepalen wanneer een widget komt en gaat, gebruik dan Zinevu.mount en Zinevu.unmount — zie window.Zinevu.

Privacy en cookies

De widget weet niets over de bezoeker. Er wordt geen cookie gezet, geen localStorage aangeraakt, geen identifier aangemaakt en geen beacon verstuurd. Zijn instellingen komen uit een publieke leesactie die voor iedereen exact hetzelfde antwoord geeft; de richtprijs en de maatcheck sturen alleen de ingevulde maat mee.

  • Daarom mag de widget draaien vóórdat je cookiebanner beantwoord is: er valt niets in te stemmen.
  • Je hoeft hem niet achter een consentcategorie te zetten. Doe je dat toch, dan werkt hij gewoon — maar je verliest de bezoekers die de banner wegklikken.
  • Eén ding leest hij wel: de drie cookies van Complianz (cmplz_banner-status, cmplz_statistics, cmplz_marketing), en verder niets. Die gaan ongewijzigd door naar een ingebedde funnel, zodat die niet een tweede keer om toestemming vraagt. Beantwoordt de bezoeker je banner terwijl de funnel open staat, dan telt dat binnen twee seconden mee.
  • Gebruik je een andere cookiebanner, geef dan zelf door wat de bezoeker koos: Zinevu.consent({ statistics: true, marketing: false }). Dat gaat vóór de cookies en bereikt ook frames die al open staan. Roep het opnieuw aan als de bezoeker zijn keuze wijzigt.
  • Pas op het moment dat een bezoeker de funnel opent, gelden de cookie- en trackinginstellingen van de dealer. Dat is een ander domein — ook als de funnel in een kader op jouw pagina staat.

Dit is een bewuste eigenschap, geen toevallige. Zodra de widget iets over de bezoeker zou opslaan of versturen, heeft elke site die hem draait een consentpoort ervoor nodig.

Advertentie-attributie

Een bezoeker die op jouw site landt via een advertentie en dan op de widget klikt, zou in de funnel aankomen zonder klik-id en met jouw eigen site als herkomst. De lead uit die betaalde klik leest dan als direct verkeer, en de verkoop die er weken later uit komt is nooit meer aan de campagne toe te rekenen.

  • Klik-id's uit de URL (Google, Meta) worden onder hun eigen naam meegegeven aan de funnel.
  • De échte landingspagina en de oorspronkelijke herkomst reizen apart mee, zodat ze niet overschreven worden door jouw domein.
  • Ook welke widget de bezoeker gebruikte gaat mee. Bij de aanvraag in het portaal lees je dan bijvoorbeeld: Prijswidget op /veranda.
  • Je hoeft hier niets voor in te stellen. Het gebeurt zolang de bezoeker op dezelfde pagina klikt waar hij binnenkwam.

Als het niet werkt

Ik zie niets op de pagina.
Open de console. De widget zegt altijd waarom hij niet tekent. De twee meest voorkomende oorzaken: de snippet staat in <head> (plak hem in de pagina, gebruik data-mount of een placeholder), of data-zv is geen geldige formulierslug. Een placeholder tekent pas als w.js ergens op de pagina geladen is.
Mijn site wisselt van taal, maar het formulier blijft Nederlands.
Het formulier staat in een frame op ons adres, en geen enkele vertaling die jouw site op zichzelf uitvoert komt daar binnen — ook Google Translate niet. Zet data-locale="auto" op de snippet (of plak een verse snippet uit het portaal): de widget leest dan zelf in welke taal je pagina staat en laadt de funnel in die taal. Wisselt jouw taalwisselaar zonder de pagina te herladen, roep dan Zinevu.setLocale("en") aan bij het wisselen. Blijft de funnel toch in één taal, dan staat die taal niet aan bij de dealer: portaal → instellingen → aanvraagformulieren → talen.
In React of Next.js verschijnt maar één widget, of hij verdwijnt na een navigatie.
Dan staat er een scripttag per widget. React ontdubbelt identieke async-scripts, en een navigatie aan de clientkant draait een script niet opnieuw. Laad w.js één keer als kale tag en gebruik <div data-zinevu>-placeholders.
Met data-open="page" gaat de bezoeker toch naar het funnel-adres, of blijft mijn funnelpagina leeg.
Het eerste betekent dat data-page ontbreekt; de console zegt het. Het tweede: controleer dat op je funnelpagina een placeholder staat (geen losse scripttag) met data-widget="form", en dat het adres zv_form bevat — of geef de placeholder een vaste data-zv.
Ik zie geen zinevu_lead in mijn dataLayer.
Dat event komt alleen uit een funnel die op jouw pagina staat: de form-widget, een paneel (drawer of modal) of je funnelpagina bij data-open="page". Met navigate verlaat de bezoeker je site voordat hij de aanvraag verstuurt.
De console zegt dat data-mount niets matcht.
Het element bestaat nog niet op het moment dat het script draait, of de selector klopt niet. Zet het doelelement boven de scripttag in de HTML.
De richtprijs-widget toont alleen een knop.
Dat is de nette terugval. De dealer heeft publieke prijzen niet aangezet voor dit formulier, of de opgegeven maat levert geen prijs op. De widget toont liever geen getal dan een verkeerd getal.
De widget ziet er anders uit dan op de rest van mijn site.
Dat is opzet — hij staat in een shadow root, buiten bereik van je stylesheet. Stem hem af met data-accent, data-theme en data-layout, of met de --zinevu-*-variabelen en ::part() uit Eigen huisstijl.
Mijn cachingplug-in heeft de widget stukgemaakt.
Sluit w.js uit van "combine", "defer" en "lazy load JS". De tag moet op zijn eigen plek in de pagina blijven staan.
Ik gebruik een strikte Content-Security-Policy.
Sta het script-domein toe waarvandaan je de loader laadt. De widget haalt zelf geen extra bestanden op: geen stylesheet, geen font, geen framework.
De webshopknop verschijnt niet bij de maatcheck.
Die verschijnt alleen als de opgegeven maat werkelijk uit voorraadpanelen te bouwen is. Is het maatwerk, dan is er geen productpagina om heen te sturen en biedt de widget de offerteroute aan. Voor de winkelwagenknop heb je bovendien data-cart én data-product allebei nodig.
Documentatie

De snippet staat klaar in het portaal

Onder Instellingen → Aanvraagformulieren zit een generator met live voorbeeld: kies het type, de kleur en de teksten, en kopieer de regel. Dit is dezelfde regel als hierboven, met de slug er al in.