Construisez votre bannière cookies sur le catalogue de tarteaucitron

zeste charge les 247 services que gère tarteaucitron.js, comme Google Analytics, YouTube, Matomo ou Hotjar, seulement après l’accord du visiteur. Vous écrivez la bannière avec vos propres composants, en Svelte, React ou Vue. Sur un site sans framework, une balise <script> ajoute une bannière toute faite.

Les textes de la bannière existent en 36 langues. Les tests vérifient qu’aucune requête ne part vers un tiers avant le consentement.

npx @zeste/cli initLire la docDisponible sur npm (v0.1.0)
Un visiteur refuse tout, autorise YouTube depuis le placeholder de la vidéo, puis le retire dans les préférences.

Pourquoi ne pas garder l’interface de tarteaucitron

tarteaucitron.js fait le travail difficile. Il sait charger des centaines de services après consentement, et ses mainteneurs tiennent cette liste à jour. Son interface, elle, s’adapte mal. Il génère son propre HTML, et pour que la bannière ressemble au site, il faut surcharger son CSS, souvent avec !important.

Le site après le passage à zeste, avec sa propre bannière et un placeholder vidéo à ses couleursLe site avec tarteaucitron, dont la bannière a des boutons jaunes et une typographie étrangère au sitetarteaucitronzeste

À gauche, un site d’exemple avec la bannière et les placeholders de tarteaucitron.

À droite, le même site passé à zeste. Il utilise sa propre bannière, ses boutons et sa modale, et charge les mêmes services.

Comment zeste est construit

Le moteur ne dessine rien

zeste enregistre le choix du visiteur dans un cookie, charge chaque script une fois accepté et met à jour Google Consent Mode. Vos composants lisent son état et appellent ses actions.

Un module par service

Les 247 services viennent de tarteaucitron.js. Vous n’importez que ceux que vous utilisez, et le navigateur télécharge le code d’un service quand le visiteur l’accepte.

Les composants vous appartiennent

npx @zeste/cli add écrit une bannière et une fenêtre de préférences dans votre projet, avec votre framework et votre lib d’UI. Vous les modifiez comme n’importe quel fichier.

Bits UIRadixReka UIArk UIshadcn-svelteshadcn/uishadcn-vuevotre design system

Installer zeste en quatre étapes

Votre framework

Déclarer ses services

npx @zeste/cli init lit le projet et déclare les services qu’il utilise déjà. Il reconnaît une configuration tarteaucitron et les scripts Google Analytics, Matomo et Hotjar ou les vidéos YouTube collés dans les pages. Son rapport donne le fichier et la ligne de chaque script à retirer.

lib/consent.ts
import { gtag, youtube, matomocloud } from '@zeste/services';

export const services = () => [
  gtag({ gtagUa: 'G-XXXXXXX' }),
  youtube(),
  matomocloud({ matomoId: 1, matomoHost: 'https://example.matomo.cloud/' }),
];

Lire le choix côté serveur

Le serveur lit le cookie de consentement, donc le premier rendu montre déjà le bon état. Un visiteur qui a déjà choisi ne voit pas la bannière apparaître puis disparaître.

src/routes/+layout.server.ts
import { parseConsentCookie } from '@zeste/svelte';

export const load = ({ cookies }) => ({
  consent: parseConsentCookie(cookies.get('tarteaucitron')),
});

Générer l’interface

La CLI détecte votre framework et votre lib d’UI. Elle propose deux styles, zeste par défaut et pelure.

npx @zeste/cli add                  # bannière, préférences et placeholder
npx @zeste/cli add --style pelure   # le style en aplat, contours épais

Ajouter zeste à votre layout

src/routes/+layout.svelte
<script lang="ts">
  import { createZeste, ZesteProvider } from '@zeste/svelte';
  import { messages } from '@zeste/services/i18n/fr';
  import { CookieBanner, CookiePreferences } from '$lib/components/zeste';
  import { services } from '$lib/consent';

  let { data, children } = $props();
  const zeste = createZeste({ services: services(), initial: data.consent, messages });
</script>

<ZesteProvider {zeste}>
  {@render children()}
  <CookieBanner />
  <CookiePreferences />
  <!-- un lien permanent pour changer d’avis -->
  <button {@attach zeste.actions.openPreferences}>Gestion des cookies</button>
</ZesteProvider>

La doc détaille chaque framework pas à pas.

Essayer la démo

La démo ci-dessous fait tourner zeste avec les composants que zeste add génère pour Bits UI, Ark UI et shadcn-svelte. Les services sont simulés. Au lieu de contacter Google ou YouTube, ils écrivent dans le journal ce qu’ils auraient chargé. La démo tourne en Svelte, et les composants React et Vue passent les mêmes tests.

Lib d’UI
Style
Thème
Garde-fou
maison-exemple.fr
Aperçu du site de démo avec la bannière zeste

Chargement de la démo…

