> ## 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.

# Construir um carrossel de onboarding com botões

> Crie um fluxo de onboarding de várias etapas usando Mensagens In-App HTML com navegação por botões e fechamento automático.

## Visão geral

Este tutorial mostra como criar um **carrossel de onboarding de várias etapas** usando uma única Mensagem In-App HTML. Ao contrário dos carrosséis tradicionais que dependem de gestos de deslizar, esta abordagem usa **navegação por botões** e mantém todas as etapas dentro de uma única mensagem.

**O que você vai construir:**

* Um fluxo de onboarding de duas etapas com imagens, texto e botões
* Navegação por botões (toque em "Próximo" para avançar, toque em "Começar" para fechar)
* Pontos indicadores de progresso
* Transições suaves de fade entre as etapas

<Frame caption="Exemplo de carrossel de onboarding com navegação por botões">
  <img src="https://mintcdn.com/onesignal/t_Vr4vLAhranGx-O/images/docs/onboarding-carousel-demo.gif?s=39418d8af9896323694f46f9cb6eebec" alt="Carrossel de onboarding mostrando tela de boas-vindas com imagem, texto e botão Próximo" style={{width: '200px', maxWidth: '100%'}} width="768" height="1660" data-path="images/docs/onboarding-carousel-demo.gif" />
</Frame>

**Use esta abordagem quando quiser:**

* Guiar os usuários através de um **fluxo curto de onboarding ou educação** (2-5 etapas)
* Exigir que os usuários **toquem explicitamente em um botão** para continuar (sem gestos de deslizar)
* Manter tudo dentro de **uma única Mensagem In-App HTML** para simplificar
* Fechar a mensagem automaticamente quando o fluxo for concluído

