> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Créer un carrousel d'onboarding piloté par boutons

> Créez un flux d'onboarding multi-étapes en utilisant des Messages In-App HTML avec navigation par boutons et fermeture automatique.

## Aperçu

Ce tutoriel vous montre comment créer un **carrousel d'onboarding multi-étapes** en utilisant un seul Message In-App HTML. Contrairement aux carrousels traditionnels qui reposent sur des gestes de balayage, cette approche utilise une **navigation par boutons** et garde toutes les étapes dans un seul message.

**Ce que vous allez construire :**

* Un flux d'onboarding en deux étapes avec images, texte et boutons
* Navigation par boutons (appuyez sur « Suivant » pour avancer, appuyez sur « Commencer » pour fermer)
* Points indicateurs de progression
* Transitions en fondu fluides entre les étapes

<Frame caption="Exemple de carrousel d'onboarding avec navigation par boutons">
  <img src="https://mintcdn.com/onesignal/t_Vr4vLAhranGx-O/images/docs/onboarding-carousel-demo.gif?s=39418d8af9896323694f46f9cb6eebec" alt="Carrousel d'onboarding montrant l'écran de bienvenue avec image, texte et bouton Suivant" style={{width: '200px', maxWidth: '100%'}} width="768" height="1660" data-path="images/docs/onboarding-carousel-demo.gif" />
</Frame>

**Utilisez cette approche lorsque vous souhaitez :**

* Guider les utilisateurs à travers un **court flux d'onboarding ou d'éducation** (2-5 étapes)
* Exiger que les utilisateurs **appuient explicitement sur un bouton** pour continuer (pas de gestes de balayage)
* Garder tout dans **un seul Message In-App HTML** pour simplifier
* Fermer automatiquement le message lorsque le flux est terminé

