Saltar al contenido principal

Descripción general

Este tutorial te muestra cómo crear un carrusel de onboarding de múltiples pasos usando un único Mensaje In-App HTML. A diferencia de los carruseles tradicionales que dependen de gestos de deslizamiento, este enfoque usa navegación por botones y mantiene todos los pasos dentro de un solo mensaje. Lo que construirás:
  • Un flujo de onboarding de dos pasos con imágenes, texto y botones
  • Navegación por botones (toca “Siguiente” para avanzar, toca “Comenzar” para cerrar)
  • Puntos indicadores de progreso
  • Transiciones suaves de desvanecimiento entre pasos
Usa este enfoque cuando quieras:
  • Guiar a los usuarios a través de un flujo corto de onboarding o educación (2-5 pasos)
  • Requerir que los usuarios toquen explícitamente un botón para continuar (sin gestos de deslizamiento)
  • Mantener todo dentro de un único Mensaje In-App HTML para simplificar
  • Cerrar el mensaje automáticamente cuando el flujo se complete
Esta guía usa un Mensaje In-App HTML para control total. También puedes construir flujos de onboarding basados en tarjetas con el editor de arrastrar y soltar—esas tarjetas son deslizables pero ofrecen menos personalización.

Requisitos previos

Antes de comenzar, asegúrate de tener:

Cómo funciona el flujo de múltiples pasos

Antes de profundizar en el código, es importante entender el enfoque técnico. Esta implementación usa un único Mensaje In-App HTML que cambia entre pasos mostrando y ocultando contenido, no cargando múltiples mensajes separados. La arquitectura se basa en cuatro componentes principales:
1

Contenedores de tarjeta para cada paso

Cada paso está envuelto en un <div> con la clase card y un ID único:
<div id="card-0" class="card active">...</div>
<div id="card-1" class="card">...</div>
  • Todas las tarjetas existen simultáneamente en el DOM
  • Solo una tarjeta es visible a la vez (controlada por la clase active)
2

Control de visibilidad con CSS

CSS maneja la lógica de mostrar/ocultar usando opacidad y eventos de puntero:
.card {
  opacity: 0;
  pointer-events: none;  /* Previene interacción con tarjetas ocultas */
  transition: opacity .25s ease;
}

.card.active {
  opacity: 1;
  pointer-events: auto;  /* Permite interacción con tarjeta visible */
}
Por qué esto importa:
  • opacity: 0 oculta la tarjeta visualmente pero la mantiene en el diseño
  • pointer-events: none previene clics accidentales en tarjetas ocultas
  • transition crea efectos suaves de desvanecimiento
3

Gestión de estado con JavaScript

La función setActive(i) controla qué tarjeta es visible:
function setActive(i) {
  // Actualizar visibilidad de tarjetas
  document.getElementById("card-0").className = i === 0 ? "card active" : "card";
  document.getElementById("card-1").className = i === 1 ? "card active" : "card";

  // Actualizar puntos de progreso
  var dots = document.getElementById("dots").children;
  dots[0].classList.toggle("active", i === 0);
  dots[1].classList.toggle("active", i === 1);
}
Esta función:
  • Elimina active de todas las tarjetas
  • Agrega active a la tarjeta objetivo
  • Actualiza los puntos indicadores de progreso
4

Listeners de eventos de botón

Los botones activan la navegación o el cierre:
// Avanzar al siguiente paso
document.getElementById("next-0").addEventListener("click", function () {
  setActive(1);
});

// Cerrar el Mensaje In-App
document.getElementById("done").addEventListener("click", function (e) {
  if (window.OneSignalIamApi && OneSignalIamApi.close) {
    OneSignalIamApi.close(e);
  }
});
Importante: OneSignalIamApi.close(e) es el método del SDK de OneSignal que cierra el Mensaje In-App desde dentro del HTML.
Información clave: Este es un patrón de aplicación de página única (SPA) dentro de un Mensaje In-App. Todo el contenido se carga una vez, y JavaScript gestiona los cambios de estado sin recargar.

Paso 1: Crear un nuevo Mensaje In-App HTML

  1. En el panel de OneSignal, ve a Messages → In-App Messages
  2. Haz clic en New In-App Message
  3. Selecciona HTML como tipo de mensaje
  4. Elige un diseño Full Screen o Large (recomendado para onboarding para maximizar el impacto visual)
  5. Continúa al editor HTML
La vista previa del editor HTML puede no reflejar completamente el comportamiento en tiempo de ejecución. Siempre prueba en un dispositivo real o usuario de prueba para verificar animaciones, comportamiento de botones y la acción de cierre.

Paso 2: Agregar la plantilla HTML