<Info>
  Este guia usa uma Mensagem In-App HTML para controle total. Você também pode [construir fluxos de onboarding baseados em cards com o editor de arrastar e soltar](./design-your-in-app-message#carousels)—esses cards são deslizáveis, mas oferecem menos personalização.
</Info>

***

## Pré-requisitos

Antes de começar, certifique-se de ter:

* Um aplicativo OneSignal ativo com Mensagens In-App habilitadas
* [Permissão para criar ou editar Mensagens In-App HTML](./manage-team-members#team-members)
* [Mobile SDK](./mobile-sdk-setup) instalado em seu aplicativo móvel
* Conhecimento básico de HTML, CSS e JavaScript

***

## Como funciona o fluxo de várias etapas

Antes de mergulhar no código, é importante entender a abordagem técnica. Esta implementação usa **uma única Mensagem In-App HTML** que alterna entre etapas **mostrando e ocultando conteúdo**, não carregando múltiplas mensagens separadas.

A arquitetura depende de **quatro componentes principais**:

<Steps>
  <Step title="Contêineres de card para cada etapa">
    Cada etapa é envolvida em um `<div>` com a classe `card` e um ID único:

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

    * Todos os cards existem simultaneamente no DOM
    * Apenas um card é visível por vez (controlado pela classe `active`)
  </Step>

  <Step title="Controle de visibilidade CSS">
    O CSS lida com a lógica de mostrar/ocultar usando opacidade e eventos de ponteiro:

    ```css theme={null}
    .card {
      opacity: 0;
      pointer-events: none;  /* Impede interação com cards ocultos */
      transition: opacity .25s ease;
    }

    .card.active {
      opacity: 1;
      pointer-events: auto;  /* Permite interação com card visível */
    }
    ```

    **Por que isso importa:**

    * `opacity: 0` oculta o card visualmente, mas o mantém no layout
    * `pointer-events: none` impede cliques acidentais em cards ocultos
    * `transition` cria efeitos de fade suaves
  </Step>

  <Step title="Gerenciamento de estado JavaScript">
    A função `setActive(i)` controla qual card está visível:

    ```javascript theme={null}
    function setActive(i) {
      // Atualizar visibilidade dos cards
      document.getElementById("card-0").className = i === 0 ? "card active" : "card";
      document.getElementById("card-1").className = i === 1 ? "card active" : "card";

      // Atualizar pontos de progresso
      var dots = document.getElementById("dots").children;
      dots[0].classList.toggle("active", i === 0);
      dots[1].classList.toggle("active", i === 1);
    }
    ```

    Esta função:

    * Remove `active` de todos os cards
    * Adiciona `active` ao card de destino
    * Atualiza os pontos indicadores de progresso
  </Step>

  <Step title="Listeners de eventos dos botões">
    Os botões disparam navegação ou fechamento:

    ```javascript theme={null}
    // Avançar para próxima etapa
    document.getElementById("next-0").addEventListener("click", function () {
      setActive(1);
    });

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

    **Importante:** `OneSignalIamApi.close(e)` é o método do SDK OneSignal que fecha a Mensagem In-App de dentro do HTML.
  </Step>
</Steps>

<Note>
  **Ponto-chave:** Este é um padrão de **aplicação de página única (SPA)** dentro de uma Mensagem In-App. Todo o conteúdo é carregado uma vez, e o JavaScript gerencia as mudanças de estado sem recarregar.
</Note>

***

## Etapa 1: Criar uma nova Mensagem In-App HTML

1. No painel do OneSignal, vá para **Messages → In-App Messages**
2. Clique em **New In-App Message**
3. Selecione **HTML** como tipo de mensagem
4. Escolha o layout **Full Screen** ou **Large** (recomendado para onboarding para maximizar o impacto visual)
5. Continue para o editor HTML

<Note>
  A visualização do editor HTML pode não refletir totalmente o comportamento em tempo de execução. Sempre teste em um dispositivo real ou usuário de teste para verificar animações, comportamento dos botões e a ação de fechar.
</Note>

***

## Etapa 2: Adicionar o template HTML

Substitua o conteúdo do editor pelo template abaixo. Este template inclui:

* **Código autocontido:** Todo HTML, CSS e JavaScript em um arquivo
* **Navegação por botões:** Sem gestos de deslizar (mais confiável em diferentes dispositivos)
* **Transições de fade:** Mudanças suaves de opacidade entre etapas
* **Integração com SDK OneSignal:** Usa `OneSignalIamApi.close(e)` para fechar a mensagem
* **Otimizado para mobile:** Layout responsivo com meta tag viewport

<Accordion title="Ver template HTML completo">
  ```html theme={null}
  <!doctype html>
  <html>
  <head>
    <meta charset="UTF-8" />
    <!-- viewport-fit=cover garante cobertura da área segura em dispositivos com notch -->
    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
    <style>
      /* Estilos base - reset e fonte do sistema */
      html, body {
        margin: 0;
        padding: 0;
        background: #ffffff;
        font-family: -apple-system, system-ui;
      }

      /* Contêiner principal com padding */
      .wrap {
        padding: 28px 22px 24px;
      }

      /* Contêiner de palco - mantém todos os cards na mesma posição */
      .stage {
        position: relative;
        min-height: 74vh;  /* Garante espaço vertical suficiente */
      }

      /* Card - cada etapa do fluxo de onboarding */
      .card {
        position: absolute;  /* Todos os cards se sobrepõem na mesma posição */
        inset: 0;            /* Cobertura total do palco */
        display: flex;
        flex-direction: column;
        align-items: center;
        opacity: 0;               /* Oculto por padrão */
        pointer-events: none;     /* Impede cliques quando oculto */
        transition: opacity .25s ease;  /* Efeito de fade suave */
      }

      /* Card ativo é visível e interativo */
      .card.active {
        opacity: 1;
        pointer-events: auto;
      }

      /* Tipografia */
      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;
      }

      /* Contêiner de imagem - quadrado com cantos arredondados */
      .image {
        width: 240px;
        height: 240px;
        border-radius: 16px;
        margin: 24px 0 12px;
        background-size: cover;
        background-position: center;
      }

      /* Botão principal */
      .btn {
        margin-top: auto;  /* Empurra o botão para o final do card */
        width: 100%;
        max-width: 260px;
        height: 52px;
        border: 0;
        border-radius: 12px;
        background: #3b82f6;  /* Azul - personalize para sua marca */
        color: #fff;
        font-size: 18px;
        font-weight: 600;
      }

      /* Pontos indicadores de progresso */
      .dots {
        display: flex;
        justify-content: center;
        gap: 8px;
        padding: 12px 0 8px;
      }

      .dot {
        width: 8px;
        height: 8px;
        border-radius: 999px;
        background: #d1d5db;  /* Cor do ponto inativo */
      }

      .dot.active {
        background: #6b7280;  /* Cor do ponto ativo */
        transform: scale(1.15);  /* Ligeiramente maior quando ativo */
      }
    </style>
  </head>

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

        <!-- ETAPA 1: Card de boas-vindas (começa visível com classe "active") -->
        <div id="card-0" class="card active">
          <h1>Bem-vindo</h1>
          <div
            class="image"
            style="background-image: url('https://images.pexels.com/photos/6153129/pexels-photo-6153129.jpeg');">
          </div>
          <p>Construa um hábito diário tranquilo em minutos.</p>
          <button
            id="next-0"
            class="btn"
            data-onesignal-unique-label="onboarding_next_0">
            Próximo
          </button>
        </div>

        <!-- ETAPA 2: Card de respiração (começa oculto, aparece quando usuário toca em "Próximo") -->
        <div id="card-1" class="card">
          <h1>Respire</h1>
          <div
            class="image"
            style="background-image: url('https://images.pexels.com/photos/417173/pexels-photo-417173.jpeg');">
          </div>
          <p>Respiração guiada sempre que você precisar de um reset.</p>
          <button
            id="done"
            class="btn"
            data-onesignal-unique-label="onboarding_done">
            Começar
          </button>
        </div>

      </div>

      <!-- Indicador de progresso: 2 pontos, primeiro começa ativo -->
      <div class="dots" id="dots">
        <div class="dot active"></div>
        <div class="dot"></div>
      </div>
    </div>

    <script>
      (function () {
        /**
         * Alternar entre cards usando a classe "active"
         * @param {number} i - Índice do card a exibir (0 ou 1)
         */
        function setActive(i) {
          // Atualizar visibilidade dos cards
          document.getElementById("card-0").className = i === 0 ? "card active" : "card";
          document.getElementById("card-1").className = i === 1 ? "card active" : "card";

          // Atualizar pontos de progresso
          var dots = document.getElementById("dots").children;
          dots[0].classList.toggle("active", i === 0);
          dots[1].classList.toggle("active", i === 1);
        }

        // Botão: Próximo (card 0 → card 1)
        document.getElementById("next-0").addEventListener("click", function () {
          setActive(1);
        });

        // Botão: Começar (fecha a Mensagem In-App)
        document.getElementById("done").addEventListener("click", function (e) {
          // Verificar se a API IAM do OneSignal está disponível
          if (window.OneSignalIamApi && OneSignalIamApi.close) {
            OneSignalIamApi.close(e);  // Fechar a mensagem
          }
        });
      })();
    </script>
  </body>
  </html>
  ```
</Accordion>

***

## Etapa 3: Personalizar seu conteúdo

### Seguro para personalizar

Você pode modificar estes elementos sem quebrar a funcionalidade:

**Conteúdo:**

* Texto do título nas tags `<h1>`
* Texto do corpo nas tags `<p>`
* Rótulos dos botões (`Próximo`, `Começar`)
* URLs de imagens nos estilos `background-image: url('...')`

**Estilo visual:**

* Cores: Altere o fundo do `.btn`, cor do texto ou cores dos pontos
* Espaçamento: Ajuste padding e margens
* Tipografia: Modifique font-family, font-size, font-weight
* Raio de borda: Atualize valores de `border-radius` para botões e imagens

### Adicionar mais etapas

Para adicionar uma terceira etapa, siga este padrão:

1. **Adicionar o card HTML:**

```html theme={null}
<div id="card-2" class="card">
  <h1>Seu Título</h1>
  <div class="image" style="background-image: url('sua-url-de-imagem');"></div>
  <p>Sua descrição</p>
  <button id="next-2" class="btn">Próximo</button>
</div>
```

2. **Adicionar um ponto de progresso:**

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

3. **Atualizar a função `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"; // Novo card

  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); // Novo ponto
}
```

4. **Atualizar o ID do botão da etapa anterior:**
   Altere `id="done"` para `id="next-1"` no botão do card 1, então adicione um listener de clique:

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

5. **Adicionar o botão de fechar ao novo último card (card-2):**

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

<Warning>
  Mantenha os fluxos de onboarding curtos (máximo de 2-4 etapas). Os usuários desistem rapidamente em fluxos mais longos. Teste as taxas de conclusão com [rastreamento de cliques](./in-app-message-api#click-name).
</Warning>

***

## Etapa 4: Testar a Mensagem In-App

### Lista de verificação de testes

1. **Salve** a mensagem no painel do OneSignal
2. **Configure os ajustes de entrega:**
   * Defina condições de gatilho (ex.: início de sessão, visualização de página específica)
   * Escolha seu público-alvo ou selecione um usuário de teste
3. **Envie para um dispositivo de teste:**
   * Use [Usuários de Teste](./test-users) para visualizar sem afetar usuários de produção
   * Instale seu app em um dispositivo físico (recomendado em vez de simuladores para comportamento preciso)
4. **Verifique a funcionalidade:**
   * ✓ O primeiro card aparece com o conteúdo correto
   * ✓ O botão "Próximo" avança para o card 2
   * ✓ Os pontos de progresso atualizam corretamente
   * ✓ As transições de fade são suaves
   * ✓ O botão "Começar" fecha a mensagem
   * ✓ A mensagem não reaparece imediatamente (verifique as configurações de limite de frequência)

<Note>
  Simuladores/emuladores podem não refletir com precisão o comportamento do dispositivo real, especialmente para interações de toque e integrações de SDK. Sempre teste em dispositivos físicos antes de lançar em produção.
</Note>

### Resolução de problemas comuns

| Problema                       | Causa provável                                 | Solução                                                                                                                  |
| ------------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Mensagem não aparece           | Condições de gatilho não atendidas             | Verifique os [Gatilhos de Mensagens In-App](./iam-triggers) e confirme que seu usuário de teste atende aos critérios     |
| Botões não funcionam           | Erros de JavaScript ou incompatibilidade de ID | Verifique o console do navegador por erros; confirme que os IDs dos botões correspondem aos IDs dos listeners de eventos |
| Imagens não carregam           | Problemas de CORS ou URLs inválidas            | Use URLs HTTPS; teste as URLs das imagens em um navegador primeiro                                                       |
| Mensagem aparece mas não fecha | SDK do OneSignal não carregado                 | Verifique se a [configuração do Mobile SDK](./mobile-sdk-setup) está completa                                            |

***

## Próximos passos

**Rastrear engajamento dos usuários:**

* Adicione rastreamento de cliques usando atributos [`data-onesignal-unique-label`](./in-app-message-api#click-name) (já incluídos no template) para medir desistência entre etapas
* Veja análises de cliques em **Messages → In-App Messages → \[Sua Mensagem] → Analytics**

**Personalizar a experiência:**

* [Marque usuários](./in-app-message-api#tag-user) que completam o onboarding (ex.: `onboarding_completed: true`)
* Use tags para [segmentar usuários](./segmentation) e evitar reexibir o fluxo de onboarding
* [Adicione dados de usuário](./add-user-data-tags) para personalizar conteúdo em mensagens futuras

**Personalização avançada:**

* [Deep link](./deep-linking#send-in-app-messages-with-deep-links) usuários para uma tela específica após o fechamento
* Use [sintaxe Liquid](./using-liquid-syntax) para personalizar títulos com nomes de usuários ou atributos
* Implemente testes A/B com diferentes fluxos de onboarding para otimizar taxas de conclusão
