Pular para o conteúdo principal

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
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
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—esses cards são deslizáveis, mas oferecem menos personalização.

Pré-requisitos

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

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:
1

Contêineres de card para cada etapa

Cada etapa é envolvida em um <div> com a classe card e um ID único:
<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)
2

Controle de visibilidade CSS

O CSS lida com a lógica de mostrar/ocultar usando opacidade e eventos de ponteiro:
.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
3

Gerenciamento de estado JavaScript

A função setActive(i) controla qual card está visível:
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
4

Listeners de eventos dos botões

Os botões disparam navegação ou fechamento:
// 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.
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.

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

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
<!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>

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:
<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>
  1. Adicionar um ponto de progresso:
<div class="dots" id="dots">
  <div class="dot active"></div>
  <div class="dot"></div>
  <div class="dot"></div> <!-- Novo ponto -->
</div>
  1. Atualizar a função setActive():
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
}
  1. 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:
document.getElementById("next-1").addEventListener("click", function () {
  setActive(2);
});
  1. Adicionar o botão de fechar ao novo último card (card-2):
document.getElementById("done").addEventListener("click", function (e) {
  if (window.OneSignalIamApi && OneSignalIamApi.close) {
    OneSignalIamApi.close(e);
  }
});
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.

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

Resolução de problemas comuns

ProblemaCausa provávelSolução
Mensagem não apareceCondições de gatilho não atendidasVerifique os Gatilhos de Mensagens In-App e confirme que seu usuário de teste atende aos critérios
Botões não funcionamErros de JavaScript ou incompatibilidade de IDVerifique o console do navegador por erros; confirme que os IDs dos botões correspondem aos IDs dos listeners de eventos
Imagens não carregamProblemas de CORS ou URLs inválidasUse URLs HTTPS; teste as URLs das imagens em um navegador primeiro
Mensagem aparece mas não fechaSDK do OneSignal não carregadoVerifique se a configuração do Mobile SDK está completa

Próximos passos

Rastrear engajamento dos usuários:
  • Adicione rastreamento de cliques usando atributos data-onesignal-unique-label (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: Personalização avançada:
  • Deep link usuários para uma tela específica após o fechamento
  • Use sintaxe Liquid 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