Reemplaza el contenido del editor con la plantilla a continuación. Esta plantilla incluye:
  • Código autocontenido: Todo el HTML, CSS y JavaScript en un archivo
  • Navegación por botones: Sin gestos de deslizamiento (más confiable en diferentes dispositivos)
  • Transiciones de desvanecimiento: Cambios suaves de opacidad entre pasos
  • Integración con SDK de OneSignal: Usa OneSignalIamApi.close(e) para cerrar el mensaje
  • Optimizado para móvil: Diseño responsive con meta tag de viewport
<!doctype html>
<html>
<head>
  <meta charset="UTF-8" />
  <!-- viewport-fit=cover asegura cobertura de área segura en dispositivos con notch -->
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
  <style>
    /* Estilos base - reset y fuente del sistema */
    html, body {
      margin: 0;
      padding: 0;
      background: #ffffff;
      font-family: -apple-system, system-ui;
    }

    /* Contenedor principal con padding */
    .wrap {
      padding: 28px 22px 24px;
    }

    /* Contenedor de escenario - mantiene todas las tarjetas en la misma posición */
    .stage {
      position: relative;
      min-height: 74vh;  /* Asegura suficiente espacio vertical */
    }

    /* Tarjeta - cada paso del flujo de onboarding */
    .card {
      position: absolute;  /* Todas las tarjetas se superponen en la misma posición */
      inset: 0;            /* Cobertura completa del escenario */
      display: flex;
      flex-direction: column;
      align-items: center;
      opacity: 0;               /* Oculta por defecto */
      pointer-events: none;     /* Previene clics cuando está oculta */
      transition: opacity .25s ease;  /* Efecto suave de desvanecimiento */
    }

    /* Tarjeta activa es visible e interactiva */
    .card.active {
      opacity: 1;
      pointer-events: auto;
    }

    /* Tipografía */
    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;
    }

    /* Contenedor de imagen - cuadrado con esquinas redondeadas */
    .image {
      width: 240px;
      height: 240px;
      border-radius: 16px;
      margin: 24px 0 12px;
      background-size: cover;
      background-position: center;
    }

    /* Botón principal */
    .btn {
      margin-top: auto;  /* Empuja el botón al fondo de la tarjeta */
      width: 100%;
      max-width: 260px;
      height: 52px;
      border: 0;
      border-radius: 12px;
      background: #3b82f6;  /* Azul - personaliza según tu marca */
      color: #fff;
      font-size: 18px;
      font-weight: 600;
    }

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

    .dot {
      width: 8px;
      height: 8px;
      border-radius: 999px;
      background: #d1d5db;  /* Color de punto inactivo */
    }

    .dot.active {
      background: #6b7280;  /* Color de punto activo */
      transform: scale(1.15);  /* Ligeramente más grande cuando está activo */
    }
  </style>
</head>

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

      <!-- PASO 1: Tarjeta de bienvenida (comienza visible con clase "active") -->
      <div id="card-0" class="card active">
        <h1>Bienvenido</h1>
        <div
          class="image"
          style="background-image: url('https://images.pexels.com/photos/6153129/pexels-photo-6153129.jpeg');">
        </div>
        <p>Construye un hábito diario tranquilo en minutos.</p>
        <button
          id="next-0"
          class="btn"
          data-onesignal-unique-label="onboarding_next_0">
          Siguiente
        </button>
      </div>

      <!-- PASO 2: Tarjeta de respiración (comienza oculta, se muestra cuando el usuario toca "Siguiente") -->
      <div id="card-1" class="card">
        <h1>Respira</h1>
        <div
          class="image"
          style="background-image: url('https://images.pexels.com/photos/417173/pexels-photo-417173.jpeg');">
        </div>
        <p>Respiración guiada cuando necesites reiniciarte.</p>
        <button
          id="done"
          class="btn"
          data-onesignal-unique-label="onboarding_done">
          Comenzar
        </button>
      </div>

    </div>

    <!-- Indicador de progreso: 2 puntos, el primero comienza activo -->
    <div class="dots" id="dots">
      <div class="dot active"></div>
      <div class="dot"></div>
    </div>
  </div>

  <script>
    (function () {
      /**
       * Cambiar entre tarjetas alternando la clase "active"
       * @param {number} i - Índice de la tarjeta a mostrar (0 o 1)
       */
      function setActive(i) {
        // Actualizar visibilidad de tarjetas
        document.getElementById("card-0").className = i === 0 ? "card active" : "card";
        document.getElementById("card-1").className = i === 1 ? "card active" : "card";

        // Actualizar puntos de progreso
        var dots = document.getElementById("dots").children;
        dots[0].classList.toggle("active", i === 0);
        dots[1].classList.toggle("active", i === 1);
      }

      // Botón: Siguiente (tarjeta 0 → tarjeta 1)
      document.getElementById("next-0").addEventListener("click", function () {
        setActive(1);
      });

      // Botón: Comenzar (cierra el Mensaje In-App)
      document.getElementById("done").addEventListener("click", function (e) {
        // Verificar si la API IAM de OneSignal está disponible
        if (window.OneSignalIamApi && OneSignalIamApi.close) {
          OneSignalIamApi.close(e);  // Cerrar el mensaje
        }
      });
    })();
  </script>