Ouvrez « Personnaliser » pour choisir service par service, ou cliquez sur le bouton du placeholder vidéo. « Gestion des services », en bas du site, rouvre les préférences. « Casser la bannière » rapetisse le bouton « Tout refuser », et le garde-fou le signale dans le journal.

Les vidéos et les cartes attendent le consentement

<ConsentGate> affiche votre placeholder jusqu’à ce que le visiteur accepte le service, puis la vidéo ou la carte. Le serveur rend le placeholder. Ajouter une vidéo à la page ne recharge pas celles déjà en lecture.

<ConsentGate service="youtube" embed={{ videoID: 'dQw4w9WgXcQ' }}>
  {#snippet placeholder({ service, allow })}
    <CookiePlaceholder {service} {allow} />
  {/snippet}
</ConsentGate>

Ce que zeste applique

  • zeste n’envoie aucune requête vers un tiers avant le consentement. Les tests le vérifient au niveau réseau du navigateur pour les 247 services. Les modes « sans cookie » de tarteaucitron contactent un tiers avant le consentement, par exemple avec Matomo Cloud ou le Consent Mode avancé. Ils restent désactivés tant que vous ne les activez pas.
  • Dans les bannières générées, « Tout refuser » et « Tout accepter » ont le même style.
  • Rien n’est pré-coché dans les préférences. Un service que le visiteur n’a pas choisi reste refusé.
  • Le visiteur peut retirer son consentement à tout moment. zeste supprime alors les cookies du service, remet les vidéos derrière leur placeholder et propose de recharger la page pour les scripts déjà lancés.
  • Google Consent Mode v2 tient en une ligne, googleConsentMode(). La version balise script l’active par défaut.
  • Le consentement dure 13 mois. zeste le redemande quand vous changez la liste des services.
  • Le RGPD demande de pouvoir montrer qu’un visiteur a consenti. zeste transmet à votre code une trace de chaque choix, avec la date, l’état de chaque service, la version de votre politique et un identifiant aléatoire gardé dans le cookie. Vous choisissez où l’envoyer, et zeste n’envoie rien lui-même.

Des avertissements pendant le développement

En développement, zeste vérifie le rendu réel de votre bannière et prévient dans la console quand elle enfreint une règle mesurable. Cela marche avec n’importe quel design system. Les messages sont en anglais, comme les autres messages destinés aux développeurs.

Une bannière dont le bouton Tout refuser a été rapetissé, avec l’avertissement du garde-fou dans un panneau de console
Console (dev)
⚠ [zeste/consent] "Deny all" is much smaller than "Accept all" (area 24%, text 9px vs 14px). Both choices must be equally prominent.

Le garde-fou prévient aussi quand « Tout refuser » manque, est caché ou difficile à lire, et quand rien ne permet de rouvrir les préférences. Ces vérifications ne remplacent pas un avis juridique.

Garder une trace de chaque choix

createZeste({
  services,
  policyVersion: '2026-10',
  onRecord: (record) =>
    navigator.sendBeacon('/consent', JSON.stringify(record)),
});
// record = {
//   id: '9f2c…', date: '2026-10-09T14:03:12Z',
//   action: 'denyAll', policyVersion: '2026-10',
//   choices: { gtag: false, youtube: false },
// }

L’identifiant est aléatoire et lié à un navigateur. Quand le choix d’un visiteur qui revient est relu dans le cookie, rien n’est envoyé. Sans framework, mettez la même option dans window.zesteConfig.

Passer un site de tarteaucitron à zeste

Nous avons passé à zeste un site d’exemple qui a son propre design system, construit sur Ark UI. Ses visiteurs ont gardé leurs choix, parce que zeste lit le même cookie que tarteaucitron.

Sur un site sans framework, remplacez la balise <script> de tarteaucitron par celle de zeste, au même endroit. Gardez tarteaucitron.init, tarteaucitron.user et tarteaucitron.job tels quels. Les liens vers #tarteaucitron et le balisage youtube_player continuent de fonctionner.

  1. Retirer tarteaucitron et déclarer les mêmes services

    Le site perd deux balises <script>. La configuration, comme gtagUa, passe en TypeScript. L’ancien balisage .youtube_player continue de fonctionner.

  2. Lancer npx @zeste/cli add

    La bannière et les préférences générées fonctionnent tout de suite.

  3. Utiliser les composants du design system

    Les Button, Modal et Toggle du site remplacent les éléments générés.

    Attention aux composants bouton qui ne transmettent pas leurs props à l’élément HTML. Le clic ne fait alors rien, et seul le garde-fou le signale.
  4. Mettre les vidéos derrière <ConsentGate>

    Le serveur rend le placeholder, dans le style du site.

Les préférences cookies dans la modale du site, avec ses interrupteurs
Les préférences dans la modale du site, avec ses propres interrupteurs.

Lire le guide de migration complet

Comment zeste est testé

Le projet compte 625 tests. Playwright pilote Chromium sur les apps d’exemple compilées, et rien n’est simulé dans la page. Le comportement est testé une fois par lib d’UI, l’apparence une fois par style et par thème.

Ce qui est testéCommentTests
Les 247 services du catalogueAvec le réseau intercepté, aucune requête ne part avant le consentement, et chaque service a un effet visible une fois accepté.247
GA4, YouTube, Matomo CloudSur le vrai réseau, avec les vrais scripts. Les cookies _ga apparaissent à l’acceptation et disparaissent au retrait.6
Les composants générésSvelte, React et Vue avec 3 libs d’UI chacun. Clavier, préférences et retrait une fois par lib. Accessibilité avec axe, thème et boutons égaux dans les deux styles, en clair et en sombre. Le garde-fou une fois par framework.174
La même app, six foisSvelteKit, Next.js, Nuxt, et chaque framework sans serveur, dont React 18. Navigation, retrait et garde-fou partout, rendu serveur dans les trois apps qui en ont un.81
Le site migréLes choix faits sous tarteaucitron sont conservés et le design system fonctionne toujours.15
La version balise scriptUn site HTML sans framework. Accessibilité, clavier, retrait, garde-fou, configuration tarteaucitron reprise telle quelle, téléchargements limités aux services déclarés, trace envoyée à chaque choix.13
La langue automatiqueUn navigateur en allemand, en norvégien, et dans une langue sans traduction. Une seule langue est téléchargée.5
Moteur, catalogue, bindings, CLITests unitaires, dont @zeste/cli init sur un vieux site PHP et une vérification que les exemples correspondent à ce que génère @zeste/cli add.84

État du projet

La bibliothèque fonctionne, est testée et publiée sur npm.

Fait

  • Le moteur, le catalogue complet et les textes en 36 langues.
  • Les bindings Svelte 5, React 18 et 19, et Vue 3, avec ou sans rendu serveur (SvelteKit, Next.js, Nuxt).
  • Une CLI qui génère les composants pour 3 frameworks et 3 libs d’UI en deux styles, et @zeste/cli init, qui trouve les services déjà utilisés par un projet.
  • Une version balise script pour les sites sans framework, qui remplace tarteaucitron.js et garde sa configuration.
  • La langue automatique, d’après la page puis le navigateur, parmi 36.
  • Une trace de chaque choix, prête à envoyer à votre serveur comme preuve de consentement.
  • Le garde-fou de conformité en développement.
  • La migration depuis tarteaucitron sans perdre les choix des visiteurs.
  • Les packages et la CLI publiés sur npm sous le scope @zeste.

À venir

  • Des boutons de bannière qui fonctionnent sans JavaScript, pour les connexions très lentes.
  • Des tests sur Firefox et Safari.

Questions

Faut-il encore charger tarteaucitron.js ?

Non. zeste reprend son catalogue de services et ses traductions, et remplace son moteur et son interface. Le script de tarteaucitron n’est jamais chargé.

Mes visiteurs vont-ils revoir la bannière après la migration ?

Non. zeste lit et écrit le même cookie que tarteaucitron, donc les choix déjà faits restent valables.

Mon design system n’utilise ni Bits UI, ni Ark UI, ni shadcn. Puis-je utiliser zeste ?

Oui. Partez de la variante générée la plus proche et remplacez les imports par vos composants. Les actions de zeste fonctionnent sur tout bouton qui transmet ses props à son élément HTML : {@attach zeste.actions.denyAll} en Svelte, {...zeste.actions.denyAll} en React et v-bind="zeste.actions.denyAll" en Vue. Un simple gestionnaire de clic sur zeste.denyAll fonctionne aussi, mais le garde-fou ne peut pas vérifier ce bouton.

zeste rend-il mon site conforme ?

Pas à lui seul, et aucun outil ne le peut. zeste applique des réglages stricts par défaut et vérifie ce qui se mesure, comme les requêtes avant consentement, la visibilité des boutons et le lien pour changer d’avis. Vos textes, votre politique de confidentialité et la finalité de chaque cookie restent à faire valider.

Quelle différence avec tarteaucitron Pro ?

tarteaucitron Pro est un service hébergé, avec un tableau de bord et un journal des consentements. zeste est une bibliothèque qui tourne dans votre projet. Pour tenir un journal, zeste transmet chaque choix à votre code avec onRecord, et vous l’envoyez à votre propre serveur.

Mon site n’a pas de framework. Puis-je utiliser zeste ?

Oui. Une balise <script> ajoute une bannière, des préférences et des placeholders, en deux styles. Sur un site qui utilise déjà tarteaucitron, remplacez sa balise et zeste lit la configuration existante.

Ça fonctionne avec React et Vue ?

Oui. @zeste/react gère React 18 et 19 et Next.js, et @zeste/vue gère Vue 3 et Nuxt. Les deux ont la même API que @zeste/svelte, et zeste add génère les composants pour Radix, Reka UI, Ark UI et shadcn. Pour les autres frameworks, le moteur (@zeste/core) et le catalogue fonctionnent sans.

Quelle est la licence ?

MIT, comme tarteaucitron.js. Le catalogue de services et les traductions viennent de tarteaucitron.js, Copyright AmauriC.