<Info>
  Ce guide utilise un Message In-App HTML pour un contrôle total. Vous pouvez également [créer des flux d'onboarding basés sur des cartes avec l'éditeur glisser-déposer](./design-your-in-app-message#carousels)—ces cartes sont balayables mais offrent moins de personnalisation.
</Info>

***

## Prérequis

Avant de commencer, assurez-vous d'avoir :

* Une application OneSignal active avec les Messages In-App activés
* [La permission de créer ou modifier des Messages In-App HTML](./manage-team-members#team-members)
* Le [Mobile SDK](./mobile-sdk-setup) installé dans votre application mobile
* Une compréhension de base du HTML, CSS et JavaScript

***

## Comment fonctionne le flux multi-étapes

Avant de plonger dans le code, il est important de comprendre l'approche technique. Cette implémentation utilise **un seul Message In-App HTML** qui bascule entre les étapes en **affichant et masquant le contenu**, sans charger plusieurs messages séparés.

L'architecture repose sur **quatre composants principaux** :

<Steps>
  <Step title="Conteneurs de carte pour chaque étape">
    Chaque étape est enveloppée dans un `<div>` avec la classe `card` et un ID unique :

    ```html theme={null}
    <div id="card-0" class="card active">...</div>
    <div id="card-1" class="card">...</div>
    ```

    * Toutes les cartes existent simultanément dans le DOM
    * Une seule carte est visible à la fois (contrôlée par la classe `active`)
  </Step>

  <Step title="Contrôle de visibilité CSS">
    Le CSS gère la logique d'affichage/masquage en utilisant l'opacité et les événements de pointeur :

    ```css theme={null}
    .card {
      opacity: 0;
      pointer-events: none;  /* Empêche l'interaction avec les cartes cachées */
      transition: opacity .25s ease;
    }

    .card.active {
      opacity: 1;
      pointer-events: auto;  /* Permet l'interaction avec la carte visible */
    }
    ```

    **Pourquoi c'est important :**

    * `opacity: 0` cache la carte visuellement mais la garde dans la mise en page
    * `pointer-events: none` empêche les clics accidentels sur les cartes cachées
    * `transition` crée des effets de fondu fluides
  </Step>

  <Step title="Gestion d'état JavaScript">
    La fonction `setActive(i)` contrôle quelle carte est visible :

    ```javascript theme={null}
    function setActive(i) {
      // Mettre à jour la visibilité des cartes
      document.getElementById("card-0").className = i === 0 ? "card active" : "card";
      document.getElementById("card-1").className = i === 1 ? "card active" : "card";

      // Mettre à jour les points de progression
      var dots = document.getElementById("dots").children;
      dots[0].classList.toggle("active", i === 0);
      dots[1].classList.toggle("active", i === 1);
    }
    ```

    Cette fonction :

    * Supprime `active` de toutes les cartes
    * Ajoute `active` à la carte cible
    * Met à jour les points indicateurs de progression
  </Step>

  <Step title="Écouteurs d'événements des boutons">
    Les boutons déclenchent la navigation ou la fermeture :

    ```javascript theme={null}
    // Avancer à l'étape suivante
    document.getElementById("next-0").addEventListener("click", function () {
      setActive(1);
    });

    // Fermer le Message In-App
    document.getElementById("done").addEventListener("click", function (e) {
      if (window.OneSignalIamApi && OneSignalIamApi.close) {
        OneSignalIamApi.close(e);
      }
    });
    ```

    **Important :** `OneSignalIamApi.close(e)` est la méthode du SDK OneSignal qui ferme le Message In-App depuis le HTML.
  </Step>
</Steps>

<Note>
  **Point clé :** C'est un modèle d'**application monopage (SPA)** au sein d'un Message In-App. Tout le contenu est chargé une fois, et JavaScript gère les changements d'état sans rechargement.
</Note>

***

## Étape 1 : Créer un nouveau Message In-App HTML

1. Dans le tableau de bord OneSignal, allez dans **Messages → In-App Messages**
2. Cliquez sur **New In-App Message**
3. Sélectionnez **HTML** comme type de message
4. Choisissez une mise en page **Full Screen** ou **Large** (recommandé pour l'onboarding pour maximiser l'impact visuel)
5. Continuez vers l'éditeur HTML

<Note>
  L'aperçu de l'éditeur HTML peut ne pas refléter entièrement le comportement à l'exécution. Testez toujours sur un appareil réel ou un utilisateur test pour vérifier les animations, le comportement des boutons et l'action de fermeture.
</Note>

***

## Étape 2 : Ajouter le modèle HTML

Remplacez le contenu de l'éditeur par le modèle ci-dessous. Ce modèle comprend :

* **Code autonome :** Tout le HTML, CSS et JavaScript dans un seul fichier
* **Navigation par boutons :** Pas de gestes de balayage (plus fiable sur différents appareils)
* **Transitions en fondu :** Changements d'opacité fluides entre les étapes
* **Intégration SDK OneSignal :** Utilise `OneSignalIamApi.close(e)` pour fermer le message
* **Optimisé pour mobile :** Mise en page responsive avec balise meta viewport

<Accordion title="Voir le modèle HTML complet">
  ```html theme={null}
  <!doctype html>
  <html>
  <head>
    <meta charset="UTF-8" />
    <!-- viewport-fit=cover assure la couverture de la zone de sécurité sur les appareils à encoche -->
    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
    <style>
      /* Styles de base - reset et police système */
      html, body {
        margin: 0;
        padding: 0;
        background: #ffffff;
        font-family: -apple-system, system-ui;
      }

      /* Conteneur principal avec padding */
      .wrap {
        padding: 28px 22px 24px;
      }

      /* Conteneur de scène - maintient toutes les cartes à la même position */
      .stage {
        position: relative;
        min-height: 74vh;  /* Assure suffisamment d'espace vertical */
      }

      /* Carte - chaque étape du flux d'onboarding */
      .card {
        position: absolute;  /* Toutes les cartes se superposent à la même position */
        inset: 0;            /* Couverture complète de la scène */
        display: flex;
        flex-direction: column;
        align-items: center;
        opacity: 0;               /* Cachée par défaut */
        pointer-events: none;     /* Empêche les clics quand cachée */
        transition: opacity .25s ease;  /* Effet de fondu fluide */
      }

      /* Carte active est visible et interactive */
      .card.active {
        opacity: 1;
        pointer-events: auto;
      }

      /* Typographie */
      h1 {
        margin: 44px 0 12px;
        font-size: 26px;
        text-align: center;
      }

      p {
        margin: 0;
        color: #6b7280;
        text-align: center;
        max-width: 260px;
        line-height: 1.35;
      }

      /* Conteneur d'image - carré avec coins arrondis */
      .image {
        width: 240px;
        height: 240px;
        border-radius: 16px;
        margin: 24px 0 12px;
        background-size: cover;
        background-position: center;
      }

      /* Bouton principal */
      .btn {
        margin-top: auto;  /* Pousse le bouton en bas de la carte */
        width: 100%;
        max-width: 260px;
        height: 52px;
        border: 0;
        border-radius: 12px;
        background: #3b82f6;  /* Bleu - personnalisez selon votre marque */
        color: #fff;
        font-size: 18px;
        font-weight: 600;
      }

      /* Points indicateurs de progression */
      .dots {
        display: flex;
        justify-content: center;
        gap: 8px;
        padding: 12px 0 8px;
      }

      .dot {
        width: 8px;
        height: 8px;
        border-radius: 999px;
        background: #d1d5db;  /* Couleur du point inactif */
      }

      .dot.active {
        background: #6b7280;  /* Couleur du point actif */
        transform: scale(1.15);  /* Légèrement plus grand quand actif */
      }
    </style>
  </head>

  <body>
    <div class="wrap">
      <div class="stage">

        <!-- ÉTAPE 1 : Carte de bienvenue (commence visible avec la classe "active") -->
        <div id="card-0" class="card active">
          <h1>Bienvenue</h1>
          <div
            class="image"
            style="background-image: url('https://images.pexels.com/photos/6153129/pexels-photo-6153129.jpeg');">
          </div>
          <p>Construisez une habitude quotidienne calme en quelques minutes.</p>
          <button
            id="next-0"
            class="btn"
            data-onesignal-unique-label="onboarding_next_0">
            Suivant
          </button>
        </div>

        <!-- ÉTAPE 2 : Carte de respiration (commence cachée, affichée quand l'utilisateur appuie sur "Suivant") -->
        <div id="card-1" class="card">
          <h1>Respirez</h1>
          <div
            class="image"
            style="background-image: url('https://images.pexels.com/photos/417173/pexels-photo-417173.jpeg');">
          </div>
          <p>Respiration guidée quand vous avez besoin de vous recentrer.</p>
          <button
            id="done"
            class="btn"
            data-onesignal-unique-label="onboarding_done">
            Commencer
          </button>
        </div>

      </div>

      <!-- Indicateur de progression : 2 points, le premier commence actif -->
      <div class="dots" id="dots">
        <div class="dot active"></div>
        <div class="dot"></div>
      </div>
    </div>

    <script>
      (function () {
        /**
         * Basculer entre les cartes en alternant la classe "active"
         * @param {number} i - Index de la carte à afficher (0 ou 1)
         */
        function setActive(i) {
          // Mettre à jour la visibilité des cartes
          document.getElementById("card-0").className = i === 0 ? "card active" : "card";
          document.getElementById("card-1").className = i === 1 ? "card active" : "card";

          // Mettre à jour les points de progression
          var dots = document.getElementById("dots").children;
          dots[0].classList.toggle("active", i === 0);
          dots[1].classList.toggle("active", i === 1);
        }

        // Bouton : Suivant (carte 0 → carte 1)
        document.getElementById("next-0").addEventListener("click", function () {
          setActive(1);
        });

        // Bouton : Commencer (ferme le Message In-App)
        document.getElementById("done").addEventListener("click", function (e) {
          // Vérifier si l'API IAM OneSignal est disponible
          if (window.OneSignalIamApi && OneSignalIamApi.close) {
            OneSignalIamApi.close(e);  // Fermer le message
          }
        });
      })();
    </script>
  </body>
  </html>
  ```
</Accordion>

***

## Étape 3 : Personnaliser votre contenu

### Sûr à personnaliser

Vous pouvez modifier ces éléments sans casser la fonctionnalité :

**Contenu :**

* Texte du titre dans les balises `<h1>`
* Texte du corps dans les balises `<p>`
* Libellés des boutons (`Suivant`, `Commencer`)
* URLs d'images dans les styles `background-image: url('...')`

**Style visuel :**

* Couleurs : Changez l'arrière-plan de `.btn`, la couleur du texte ou les couleurs des points
* Espacement : Ajustez le padding et les marges
* Typographie : Modifiez font-family, font-size, font-weight
* Rayon de bordure : Mettez à jour les valeurs `border-radius` pour les boutons et images

### Ajouter plus d'étapes

Pour ajouter une troisième étape, suivez ce modèle :

1. **Ajouter la carte HTML :**

```html theme={null}
<div id="card-2" class="card">
  <h1>Votre titre</h1>
  <div class="image" style="background-image: url('votre-url-image');"></div>
  <p>Votre description</p>
  <button id="next-2" class="btn">Suivant</button>
</div>
```

2. **Ajouter un point de progression :**

```html theme={null}
<div class="dots" id="dots">
  <div class="dot active"></div>
  <div class="dot"></div>
  <div class="dot"></div> <!-- Nouveau point -->
</div>
```

3. **Mettre à jour la fonction `setActive()` :**

```javascript theme={null}
function setActive(i) {
  document.getElementById("card-0").className = i === 0 ? "card active" : "card";
  document.getElementById("card-1").className = i === 1 ? "card active" : "card";
  document.getElementById("card-2").className = i === 2 ? "card active" : "card"; // Nouvelle carte

  var dots = document.getElementById("dots").children;
  dots[0].classList.toggle("active", i === 0);
  dots[1].classList.toggle("active", i === 1);
  dots[2].classList.toggle("active", i === 2); // Nouveau point
}
```

4. **Mettre à jour l'ID du bouton de l'étape précédente :**
   Changez `id="done"` en `id="next-1"` sur le bouton de la carte 1, puis ajoutez un écouteur de clic :

```javascript theme={null}
document.getElementById("next-1").addEventListener("click", function () {
  setActive(2);
});
```

5. **Ajouter le bouton de fermeture à la nouvelle dernière carte (card-2) :**

```javascript theme={null}
document.getElementById("done").addEventListener("click", function (e) {
  if (window.OneSignalIamApi && OneSignalIamApi.close) {
    OneSignalIamApi.close(e);
  }
});
```

<Warning>
  Gardez les flux d'onboarding courts (maximum 2-4 étapes). Les utilisateurs abandonnent rapidement dans les flux plus longs. Testez les taux de complétion avec le [suivi des clics](./in-app-message-api#click-name).
</Warning>

***

## Étape 4 : Tester le Message In-App

### Liste de vérification des tests

1. **Enregistrez** le message dans le tableau de bord OneSignal
2. **Configurez les paramètres de livraison :**
   * Définissez les conditions de déclenchement (ex., début de session, vue de page spécifique)
   * Choisissez votre audience cible ou sélectionnez un utilisateur test
3. **Envoyez à un appareil de test :**
   * Utilisez les [Utilisateurs test](./test-users) pour prévisualiser sans affecter les utilisateurs de production
   * Installez votre application sur un appareil physique (recommandé plutôt que les simulateurs pour un comportement précis)
4. **Vérifiez la fonctionnalité :**
   * ✓ La première carte apparaît avec le bon contenu
   * ✓ Le bouton « Suivant » avance à la carte 2
   * ✓ Les points de progression se mettent à jour correctement
   * ✓ Les transitions en fondu sont fluides
   * ✓ Le bouton « Commencer » ferme le message
   * ✓ Le message ne réapparaît pas immédiatement (vérifiez les paramètres de limitation de fréquence)

<Note>
  Les simulateurs/émulateurs peuvent ne pas refléter précisément le comportement de l'appareil réel, en particulier pour les interactions tactiles et les intégrations SDK. Testez toujours sur des appareils physiques avant de lancer en production.
</Note>

### Dépannage des problèmes courants

| Problème                                 | Cause probable                               | Solution                                                                                                                    |
| ---------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Le message n'apparaît pas                | Conditions de déclenchement non remplies     | Vérifiez les [Déclencheurs de Messages In-App](./iam-triggers) et confirmez que votre utilisateur test remplit les critères |
| Les boutons ne fonctionnent pas          | Erreurs JavaScript ou IDs non correspondants | Vérifiez la console du navigateur pour les erreurs ; vérifiez que les IDs des boutons correspondent aux IDs des écouteurs   |
| Les images ne se chargent pas            | Problèmes CORS ou URLs invalides             | Utilisez des URLs HTTPS ; testez d'abord les URLs d'images dans un navigateur                                               |
| Le message apparaît mais ne se ferme pas | SDK OneSignal non chargé                     | Vérifiez que la [configuration du Mobile SDK](./mobile-sdk-setup) est complète                                              |

***

## Prochaines étapes

**Suivre l'engagement des utilisateurs :**

* Ajoutez le suivi des clics en utilisant les attributs [`data-onesignal-unique-label`](./in-app-message-api#click-name) (déjà inclus dans le modèle) pour mesurer l'abandon entre les étapes
* Consultez les analyses de clics dans **Messages → In-App Messages → \[Votre message] → Analytics**

**Personnaliser l'expérience :**

* [Taguez les utilisateurs](./in-app-message-api#tag-user) qui terminent l'onboarding (ex., `onboarding_completed: true`)
* Utilisez les tags pour [segmenter les utilisateurs](./segmentation) et éviter de remontrer le flux d'onboarding
* [Ajoutez des données utilisateur](./add-user-data-tags) pour personnaliser le contenu des futurs messages

**Personnalisation avancée :**

* [Lien profond](./deep-linking#send-in-app-messages-with-deep-links) vers un écran spécifique après la fermeture
* Utilisez la [syntaxe Liquid](./using-liquid-syntax) pour personnaliser les titres avec les noms d'utilisateurs ou attributs
* Implémentez des tests A/B avec différents flux d'onboarding pour optimiser les taux de complétion