</body>
</html>

Paso 3: Personalizar tu contenido

Seguro para personalizar

Puedes modificar estos elementos sin romper la funcionalidad: Contenido:
  • Texto del título en etiquetas <h1>
  • Texto del cuerpo en etiquetas <p>
  • Etiquetas de botones (Siguiente, Comenzar)
  • URLs de imágenes en los estilos background-image: url('...')
Estilo visual:
  • Colores: Cambia el fondo de .btn, color de texto o colores de puntos
  • Espaciado: Ajusta padding y márgenes
  • Tipografía: Modifica font-family, font-size, font-weight
  • Radio de borde: Actualiza valores de border-radius para botones e imágenes

Agregar más pasos

Para agregar un tercer paso, sigue este patrón:
  1. Agregar la tarjeta HTML:
<div id="card-2" class="card">
  <h1>Tu título</h1>
  <div class="image" style="background-image: url('tu-url-de-imagen');"></div>
  <p>Tu descripción</p>
  <button id="next-2" class="btn">Siguiente</button>
</div>
  1. Agregar un punto de progreso:
<div class="dots" id="dots">
  <div class="dot active"></div>
  <div class="dot"></div>
  <div class="dot"></div> <!-- Nuevo punto -->
</div>
  1. Actualizar la función 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"; // Nueva tarjeta

  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); // Nuevo punto
}
  1. Actualizar el ID del botón del paso anterior: Cambia id="done" a id="next-1" en el botón de la tarjeta 1, luego agrega un listener de clic:
document.getElementById("next-1").addEventListener("click", function () {
  setActive(2);
});
  1. Agregar el botón de cierre a la nueva última tarjeta (card-2):
document.getElementById("done").addEventListener("click", function (e) {
  if (window.OneSignalIamApi && OneSignalIamApi.close) {
    OneSignalIamApi.close(e);
  }
});
Mantén los flujos de onboarding cortos (máximo 2-4 pasos). Los usuarios abandonan rápidamente en flujos más largos. Prueba las tasas de finalización con seguimiento de clics.

Paso 4: Probar el Mensaje In-App

Lista de verificación de pruebas

  1. Guarda el mensaje en el panel de OneSignal
  2. Configura los ajustes de entrega:
    • Establece condiciones de activación (ej., inicio de sesión, vista de página específica)
    • Elige tu audiencia objetivo o selecciona un usuario de prueba
  3. Envía a un dispositivo de prueba:
    • Usa Usuarios de prueba para previsualizar sin afectar usuarios de producción
    • Instala tu app en un dispositivo físico (recomendado sobre simuladores para comportamiento preciso)
  4. Verifica la funcionalidad:
    • ✓ La primera tarjeta aparece con contenido correcto
    • ✓ El botón “Siguiente” avanza a la tarjeta 2
    • ✓ Los puntos de progreso se actualizan correctamente
    • ✓ Las transiciones de desvanecimiento son suaves
    • ✓ El botón “Comenzar” cierra el mensaje
    • ✓ El mensaje no reaparece inmediatamente (verifica configuración de límite de frecuencia)
Los simuladores/emuladores pueden no reflejar con precisión el comportamiento del dispositivo real, especialmente para interacciones táctiles e integraciones de SDK. Siempre prueba en dispositivos físicos antes de lanzar a producción.

Solución de problemas comunes

ProblemaCausa probableSolución
El mensaje no apareceCondiciones de activación no cumplidasVerifica los Disparadores de Mensajes In-App y confirma que tu usuario de prueba cumple los criterios
Los botones no funcionanErrores de JavaScript o IDs no coincidenRevisa la consola del navegador por errores; verifica que los IDs de botones coincidan con los IDs de listeners
Las imágenes no carganProblemas de CORS o URLs inválidasUsa URLs HTTPS; prueba las URLs de imágenes en un navegador primero
El mensaje aparece pero no se cierraSDK de OneSignal no cargadoVerifica que la configuración del Mobile SDK esté completa

Próximos pasos

Rastrear engagement de usuarios:
  • Agrega seguimiento de clics usando atributos data-onesignal-unique-label (ya incluidos en la plantilla) para medir abandono entre pasos
  • Ve las analíticas de clics en Messages → In-App Messages → [Tu mensaje] → Analytics
Personalizar la experiencia: Personalización avanzada:
  • Deep link a usuarios a una pantalla específica después del cierre
  • Usa sintaxis Liquid para personalizar títulos con nombres de usuario o atributos
  • Implementa pruebas A/B con diferentes flujos de onboarding para optimizar tasas de finalización