Índice del artículo
- Lo que vas a construir
- Paso 1. HTML semántico
- Paso 2. CSS claro y adaptable
- Paso 3. JavaScript para abrir, cerrar y atrapar el foco
- Accesibilidad que ayuda a convertir
- UX y contenido que generan resultados
- Recetas de disparadores habituales
- Intención de salida en escritorio
- Mostrar después de cierta interacción
- Una sola vez por sesión
- Medir y mejorar con A/B testing
- Integrarlo en tu sitio
- Errores comunes a evitar
- Para profundizar
Publicado en: 21 Ago 2025
No hace falta un framework para crear un buen popup. Con HTML semántico, un poco de CSS moderno y JavaScript puro podés construir una ventana modal rápida, accesible y lista para convertir tanto en escritorio como en móvil. En esta guía vas a programar un componente reutilizable, elegir disparadores y tiempos efectivos, y optimizarlo para usuarios reales.
Si necesitás contexto estratégico, repasá estas mejores prácticas de ventanas emergentes y elegí momentos inteligentes como se explica en cuándo mostrar una ventana emergente. Si recién empezás, este resumen de qué es una ventana emergente te ubica rápidamente.
Lo que vas a construir
Una modal que:
-
Se abre con un botón y puede dispararse por tiempo o por scroll.
-
Bloquea el scroll mientras está visible y lo restaura al cerrar.
-
Atrae el foco del teclado, lo mantiene dentro, permite cerrar con Esc y admite cerrar al tocar el overlay.
-
Anima entrada y salida sin tirones.
-
Es fácil de personalizar y reutilizar.
Si preferís un camino guiado mientras practicás, guardá la Academia de I Love PopUps y clases como cómo crear popups sin programar o cómo analizar los datos de tus popups.
Paso 1. HTML semántico
Empezá con un botón disparador y el contenedor del diálogo. Usá role="dialog" y aria-modal="true". Ocultalo con hidden para que no aparezca en el árbol de accesibilidad.
<button id="openPopup" class="btn">Open offer</button> <div id="popup" class="popup" role="dialog" aria-modal="true" aria-labelledby="popupTitle" aria-describedby="popupDesc" hidden> <div class="popup__overlay" data-close></div> <div class="popup__content" role="document"> <button class="popup__close" aria-label="Close popup" data-close>×</button> <h2 id="popupTitle">Get 10% off your first order</h2> <p id="popupDesc">Join our list for tips, deals, and updates. Unsubscribe anytime.</p> <form class="popup__form" action="#" method="post" novalidate> <label for="email">Email</label> <input id="email" type="email" required placeholder="you@example.com" autocomplete="email"> <button type="submit" class="btn btn--primary">Subscribe</button> <small class="form-hint">No spam. One-click opt out.</small> </form> </div> </div>
Para ideas de diseño y microcopys que realmente convierten, mirá estas buenas prácticas para crear popups que convierten.
Paso 2. CSS claro y adaptable
Variables para tematizar, transiciones suaves con opacity y transform, y un overlay que cubre toda la pantalla. Evitá animaciones exageradas.
:root {
--popup-bg: #ffffff;
--overlay-bg: rgba(0, 0, 0, 0.5);
--radius: 12px;
--max-w: 520px;
--gap: 16px;
}
.popup[hidden] { display: none; }
.popup__overlay {
position: fixed; inset: 0; background: var(--overlay-bg);
opacity: 0; transition: opacity 200ms ease;
}
.popup__content {
position: fixed; inset: 0; display: grid; place-items: center;
padding: 24px;
}
.popup__content > * {
width: min(var(--max-w), calc(100vw - 32px));
background: var(--popup-bg);
border-radius: var(--radius);
box-shadow: 0 10px 30px rgba(0,0,0,.15);
padding: 24px;
transform: translateY(20px) scale(.98);
opacity: 0;
transition: transform 220ms ease, opacity 220ms ease;
}
.popup.open .popup__overlay { opacity: 1; }
.popup.open .popup__content > * {
transform: translateY(0) scale(1);
opacity: 1;
}
.popup__close {
position: absolute; top: 10px; right: 12px;
background: transparent; border: 0; font-size: 24px; cursor: pointer;
}
.popup__form { display: grid; gap: var(--gap); margin-top: 8px; }
.btn { cursor: pointer; padding: 10px 16px; border: 1px solid #111; background: #fff; }
.btn--primary { background: #111; color: #fff; border-color: #111; }
Paso 3. JavaScript para abrir, cerrar y atrapar el foco
El script:
-
Alterna
hiddeny la clase.abierto. -
Recuerda qué elemento tenía el foco y lo restaura al cerrar.
-
Atrapa el foco dentro del diálogo.
-
Cierra con overlay o con Esc.
<script>
(() => {
const popup = document.getElementById('popup');
const openBtn = document.getElementById('openPopup');
const overlay = popup.querySelector('.popup__overlay');
const closeEls = popup.querySelectorAll('[data-close]');
let lastFocus = null;
function getFocusable(container) {
return [...container.querySelectorAll(
'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
)].filter(el => el.offsetParent !== null);
}
function openPopup() {
lastFocus = document.activeElement;
popup.hidden = false;
popup.classList.add('open');
document.documentElement.style.overflow = 'hidden';
const focusables = getFocusable(popup);
if (focusables.length) focusables[0].focus();
document.addEventListener('keydown', onKeydown);
popup.addEventListener('keydown', trapFocus);
}
function closePopup() {
popup.classList.remove('open');
popup.hidden = true;
document.documentElement.style.overflow = '';
document.removeEventListener('keydown', onKeydown);
popup.removeEventListener('keydown', trapFocus);
if (lastFocus) lastFocus.focus();
}
function onKeydown(e) {
if (e.key === 'Escape') closePopup();
}
function trapFocus(e) {
if (e.key !== 'Tab') return;
const f = getFocusable(popup);
if (!f.length) return;
const first = f[0], last = f[f.length - 1];
if (e.shiftKey && document.activeElement === first) {
last.focus(); e.preventDefault();
} else if (!e.shiftKey && document.activeElement === last) {
first.focus(); e.preventDefault();
}
}
openBtn.addEventListener('click', openPopup);
overlay.addEventListener('click', closePopup);
closeEls.forEach(el => el.addEventListener('click', closePopup));
// Optional triggers
// 1) Timed trigger after 10 seconds
setTimeout(() => { /* openPopup(); */ }, 10000);
// 2) Scroll trigger at 50%
const onScroll = () => {
const scrolled = (window.scrollY + window.innerHeight) / document.documentElement.scrollHeight;
if (scrolled > 0.5) {
// openPopup();
window.removeEventListener('scroll', onScroll);
}
};
window.addEventListener('scroll', onScroll, { passive: true });
})();
</script>
Para decidir el momento exacto, apoyate en cómo utilizar ventanas emergentes en tu web y en el análisis del impacto de un popup en tu sitio web.
Accesibilidad que ayuda a convertir
-
Roles y labels correctos:
role="dialog" aria-modal="true"conaria-labelledbyyaria-describedby. -
Soporte de teclado completo: mover el foco al abrir, atraparlo y devolverlo al cerrar. Esc debe cerrar.
-
Foco visible: no elimines outlines sin reemplazarlos por un estilo claro.
-
Botón de cierre accesible y grande.
-
Preferencias de movimiento: considerá
@media (prefers-reduced-motion: reduce)para reducir animaciones.
Para más hábitos que elevan usabilidad y resultados, mirá cómo aprovechar al máximo I Love PopUps.
UX y contenido que generan resultados
-
Beneficio concreto y específico. Incentivos claros suelen ganar.
-
Formularios cortos. Pedir solo email suele rendir más.
-
Timing contextual. En páginas informativas, suele funcionar mejor un disparador por scroll o salida. Ver ejemplos en cuándo mostrar una ventana emergente.
-
Cierre fácil. Botón visible y overlay que acepta clic.
-
No apiles modales.
-
Móvil primero. Objetivos táctiles grandes, espaciado generoso y texto legible.
-
Coherencia visual con la marca. Para ideas prácticas, repasá estas mejores prácticas de ventanas emergentes.
Recetas de disparadores habituales
Intención de salida en escritorio
(function() {
let fired = false;
function exitIntent(e) {
if (fired) return;
if (e.clientY <= 0) {
// openPopup();
fired = true;
window.removeEventListener('mouseout', exitIntent);
}
}
window.addEventListener('mouseout', exitIntent);
})();
Mostrar después de cierta interacción
let interactions = 0;
function maybeShow() {
interactions += 1;
if (interactions === 2) {
// openPopup();
document.removeEventListener('click', maybeShow);
document.removeEventListener('scroll', maybeShow);
}
}
document.addEventListener('click', maybeShow, { passive: true });
document.addEventListener('scroll', maybeShow, { passive: true });
Una sola vez por sesión
if (!sessionStorage.getItem('nl-popup')) {
setTimeout(() => {
// openPopup();
sessionStorage.setItem('nl-popup', '1');
}, 8000);
}
Medir y mejorar con A/B testing
Cambios pequeños en el título, imagen o timing pueden mover mucho la aguja. La forma más rápida de aprender es hacer test A/B en tus popups. Probá una variable por vez, corré el test el tiempo suficiente y quedate con el ganador para luego iterar.
Cuando tengas datos, practicá cómo analizar los datos de tus popups: mirá vistas, CTR, envíos del formulario y resultados posteriores como compras o registros.
Integrarlo en tu sitio
Si tu sitio usa un constructor o plantillas, podés insertar el HTML en el layout y cargar el script global. Si preferís pegar un único snippet, esta guía sobre cómo instalar un script en tu sitio web te ayuda a ubicar el código sin romper el renderizado inicial.
Errores comunes a evitar
-
Ocultar o minimizar demasiado el botón de cierre.
-
Repetir el mismo popup varias veces en la misma sesión.
-
Disparar demasiado pronto cuando la persona aún se orienta.
-
Validaciones que bloquean la navegación por teclado.
-
Animaciones excesivas que ignoran
prefers-reduced-motion.
Para profundizar
-
Ideas rápidas para crear popups sin programar.
-
Un repaso de cómo utilizar ventanas emergentes en tu web.
-
Perspectiva del impacto de un popup en tu sitio web.
Publicá tu primera versión, juntá datos una semana y volvé a iterar. Los popups funcionan cuando se sienten oportunos, relevantes y respetuosos.