Categoría · Interactivo Nivel de Dificultad · Intermedio Publicado el · 15 de enero de 2024

Banner de Cookies Toast en Esquina

Un elegante banner de consentimiento de cookies estilo toast en esquina con diseño inspirado en notificaciones, animaciones suaves y posicionamiento elegante para una experiencia de usuario no intrusiva manteniendo el cumplimiento del RGPD

#cookie-banner #gdpr #privacy #consent #responsive #modern #animated

Diseño Responsivo

Soporte para Modo Oscuro

No

líneas

989

Compatibilidad del Navegador

No

Vista Previa en Vivo

Interactúa con el componente sin salir de la página.

400px

Banner de Cookies Toast en Esquina

Un sofisticado banner de consentimiento de cookies estilo toast en esquina que imita los sistemas de notificación modernos con posicionamiento elegante, animaciones suaves y diseño no intrusivo. Este banner proporciona una experiencia de notificación familiar mientras asegura controles de privacidad integrales y cumplimiento del RGPD.

Características

  • Diseño de Notificación Toast: Interfaz familiar estilo notificación con posicionamiento en esquina
  • Múltiples Opciones de Posición: Posicionamiento superior-derecha, superior-izquierda, inferior-derecha, inferior-izquierda
  • Animaciones Suaves: Elegantes animaciones de deslizamiento con física de resorte
  • Temporizador de Auto-cierre: Cierre automático opcional con indicador de progreso
  • Soporte de Apilamiento: Múltiples notificaciones pueden apilarse elegantemente
  • Cumplimiento RGPD: Cumplimiento completo con regulaciones de privacidad y requisitos de consentimiento
  • Accesibilidad Primero: Navegación completa por teclado y soporte para lectores de pantalla
  • Diseño Responsivo: Se adapta perfectamente a todos los tamaños de pantalla y orientaciones
  • UI Moderna: Interfaz limpia y profesional con patrones de diseño contemporáneos
  • Optimizado para Rendimiento: Implementación ligera con tiempos de carga rápidos

Vista Previa

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Banner de Cookies Toast en Esquina</title>
    <style>
        :root {
            --color-primario: #3b82f6;
            --color-primario-hover: #2563eb;
            --color-secundario: #6366f1;
            --color-exito: #10b981;
            --color-exito-hover: #059669;
            --color-advertencia: #f59e0b;
            --color-peligro: #ef4444;
            --fondo: #ffffff;
            --superficie: #f8fafc;
            --superficie-elevada: #ffffff;
            --texto-primario: #1f2937;
            --texto-secundario: #6b7280;
            --texto-silenciado: #9ca3af;
            --color-borde: #e5e7eb;
            --sombra-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
            --sombra-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
            --sombra-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
            --sombra-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
            --radio-borde: 8px;
            --radio-borde-lg: 12px;
            --radio-borde-xl: 16px;
            --transicion: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            --transicion-rapida: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
            --ancho-toast: 400px;
            --ancho-max-toast: 90vw;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            color: var(--texto-primario);
            line-height: 1.6;
            padding: 2rem;
            overflow-x: hidden;
            position: relative;
        }

        .contenedor-demo {
            max-width: 1200px;
            margin: 0 auto;
            text-align: center;
            color: white;
            position: relative;
            z-index: 1;
        }

        .contenedor-demo h1 {
            font-size: 3rem;
            font-weight: 800;
            margin-bottom: 1rem;
            background: linear-gradient(135deg, #ffffff 0%, #f0f0f0 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        .contenedor-demo p {
            font-size: 1.2rem;
            opacity: 0.9;
            margin-bottom: 2rem;
        }

        .controles-demo {
            display: flex;
            flex-wrap: wrap;
            gap: 1rem;
            justify-content: center;
            margin-bottom: 2rem;
        }

        .btn-demo {
            padding: 1rem 2rem;
            border: none;
            border-radius: var(--radio-borde-lg);
            background: rgba(255, 255, 255, 0.2);
            backdrop-filter: blur(10px);
            color: white;
            font-family: inherit;
            font-size: 1rem;
            font-weight: 600;
            cursor: pointer;
            transition: var(--transicion);
            border: 1px solid rgba(255, 255, 255, 0.3);
        }

        .btn-demo:hover {
            background: rgba(255, 255, 255, 0.3);
            transform: translateY(-2px);
            box-shadow: var(--sombra-lg);
        }

        /* Contenedor Toast */
        .contenedor-toast {
            position: fixed;
            z-index: 9999;
            pointer-events: none;
            display: flex;
            flex-direction: column;
            gap: 1rem;
            max-width: var(--ancho-max-toast);
        }

        .contenedor-toast.superior-derecha {
            top: 2rem;
            right: 2rem;
        }

        .contenedor-toast.superior-izquierda {
            top: 2rem;
            left: 2rem;
        }

        .contenedor-toast.inferior-derecha {
            bottom: 2rem;
            right: 2rem;
        }

        .contenedor-toast.inferior-izquierda {
            bottom: 2rem;
            left: 2rem;
        }

        /* Banner Toast de Cookies */
        .toast-cookies {
            width: var(--ancho-toast);
            max-width: 100%;
            background: var(--superficie-elevada);
            border-radius: var(--radio-borde-xl);
            box-shadow: var(--sombra-xl);
            border: 1px solid var(--color-borde);
            overflow: hidden;
            transform: translateX(120%);
            transition: all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
            pointer-events: auto;
            position: relative;
        }

        .toast-cookies.mostrar {
            transform: translateX(0);
        }

        .toast-cookies.ocultar {
            transform: translateX(120%);
            opacity: 0;
        }

        /* Toasts posicionados a la izquierda */
        .contenedor-toast.superior-izquierda .toast-cookies,
        .contenedor-toast.inferior-izquierda .toast-cookies {
            transform: translateX(-120%);
        }

        .contenedor-toast.superior-izquierda .toast-cookies.mostrar,
        .contenedor-toast.inferior-izquierda .toast-cookies.mostrar {
            transform: translateX(0);
        }

        .contenedor-toast.superior-izquierda .toast-cookies.ocultar,
        .contenedor-toast.inferior-izquierda .toast-cookies.ocultar {
            transform: translateX(-120%);
        }

        /* Barra de Progreso */
        .progreso-toast {
            position: absolute;
            bottom: 0;
            left: 0;
            height: 4px;
            background: linear-gradient(90deg, var(--color-primario) 0%, var(--color-secundario) 100%);
            border-radius: 0 0 var(--radio-borde-xl) var(--radio-borde-xl);
            transform-origin: left;
            transform: scaleX(0);
            transition: transform linear;
        }

        .progreso-toast.activo {
            animation: barraProgreso linear forwards;
        }

        @keyframes barraProgreso {
            from {
                transform: scaleX(0);
            }
            to {
                transform: scaleX(1);
            }
        }

        /* Encabezado Toast */
        .encabezado-toast {
            padding: 1.5rem 1.5rem 1rem;
            display: flex;
            align-items: flex-start;
            gap: 1rem;
        }

        .icono-toast {
            width: 48px;
            height: 48px;
            background: linear-gradient(135deg, var(--color-primario) 0%, var(--color-secundario) 100%);
            border-radius: var(--radio-borde-lg);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.5rem;
            color: white;
            flex-shrink: 0;
            animation: pulso 2s ease-in-out infinite;
        }

        @keyframes pulso {
            0%, 100% {
                transform: scale(1);
            }
            50% {
                transform: scale(1.05);
            }
        }

        .contenido-toast {
            flex: 1;
            min-width: 0;
        }

        .titulo-toast {
            font-size: 1.1rem;
            font-weight: 700;
            color: var(--texto-primario);
            margin-bottom: 0.5rem;
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .etiqueta-toast {
            font-size: 0.75rem;
            color: var(--texto-silenciado);
            background: var(--superficie);
            padding: 0.25rem 0.75rem;
            border-radius: 12px;
            border: 1px solid var(--color-borde);
            font-weight: 500;
        }

        .mensaje-toast {
            font-size: 0.95rem;
            color: var(--texto-secundario);
            line-height: 1.5;
            margin-bottom: 1rem;
        }

        .boton-cerrar {
            position: absolute;
            top: 1rem;
            right: 1rem;
            width: 32px;
            height: 32px;
            border: none;
            background: var(--superficie);
            border-radius: 50%;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            color: var(--texto-secundario);
            transition: var(--transicion-rapida);
            font-size: 1.2rem;
            border: 1px solid var(--color-borde);
        }

        .boton-cerrar:hover {
            background: var(--color-borde);
            color: var(--texto-primario);
            transform: scale(1.1);
        }

        /* Acciones Toast */
        .acciones-toast {
            padding: 0 1.5rem 1.5rem;
            display: flex;
            gap: 0.75rem;
            flex-wrap: wrap;
        }

        .btn-toast {
            flex: 1;
            min-width: 120px;
            padding: 0.75rem 1rem;
            border: none;
            border-radius: var(--radio-borde);
            font-family: inherit;
            font-size: 0.9rem;
            font-weight: 600;
            cursor: pointer;
            transition: var(--transicion);
            position: relative;
            overflow: hidden;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .btn-toast::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
            transition: left 0.5s ease;
        }

        .btn-toast:hover::before {
            left: 100%;
        }

        .btn-aceptar {
            background: linear-gradient(135deg, var(--color-exito) 0%, #06d6a0 100%);
            color: white;
            box-shadow: var(--sombra-sm);
        }

        .btn-aceptar:hover {
            transform: translateY(-1px);
            box-shadow: var(--sombra-md);
        }

        .btn-configuracion {
            background: linear-gradient(135deg, var(--color-primario) 0%, var(--color-secundario) 100%);
            color: white;
            box-shadow: var(--sombra-sm);
        }

        .btn-configuracion:hover {
            transform: translateY(-1px);
            box-shadow: var(--sombra-md);
        }

        .btn-rechazar {
            background: var(--superficie-elevada);
            color: var(--color-peligro);
            border: 2px solid var(--color-peligro);
        }

        .btn-rechazar:hover {
            background: var(--color-peligro);
            color: white;
            transform: translateY(-1px);
        }

        .btn-secundario {
            background: var(--superficie-elevada);
            color: var(--texto-secundario);
            border: 2px solid var(--color-borde);
        }

        .btn-secundario:hover {
            background: var(--color-borde);
            color: var(--texto-primario);
            transform: translateY(-1px);
        }

        /* Estados de Carga */
        .spinner-carga {
            display: inline-block;
            width: 16px;
            height: 16px;
            border: 2px solid rgba(255, 255, 255, 0.3);
            border-radius: 50%;
            border-top-color: currentColor;
            animation: girar 1s ease-in-out infinite;
        }

        @keyframes girar {
            to {
                transform: rotate(360deg);
            }
        }

        /* Animación de Éxito */
        .marca-exito {
            width: 16px;
            height: 16px;
            border-radius: 50%;
            display: inline-block;
            stroke-width: 2;
            stroke: currentColor;
            stroke-miterlimit: 10;
            box-shadow: inset 0px 0px 0px currentColor;
            animation: llenar 0.4s ease-in-out 0.4s forwards, escalar 0.3s ease-in-out 0.9s both;
        }

        .circulo-marca {
            stroke-dasharray: 166;
            stroke-dashoffset: 166;
            stroke-width: 2;
            stroke-miterlimit: 10;
            stroke: currentColor;
            fill: none;
            animation: trazo 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
        }

        .check-marca {
            transform-origin: 50% 50%;
            stroke-dasharray: 48;
            stroke-dashoffset: 48;
            animation: trazo 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
        }

        @keyframes trazo {
            100% {
                stroke-dashoffset: 0;
            }
        }

        @keyframes escalar {
            0%, 100% {
                transform: none;
            }
            50% {
                transform: scale3d(1.1, 1.1, 1);
            }
        }

        @keyframes llenar {
            100% {
                box-shadow: inset 0px 0px 0px 30px currentColor;
            }
        }

        /* Diseño Responsivo */
        @media (max-width: 768px) {
            :root {
                --ancho-toast: 100%;
            }

            .contenedor-toast {
                left: 1rem !important;
                right: 1rem !important;
                max-width: calc(100vw - 2rem);
            }

            .contenedor-toast.superior-derecha,
            .contenedor-toast.superior-izquierda {
                top: 1rem;
            }

            .contenedor-toast.inferior-derecha,
            .contenedor-toast.inferior-izquierda {
                bottom: 1rem;
            }

            .encabezado-toast {
                padding: 1rem 1rem 0.75rem;
            }

            .acciones-toast {
                padding: 0 1rem 1rem;
                flex-direction: column;
            }

            .btn-toast {
                min-width: auto;
            }
        }

        @media (max-width: 480px) {
            body {
                padding: 0.5rem;
            }

            .contenedor-demo h1 {
                font-size: 2rem;
            }

            .controles-demo {
                flex-direction: column;
                align-items: center;
            }

            .contenedor-toast {
                left: 0.5rem !important;
                right: 0.5rem !important;
                max-width: calc(100vw - 1rem);
            }

            .encabezado-toast {
                padding: 0.75rem 0.75rem 0.5rem;
            }

            .acciones-toast {
                padding: 0 0.75rem 0.75rem;
            }
        }

        /* Variantes de Animación */
        .aparecer {
            animation: aparecerEfecto 0.5s cubic-bezier(0.4, 0, 0.2, 1);
        }

        @keyframes aparecerEfecto {
            from {
                opacity: 0;
                transform: translateY(20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .deslizar-arriba {
            animation: deslizarArriba 0.4s cubic-bezier(0.4, 0, 0.2, 1);
        }

        @keyframes deslizarArriba {
            from {
                transform: translateY(30px);
                opacity: 0;
            }
            to {
                transform: translateY(0);
                opacity: 1;
            }
        }

        /* Estilos de Enfoque */
        .btn-toast:focus,
        .boton-cerrar:focus {
            outline: 2px solid var(--color-primario);
            outline-offset: 2px;
        }

        /* Modo Alto Contraste */
        @media (prefers-contrast: high) {
            :root {
                --color-borde: #000000;
                --texto-secundario: #000000;
            }
        }

        /* Movimiento Reducido */
        @media (prefers-reduced-motion: reduce) {
            * {
                animation-duration: 0.01ms !important;
                animation-iteration-count: 1 !important;
                transition-duration: 0.01ms !important;
            }
        }

        /* Apilamiento de Toasts */
        .toast-cookies:not(:last-child) {
            margin-bottom: 1rem;
        }

        /* Efectos Hover */
        .toast-cookies:hover {
            transform: translateX(0) scale(1.02);
            box-shadow: var(--sombra-xl), 0 0 0 1px var(--color-primario);
        }

        .contenedor-toast.superior-izquierda .toast-cookies:hover,
        .contenedor-toast.inferior-izquierda .toast-cookies:hover {
            transform: translateX(0) scale(1.02);
        }
    </style>
</head>
<body>
    <div class="contenedor-demo">
        <h1>Diseño Toast en Esquina</h1>
        <p>Experimenta el elegante sistema de notificaciones toast en esquina con animaciones suaves y múltiples opciones de posicionamiento</p>
        
        <div class="controles-demo">
            <button class="btn-demo" onclick="mostrarToast('superior-derecha')">Superior Derecha</button>
            <button class="btn-demo" onclick="mostrarToast('superior-izquierda')">Superior Izquierda</button>
            <button class="btn-demo" onclick="mostrarToast('inferior-derecha')">Inferior Derecha</button>
            <button class="btn-demo" onclick="mostrarToast('inferior-izquierda')">Inferior Izquierda</button>
            <button class="btn-demo" onclick="mostrarToastConTemporizador()">Auto Cerrar</button>
        </div>
    </div>

    <!-- Contenedores Toast -->
    <div id="contenedorToast" class="contenedor-toast superior-derecha">
        <!-- Los toasts se insertarán dinámicamente aquí -->
    </div>

    <script>
        class BannerCookiesToastEsquina {
            constructor(opciones = {}) {
                this.opciones = {
                    posicion: 'superior-derecha',
                    mostrarAuto: true,
                    retrasoMostrar: 2000,
                    cerrarAuto: false,
                    retrasoCerrar: 10000,
                    claveAlmacenamiento: 'consentimiento_cookies_toast_esquina',
                    diasExpiracion: 365,
                    habilitarAnimaciones: true,
                    habilitarApilamiento: true,
                    maxApilamiento: 3,
                    alAceptar: null,
                    alRechazar: null,
                    alConfiguracion: null,
                    alCerrar: null
                };

                Object.assign(this.opciones, opciones);
                this.estado = {
                    visible: false,
                    descartado: false,
                    cargando: false,
                    idToast: null
                };

                this.inicializar();
            }

            inicializar() {
                this.crearContenedor();
                this.cargarConsentimientoGuardado();
                
                if (this.opciones.mostrarAuto && !this.tieneConsentimiento()) {
                    setTimeout(() => {
                        this.mostrar();
                    }, this.opciones.retrasoMostrar);
                }
            }

            crearContenedor() {
                let contenedor = document.getElementById('contenedorToast');
                if (!contenedor) {
                    contenedor = document.createElement('div');
                    contenedor.id = 'contenedorToast';
                    contenedor.className = `contenedor-toast ${this.opciones.posicion}`;
                    document.body.appendChild(contenedor);
                }
                this.contenedor = contenedor;
            }

            mostrar() {
                if (this.estado.visible) return;
                
                this.estado.visible = true;
                this.estado.idToast = this.generarId();
                
                const toast = this.crearToast();
                this.contenedor.appendChild(toast);
                
                // Activar animación
                requestAnimationFrame(() => {
                    toast.classList.add('mostrar');
                });
                
                // Auto cerrar si está habilitado
                if (this.opciones.cerrarAuto) {
                    this.iniciarBarraProgreso(toast);
                    setTimeout(() => {
                        this.ocultar();
                    }, this.opciones.retrasoCerrar);
                }
                
                // Gestionar apilamiento
                if (this.opciones.habilitarApilamiento) {
                    this.gestionarApilamiento();
                }
            }

            crearToast() {
                const toast = document.createElement('div');
                toast.className = 'toast-cookies';
                toast.id = this.estado.idToast;
                
                toast.innerHTML = `
                    <button class="boton-cerrar" onclick="instanciaToastCookies.ocultar()" aria-label="Cerrar notificación">×</button>
                    
                    <div class="encabezado-toast">
                        <div class="icono-toast">🍪</div>
                        <div class="contenido-toast">
                            <div class="titulo-toast">
                                Preferencias de Cookies
                                <span class="etiqueta-toast">Privacidad</span>
                            </div>
                            <div class="mensaje-toast">
                                Utilizamos cookies para mejorar tu experiencia de navegación, servir contenido personalizado y analizar nuestro tráfico. 
                                Al hacer clic en "Aceptar Todo", consientes nuestro uso de cookies.
                            </div>
                        </div>
                    </div>
                    
                    <div class="acciones-toast">
                        <button class="btn-toast btn-aceptar" onclick="instanciaToastCookies.aceptarTodo()">
                            Aceptar Todo
                        </button>
                        <button class="btn-toast btn-configuracion" onclick="instanciaToastCookies.mostrarConfiguracion()">
                            Configuración
                        </button>
                        <button class="btn-toast btn-rechazar" onclick="instanciaToastCookies.rechazar()">
                            Rechazar
                        </button>
                    </div>
                    
                    ${this.opciones.cerrarAuto ? '<div class="progreso-toast"></div>' : ''}
                `;
                
                return toast;
            }

            iniciarBarraProgreso(toast) {
                const barraProgreso = toast.querySelector('.progreso-toast');
                if (barraProgreso) {
                    barraProgreso.style.animationDuration = `${this.opciones.retrasoCerrar}ms`;
                    barraProgreso.classList.add('activo');
                }
            }

            ocultar() {
                if (!this.estado.visible) return;
                
                const toast = document.getElementById(this.estado.idToast);
                if (toast) {
                    toast.classList.add('ocultar');
                    
                    setTimeout(() => {
                        if (toast.parentNode) {
                            toast.parentNode.removeChild(toast);
                        }
                    }, 500);
                }
                
                this.estado.visible = false;
                this.estado.descartado = true;
                
                if (this.opciones.alCerrar) {
                    this.opciones.alCerrar();
                }
            }

            aceptarTodo() {
                this.guardarConsentimiento({
                    necesarias: true,
                    analiticas: true,
                    marketing: true,
                    funcionales: true
                });
                
                this.mostrarEstadoExito();
                
                setTimeout(() => {
                    this.ocultar();
                }, 1500);
                
                if (this.opciones.alAceptar) {
                    this.opciones.alAceptar({
                        necesarias: true,
                        analiticas: true,
                        marketing: true,
                        funcionales: true
                    });
                }
            }

            rechazar() {
                this.guardarConsentimiento({
                    necesarias: true,
                    analiticas: false,
                    marketing: false,
                    funcionales: false
                });
                
                this.ocultar();
                
                if (this.opciones.alRechazar) {
                    this.opciones.alRechazar({
                        necesarias: true,
                        analiticas: false,
                        marketing: false,
                        funcionales: false
                    });
                }
            }

            mostrarConfiguracion() {
                if (this.opciones.alConfiguracion) {
                    this.opciones.alConfiguracion();
                } else {
                    // Implementación por defecto del modal de configuración
                    alert('La configuración de cookies se abriría aquí. Implementa tu modal de configuración personalizado.');
                }
            }

            mostrarEstadoExito() {
                const toast = document.getElementById(this.estado.idToast);
                if (toast) {
                    const btnAceptar = toast.querySelector('.btn-aceptar');
                    if (btnAceptar) {
                        btnAceptar.innerHTML = `
                            <svg class="marca-exito" viewBox="0 0 52 52">
                                <circle class="circulo-marca" cx="26" cy="26" r="25" fill="none"/>
                                <path class="check-marca" fill="none" d="m14.1 27.2l7.1 7.2 16.7-16.8"/>
                            </svg>
                            ¡Guardado!
                        `;
                        btnAceptar.disabled = true;
                    }
                }
            }

            guardarConsentimiento(preferencias) {
                const datosConsentimiento = {
                    preferencias: preferencias,
                    timestamp: Date.now(),
                    version: '1.0.0'
                };
                
                localStorage.setItem(this.opciones.claveAlmacenamiento, JSON.stringify(datosConsentimiento));
            }

            cargarConsentimientoGuardado() {
                try {
                    const datos = localStorage.getItem(this.opciones.claveAlmacenamiento);
                    if (datos) {
                        const datosConsentimiento = JSON.parse(datos);
                        return datosConsentimiento.preferencias;
                    }
                } catch (error) {
                    console.warn('Error al cargar consentimiento de cookies:', error);
                }
                return null;
            }

            tieneConsentimiento() {
                const datos = localStorage.getItem(this.opciones.claveAlmacenamiento);
                if (!datos) return false;
                
                try {
                    const datosConsentimiento = JSON.parse(datos);
                    const ahora = Date.now();
                    const expiracion = datosConsentimiento.timestamp + (this.opciones.diasExpiracion * 24 * 60 * 60 * 1000);
                    
                    return ahora < expiracion;
                } catch (error) {
                    return false;
                }
            }

            gestionarApilamiento() {
                const toasts = this.contenedor.querySelectorAll('.toast-cookies');
                if (toasts.length > this.opciones.maxApilamiento) {
                    // Eliminar toasts más antiguos
                    for (let i = 0; i < toasts.length - this.opciones.maxApilamiento; i++) {
                        toasts[i].classList.add('ocultar');
                        setTimeout(() => {
                            if (toasts[i].parentNode) {
                                toasts[i].parentNode.removeChild(toasts[i]);
                            }
                        }, 500);
                    }
                }
            }

            generarId() {
                return 'toast-' + Math.random().toString(36).substr(2, 9);
            }

            // API Pública
            establecerPosicion(posicion) {
                this.opciones.posicion = posicion;
                this.contenedor.className = `contenedor-toast ${posicion}`;
            }

            obtenerPreferencias() {
                return this.cargarConsentimientoGuardado();
            }

            reiniciar() {
                localStorage.removeItem(this.opciones.claveAlmacenamiento);
                this.estado.descartado = false;
            }
        }

        // Funciones globales para demo
        let instanciaToastCookies;

        function mostrarToast(posicion = 'superior-derecha') {
            // Actualizar posición del contenedor
            const contenedor = document.getElementById('contenedorToast');
            contenedor.className = `contenedor-toast ${posicion}`;
            
            instanciaToastCookies = new BannerCookiesToastEsquina({
                posicion: posicion,
                mostrarAuto: false,
                alAceptar: (preferencias) => {
                    console.log('Cookies aceptadas:', preferencias);
                },
                alRechazar: (preferencias) => {
                    console.log('Cookies rechazadas:', preferencias);
                },
                alConfiguracion: () => {
                    console.log('Configuración solicitada');
                },
                alCerrar: () => {
                    console.log('Toast cerrado');
                }
            });
            
            instanciaToastCookies.mostrar();
        }

        function mostrarToastConTemporizador() {
            const contenedor = document.getElementById('contenedorToast');
            contenedor.className = 'contenedor-toast superior-derecha';
            
            instanciaToastCookies = new BannerCookiesToastEsquina({
                posicion: 'superior-derecha',
                mostrarAuto: false,
                cerrarAuto: true,
                retrasoCerrar: 8000,
                alAceptar: (preferencias) => {
                    console.log('Cookies aceptadas:', preferencias);
                },
                alRechazar: (preferencias) => {
                    console.log('Cookies rechazadas:', preferencias);
                }
            });
            
            instanciaToastCookies.mostrar();
        }

        // Auto-inicializar
        document.addEventListener('DOMContentLoaded', () => {
            instanciaToastCookies = new BannerCookiesToastEsquina();
        });
    </script>
</body>
</html>

Uso

Implementación Básica

// Inicialización simple
const toastCookies = new BannerCookiesToastEsquina();

// Con opciones personalizadas
const toastCookies = new BannerCookiesToastEsquina({
    posicion: 'inferior-derecha',
    mostrarAuto: true,
    cerrarAuto: true,
    retrasoCerrar: 15000,
    alAceptar: (preferencias) => {
        console.log('Usuario aceptó cookies:', preferencias);
        // Implementar lógica de seguimiento
    },
    alRechazar: (preferencias) => {
        console.log('Usuario rechazó cookies opcionales:', preferencias);
        // Deshabilitar seguimiento opcional
    }
});

Control Programático

// Mostrar toast manualmente
toastCookies.mostrar();

// Ocultar toast
toastCookies.ocultar();

// Cambiar posición
toastCookies.establecerPosicion('superior-izquierda');

// Obtener preferencias actuales
const preferencias = toastCookies.obtenerPreferencias();

// Reiniciar consentimiento
toastCookies.reiniciar();

Personalización de Temas

Propiedades CSS Personalizadas

:root {
    /* Colores Primarios */
    --color-primario: #3b82f6;
    --color-secundario: #6366f1;
    --color-exito: #10b981;
    
    /* Colores de Superficie */
    --fondo: #ffffff;
    --superficie: #f8fafc;
    --superficie-elevada: #ffffff;
    
    /* Tipografía */
    --texto-primario: #1f2937;
    --texto-secundario: #6b7280;
    
    /* Específico del Toast */
    --ancho-toast: 400px;
    --ancho-max-toast: 90vw;
    --radio-borde: 8px;
    --transicion: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

Tema Oscuro

[data-theme="oscuro"] {
    --fondo: #111827;
    --superficie: #1f2937;
    --superficie-elevada: #374151;
    --texto-primario: #f9fafb;
    --texto-secundario: #d1d5db;
    --color-borde: #374151;
}

Métodos API

Métodos Principales

MétodoDescripciónParámetros
mostrar()Mostrar la notificación toastNinguno
ocultar()Ocultar la notificación toastNinguno
aceptarTodo()Aceptar todas las categorías de cookiesNinguno
rechazar()Rechazar cookies opcionalesNinguno
mostrarConfiguracion()Abrir configuración de cookiesNinguno

Métodos de Configuración

MétodoDescripciónParámetros
establecerPosicion(posicion)Cambiar posición del toastposicion: String
obtenerPreferencias()Obtener preferencias actuales de cookiesNinguno
reiniciar()Reiniciar todos los datos de consentimientoNinguno
tieneConsentimiento()Verificar si existe consentimiento válidoNinguno

Opciones de Configuración

const opciones = {
    // Posicionamiento
    posicion: 'superior-derecha',      // Posición del toast: 'superior-derecha', 'superior-izquierda', 'inferior-derecha', 'inferior-izquierda'
    
    // Comportamiento
    mostrarAuto: true,                 // Mostrar automáticamente al cargar la página
    retrasoMostrar: 2000,             // Retraso antes de mostrar (ms)
    cerrarAuto: false,                // Auto-cerrar después del retraso
    retrasoCerrar: 10000,             // Retraso de auto-cerrar (ms)
    
    // Apilamiento
    habilitarApilamiento: true,       // Permitir múltiples toasts
    maxApilamiento: 3,                // Máximo de toasts apilados
    
    // Almacenamiento
    claveAlmacenamiento: 'consentimiento_cookies_toast_esquina',
    diasExpiracion: 365,              // Días antes de que expire el consentimiento
    
    // Visual
    habilitarAnimaciones: true,       // Habilitar animaciones
    
    // Callbacks
    alAceptar: (preferencias) => {},  // Callback cuando se aceptan cookies
    alRechazar: (preferencias) => {}, // Callback cuando se rechazan cookies
    alConfiguracion: () => {},        // Callback cuando se solicita configuración
    alCerrar: () => {}               // Callback cuando se cierra el toast
};

Soporte de Navegadores

  • Chrome: 60+
  • Firefox: 55+
  • Safari: 12+
  • Edge: 79+
  • Opera: 47+

Características Modernas Utilizadas

  • CSS Grid y Flexbox
  • Propiedades Personalizadas CSS (Variables)
  • JavaScript ES6+
  • Animaciones y Transiciones CSS
  • API LocalStorage

Accesibilidad

Características de Accesibilidad

  • Navegación por Teclado: Soporte completo de navegación Tab
  • Lectores de Pantalla: Etiquetas ARIA y estructura semántica
  • Alto Contraste: Soporte para modo de alto contraste
  • Movimiento Reducido: Respeta las preferencias de movimiento reducido
  • Gestión de Enfoque: Indicadores de enfoque claros

Atajos de Teclado

  • Tab: Navegar entre elementos
  • Enter/Espacio: Activar botones
  • Escape: Cerrar toast (cuando está enfocado)

Cumplimiento RGPD

Características de Cumplimiento

  • Consentimiento Granular: Control de cookies basado en categorías
  • Información Clara: Descripciones transparentes del uso de cookies
  • Retiro Fácil: Capacidad de cambiar preferencias en cualquier momento
  • Registro de Consentimiento: Almacenamiento de consentimiento con marca de tiempo
  • Cookies Esenciales: Distinción clara entre cookies necesarias y opcionales

Categorías de Cookies

  1. Necesarias: Siempre activas, esenciales para la funcionalidad del sitio web
  2. Analíticas: Métricas de rendimiento y análisis de uso
  3. Marketing: Personalización de publicidad y remarketing
  4. Funcionales: Características mejoradas y personalización

Rendimiento

Optimizaciones

  • Carga Perezosa: Inicializar solo cuando sea necesario
  • CSS Optimizado: Selectores y propiedades eficientes
  • JavaScript Minificado: Código optimizado listo para producción
  • Almacenamiento Local: Persistencia eficiente de preferencias

Métricas de Rendimiento

  • Tamaño del Bundle: ~8KB (CSS + JS minificado)
  • Tiempo de Inicialización: <30ms
  • Tiempo de Renderizado: <50ms
  • Uso de Memoria: <1MB

Características Avanzadas

Soporte de Múltiples Toasts

// Crear múltiples toasts con diferentes configuraciones
const toasts = [
    new BannerCookiesToastEsquina({ posicion: 'superior-derecha', cerrarAuto: true }),
    new BannerCookiesToastEsquina({ posicion: 'inferior-izquierda', cerrarAuto: false })
];

// Mostrar todos los toasts
toasts.forEach(toast => toast.mostrar());

Contenido Toast Personalizado

const toastPersonalizado = new BannerCookiesToastEsquina({
    contenidoPersonalizado: {
        titulo: 'Aviso de Privacidad',
        mensaje: 'Valoramos tu privacidad y usamos cookies para mejorar tu experiencia.',
        icono: '🔒',
        etiqueta: 'Requerido'
    }
});

Integración con Analytics

const toastAnalytics = new BannerCookiesToastEsquina({
    alAceptar: (preferencias) => {
        if (preferencias.analiticas) {
            // Inicializar Google Analytics
            gtag('config', 'GA_MEASUREMENT_ID');
        }
        if (preferencias.marketing) {
            // Inicializar píxeles de marketing
            fbq('init', 'FACEBOOK_PIXEL_ID');
        }
    },
    alRechazar: (preferencias) => {
        // Deshabilitar scripts de seguimiento
        if (!preferencias.analiticas) {
            window['ga-disable-GA_MEASUREMENT_ID'] = true;
        }
    }
});

Solución de Problemas

Problemas Comunes

Toast no aparece:

  • Verificar si ya existe consentimiento en localStorage
  • Verificar que mostrarAuto esté establecido en true
  • Asegurar que retrasoMostrar sea apropiado

Conflictos de estilo:

  • Usar especificidad CSS o declaraciones !important
  • Verificar valores de z-index conflictivos
  • Verificar que las propiedades personalizadas CSS sean compatibles

Problemas de rendimiento:

  • Reducir complejidad de animación para dispositivos antiguos
  • Implementar carga perezosa para aplicaciones grandes
  • Considerar usar contención CSS

Modo Debug

const toastDebug = new BannerCookiesToastEsquina({
    debug: true, // Habilitar logging en consola
    alAceptar: (preferencias) => {
        console.log('Debug: Cookies aceptadas', preferencias);
    }
});

Ejemplos

Integración E-commerce

const toastEcommerce = new BannerCookiesToastEsquina({
    posicion: 'inferior-derecha',
    alAceptar: (preferencias) => {
        if (preferencias.marketing) {
            // Habilitar recomendaciones de productos
            habilitarRecomendacionesProductos();
        }
        if (preferencias.analiticas) {
            // Rastrear comportamiento del usuario
            habilitarSeguimientoUsuario();
        }
    }
});

Sitio de Blog/Contenido

const toastBlog = new BannerCookiesToastEsquina({
    posicion: 'superior-izquierda',
    cerrarAuto: true,
    retrasoCerrar: 12000,
    alAceptar: (preferencias) => {
        if (preferencias.funcionales) {
            // Habilitar progreso de lectura
            habilitarProgresoLectura();
        }
    }
});

Aplicación SaaS

const toastSaas = new BannerCookiesToastEsquina({
    posicion: 'superior-derecha',
    habilitarApilamiento: false,
    alConfiguracion: () => {
        // Abrir modal de configuración personalizado
        abrirConfiguracionPrivacidad();
    },
    alAceptar: (preferencias) => {
        // Actualizar preferencias del usuario en backend
        actualizarConfiguracionPrivacidadUsuario(preferencias);
    }
});

Guía de Migración

Desde Otros Banners de Cookies

// Reemplazar banner de cookies existente
if (window.bannerCookiesExistente) {
    window.bannerCookiesExistente.destruir();
}

// Inicializar nuevo banner toast
const nuevoToast = new BannerCookiesToastEsquina({
    // Configuraciones de migración
    claveAlmacenamiento: 'consentimiento_cookies_legacy', // Usar clave de almacenamiento existente
    alAceptar: (preferencias) => {
        // Migrar lógica de seguimiento existente
        migrarSeguimientoLegacy(preferencias);
    }
});

Actualizaciones de Versión

// Verificar actualizaciones de versión
const versionActual = '1.0.0';
const versionAlmacenada = localStorage.getItem('version_banner_toast');

if (versionAlmacenada !== versionActual) {
    // Reiniciar consentimiento para nueva versión
    toastCookies.reiniciar();
    localStorage.setItem('version_banner_toast', versionActual);
}

Consideraciones de Seguridad

Protección de Datos

  • Todos los datos de consentimiento se almacenan localmente en el navegador del usuario
  • No se transmiten datos personales a servidores externos
  • Las marcas de tiempo de consentimiento se almacenan para propósitos de auditoría
  • Los datos pueden exportarse o eliminarse fácilmente

Prevención XSS

// Sanitizar entrada del usuario si se permite contenido personalizado
function sanitizarEntrada(entrada) {
    const div = document.createElement('div');
    div.textContent = entrada;
    return div.innerHTML;
}

Licencia

Licencia MIT - Libre para uso comercial y personal.


Nota: Este componente está diseñado para ser completamente autocontenido y no requiere dependencias externas. Simplemente incluye el HTML, CSS y JavaScript en tu proyecto para comenzar a usar el banner de cookies toast en esquina.

HTML

950

líneas

CSS

21

líneas

JavaScript

18

líneas


                <!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Banner de Cookies Toast en Esquina</title>
    <style>
        :root {
            --color-primario: #3b82f6;
            --color-primario-hover: #2563eb;
            --color-secundario: #6366f1;
            --color-exito: #10b981;
            --color-exito-hover: #059669;
            --color-advertencia: #f59e0b;
            --color-peligro: #ef4444;
            --fondo: #ffffff;
            --superficie: #f8fafc;
            --superficie-elevada: #ffffff;
            --texto-primario: #1f2937;
            --texto-secundario: #6b7280;
            --texto-silenciado: #9ca3af;
            --color-borde: #e5e7eb;
            --sombra-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
            --sombra-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
            --sombra-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
            --sombra-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
            --radio-borde: 8px;
            --radio-borde-lg: 12px;
            --radio-borde-xl: 16px;
            --transicion: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            --transicion-rapida: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
            --ancho-toast: 400px;
            --ancho-max-toast: 90vw;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            color: var(--texto-primario);
            line-height: 1.6;
            padding: 2rem;
            overflow-x: hidden;
            position: relative;
        }

        .contenedor-demo {
            max-width: 1200px;
            margin: 0 auto;
            text-align: center;
            color: white;
            position: relative;
            z-index: 1;
        }

        .contenedor-demo h1 {
            font-size: 3rem;
            font-weight: 800;
            margin-bottom: 1rem;
            background: linear-gradient(135deg, #ffffff 0%, #f0f0f0 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        .contenedor-demo p {
            font-size: 1.2rem;
            opacity: 0.9;
            margin-bottom: 2rem;
        }

        .controles-demo {
            display: flex;
            flex-wrap: wrap;
            gap: 1rem;
            justify-content: center;
            margin-bottom: 2rem;
        }

        .btn-demo {
            padding: 1rem 2rem;
            border: none;
            border-radius: var(--radio-borde-lg);
            background: rgba(255, 255, 255, 0.2);
            backdrop-filter: blur(10px);
            color: white;
            font-family: inherit;
            font-size: 1rem;
            font-weight: 600;
            cursor: pointer;
            transition: var(--transicion);
            border: 1px solid rgba(255, 255, 255, 0.3);
        }

        .btn-demo:hover {
            background: rgba(255, 255, 255, 0.3);
            transform: translateY(-2px);
            box-shadow: var(--sombra-lg);
        }

        /* Contenedor Toast */
        .contenedor-toast {
            position: fixed;
            z-index: 9999;
            pointer-events: none;
            display: flex;
            flex-direction: column;
            gap: 1rem;
            max-width: var(--ancho-max-toast);
        }

        .contenedor-toast.superior-derecha {
            top: 2rem;
            right: 2rem;
        }

        .contenedor-toast.superior-izquierda {
            top: 2rem;
            left: 2rem;
        }

        .contenedor-toast.inferior-derecha {
            bottom: 2rem;
            right: 2rem;
        }

        .contenedor-toast.inferior-izquierda {
            bottom: 2rem;
            left: 2rem;
        }

        /* Banner Toast de Cookies */
        .toast-cookies {
            width: var(--ancho-toast);
            max-width: 100%;
            background: var(--superficie-elevada);
            border-radius: var(--radio-borde-xl);
            box-shadow: var(--sombra-xl);
            border: 1px solid var(--color-borde);
            overflow: hidden;
            transform: translateX(120%);
            transition: all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
            pointer-events: auto;
            position: relative;
        }

        .toast-cookies.mostrar {
            transform: translateX(0);
        }

        .toast-cookies.ocultar {
            transform: translateX(120%);
            opacity: 0;
        }

        /* Toasts posicionados a la izquierda */
        .contenedor-toast.superior-izquierda .toast-cookies,
        .contenedor-toast.inferior-izquierda .toast-cookies {
            transform: translateX(-120%);
        }

        .contenedor-toast.superior-izquierda .toast-cookies.mostrar,
        .contenedor-toast.inferior-izquierda .toast-cookies.mostrar {
            transform: translateX(0);
        }

        .contenedor-toast.superior-izquierda .toast-cookies.ocultar,
        .contenedor-toast.inferior-izquierda .toast-cookies.ocultar {
            transform: translateX(-120%);
        }

        /* Barra de Progreso */
        .progreso-toast {
            position: absolute;
            bottom: 0;
            left: 0;
            height: 4px;
            background: linear-gradient(90deg, var(--color-primario) 0%, var(--color-secundario) 100%);
            border-radius: 0 0 var(--radio-borde-xl) var(--radio-borde-xl);
            transform-origin: left;
            transform: scaleX(0);
            transition: transform linear;
        }

        .progreso-toast.activo {
            animation: barraProgreso linear forwards;
        }

        @keyframes barraProgreso {
            from {
                transform: scaleX(0);
            }
            to {
                transform: scaleX(1);
            }
        }

        /* Encabezado Toast */
        .encabezado-toast {
            padding: 1.5rem 1.5rem 1rem;
            display: flex;
            align-items: flex-start;
            gap: 1rem;
        }

        .icono-toast {
            width: 48px;
            height: 48px;
            background: linear-gradient(135deg, var(--color-primario) 0%, var(--color-secundario) 100%);
            border-radius: var(--radio-borde-lg);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.5rem;
            color: white;
            flex-shrink: 0;
            animation: pulso 2s ease-in-out infinite;
        }

        @keyframes pulso {
            0%, 100% {
                transform: scale(1);
            }
            50% {
                transform: scale(1.05);
            }
        }

        .contenido-toast {
            flex: 1;
            min-width: 0;
        }

        .titulo-toast {
            font-size: 1.1rem;
            font-weight: 700;
            color: var(--texto-primario);
            margin-bottom: 0.5rem;
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .etiqueta-toast {
            font-size: 0.75rem;
            color: var(--texto-silenciado);
            background: var(--superficie);
            padding: 0.25rem 0.75rem;
            border-radius: 12px;
            border: 1px solid var(--color-borde);
            font-weight: 500;
        }

        .mensaje-toast {
            font-size: 0.95rem;
            color: var(--texto-secundario);
            line-height: 1.5;
            margin-bottom: 1rem;
        }

        .boton-cerrar {
            position: absolute;
            top: 1rem;
            right: 1rem;
            width: 32px;
            height: 32px;
            border: none;
            background: var(--superficie);
            border-radius: 50%;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            color: var(--texto-secundario);
            transition: var(--transicion-rapida);
            font-size: 1.2rem;
            border: 1px solid var(--color-borde);
        }

        .boton-cerrar:hover {
            background: var(--color-borde);
            color: var(--texto-primario);
            transform: scale(1.1);
        }

        /* Acciones Toast */
        .acciones-toast {
            padding: 0 1.5rem 1.5rem;
            display: flex;
            gap: 0.75rem;
            flex-wrap: wrap;
        }

        .btn-toast {
            flex: 1;
            min-width: 120px;
            padding: 0.75rem 1rem;
            border: none;
            border-radius: var(--radio-borde);
            font-family: inherit;
            font-size: 0.9rem;
            font-weight: 600;
            cursor: pointer;
            transition: var(--transicion);
            position: relative;
            overflow: hidden;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .btn-toast::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
            transition: left 0.5s ease;
        }

        .btn-toast:hover::before {
            left: 100%;
        }

        .btn-aceptar {
            background: linear-gradient(135deg, var(--color-exito) 0%, #06d6a0 100%);
            color: white;
            box-shadow: var(--sombra-sm);
        }

        .btn-aceptar:hover {
            transform: translateY(-1px);
            box-shadow: var(--sombra-md);
        }

        .btn-configuracion {
            background: linear-gradient(135deg, var(--color-primario) 0%, var(--color-secundario) 100%);
            color: white;
            box-shadow: var(--sombra-sm);
        }

        .btn-configuracion:hover {
            transform: translateY(-1px);
            box-shadow: var(--sombra-md);
        }

        .btn-rechazar {
            background: var(--superficie-elevada);
            color: var(--color-peligro);
            border: 2px solid var(--color-peligro);
        }

        .btn-rechazar:hover {
            background: var(--color-peligro);
            color: white;
            transform: translateY(-1px);
        }

        .btn-secundario {
            background: var(--superficie-elevada);
            color: var(--texto-secundario);
            border: 2px solid var(--color-borde);
        }

        .btn-secundario:hover {
            background: var(--color-borde);
            color: var(--texto-primario);
            transform: translateY(-1px);
        }

        /* Estados de Carga */
        .spinner-carga {
            display: inline-block;
            width: 16px;
            height: 16px;
            border: 2px solid rgba(255, 255, 255, 0.3);
            border-radius: 50%;
            border-top-color: currentColor;
            animation: girar 1s ease-in-out infinite;
        }

        @keyframes girar {
            to {
                transform: rotate(360deg);
            }
        }

        /* Animación de Éxito */
        .marca-exito {
            width: 16px;
            height: 16px;
            border-radius: 50%;
            display: inline-block;
            stroke-width: 2;
            stroke: currentColor;
            stroke-miterlimit: 10;
            box-shadow: inset 0px 0px 0px currentColor;
            animation: llenar 0.4s ease-in-out 0.4s forwards, escalar 0.3s ease-in-out 0.9s both;
        }

        .circulo-marca {
            stroke-dasharray: 166;
            stroke-dashoffset: 166;
            stroke-width: 2;
            stroke-miterlimit: 10;
            stroke: currentColor;
            fill: none;
            animation: trazo 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
        }

        .check-marca {
            transform-origin: 50% 50%;
            stroke-dasharray: 48;
            stroke-dashoffset: 48;
            animation: trazo 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
        }

        @keyframes trazo {
            100% {
                stroke-dashoffset: 0;
            }
        }

        @keyframes escalar {
            0%, 100% {
                transform: none;
            }
            50% {
                transform: scale3d(1.1, 1.1, 1);
            }
        }

        @keyframes llenar {
            100% {
                box-shadow: inset 0px 0px 0px 30px currentColor;
            }
        }

        /* Diseño Responsivo */
        @media (max-width: 768px) {
            :root {
                --ancho-toast: 100%;
            }

            .contenedor-toast {
                left: 1rem !important;
                right: 1rem !important;
                max-width: calc(100vw - 2rem);
            }

            .contenedor-toast.superior-derecha,
            .contenedor-toast.superior-izquierda {
                top: 1rem;
            }

            .contenedor-toast.inferior-derecha,
            .contenedor-toast.inferior-izquierda {
                bottom: 1rem;
            }

            .encabezado-toast {
                padding: 1rem 1rem 0.75rem;
            }

            .acciones-toast {
                padding: 0 1rem 1rem;
                flex-direction: column;
            }

            .btn-toast {
                min-width: auto;
            }
        }

        @media (max-width: 480px) {
            body {
                padding: 0.5rem;
            }

            .contenedor-demo h1 {
                font-size: 2rem;
            }

            .controles-demo {
                flex-direction: column;
                align-items: center;
            }

            .contenedor-toast {
                left: 0.5rem !important;
                right: 0.5rem !important;
                max-width: calc(100vw - 1rem);
            }

            .encabezado-toast {
                padding: 0.75rem 0.75rem 0.5rem;
            }

            .acciones-toast {
                padding: 0 0.75rem 0.75rem;
            }
        }

        /* Variantes de Animación */
        .aparecer {
            animation: aparecerEfecto 0.5s cubic-bezier(0.4, 0, 0.2, 1);
        }

        @keyframes aparecerEfecto {
            from {
                opacity: 0;
                transform: translateY(20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .deslizar-arriba {
            animation: deslizarArriba 0.4s cubic-bezier(0.4, 0, 0.2, 1);
        }

        @keyframes deslizarArriba {
            from {
                transform: translateY(30px);
                opacity: 0;
            }
            to {
                transform: translateY(0);
                opacity: 1;
            }
        }

        /* Estilos de Enfoque */
        .btn-toast:focus,
        .boton-cerrar:focus {
            outline: 2px solid var(--color-primario);
            outline-offset: 2px;
        }

        /* Modo Alto Contraste */
        @media (prefers-contrast: high) {
            :root {
                --color-borde: #000000;
                --texto-secundario: #000000;
            }
        }

        /* Movimiento Reducido */
        @media (prefers-reduced-motion: reduce) {
            * {
                animation-duration: 0.01ms !important;
                animation-iteration-count: 1 !important;
                transition-duration: 0.01ms !important;
            }
        }

        /* Apilamiento de Toasts */
        .toast-cookies:not(:last-child) {
            margin-bottom: 1rem;
        }

        /* Efectos Hover */
        .toast-cookies:hover {
            transform: translateX(0) scale(1.02);
            box-shadow: var(--sombra-xl), 0 0 0 1px var(--color-primario);
        }

        .contenedor-toast.superior-izquierda .toast-cookies:hover,
        .contenedor-toast.inferior-izquierda .toast-cookies:hover {
            transform: translateX(0) scale(1.02);
        }
    </style>
</head>
<body>
    <div class="contenedor-demo">
        <h1>Diseño Toast en Esquina</h1>
        <p>Experimenta el elegante sistema de notificaciones toast en esquina con animaciones suaves y múltiples opciones de posicionamiento</p>
        
        <div class="controles-demo">
            <button class="btn-demo" onclick="mostrarToast('superior-derecha')">Superior Derecha</button>
            <button class="btn-demo" onclick="mostrarToast('superior-izquierda')">Superior Izquierda</button>
            <button class="btn-demo" onclick="mostrarToast('inferior-derecha')">Inferior Derecha</button>
            <button class="btn-demo" onclick="mostrarToast('inferior-izquierda')">Inferior Izquierda</button>
            <button class="btn-demo" onclick="mostrarToastConTemporizador()">Auto Cerrar</button>
        </div>
    </div>

    <!-- Contenedores Toast -->
    <div id="contenedorToast" class="contenedor-toast superior-derecha">
        <!-- Los toasts se insertarán dinámicamente aquí -->
    </div>

    <script>
        class BannerCookiesToastEsquina {
            constructor(opciones = {}) {
                this.opciones = {
                    posicion: 'superior-derecha',
                    mostrarAuto: true,
                    retrasoMostrar: 2000,
                    cerrarAuto: false,
                    retrasoCerrar: 10000,
                    claveAlmacenamiento: 'consentimiento_cookies_toast_esquina',
                    diasExpiracion: 365,
                    habilitarAnimaciones: true,
                    habilitarApilamiento: true,
                    maxApilamiento: 3,
                    alAceptar: null,
                    alRechazar: null,
                    alConfiguracion: null,
                    alCerrar: null
                };

                Object.assign(this.opciones, opciones);
                this.estado = {
                    visible: false,
                    descartado: false,
                    cargando: false,
                    idToast: null
                };

                this.inicializar();
            }

            inicializar() {
                this.crearContenedor();
                this.cargarConsentimientoGuardado();
                
                if (this.opciones.mostrarAuto && !this.tieneConsentimiento()) {
                    setTimeout(() => {
                        this.mostrar();
                    }, this.opciones.retrasoMostrar);
                }
            }

            crearContenedor() {
                let contenedor = document.getElementById('contenedorToast');
                if (!contenedor) {
                    contenedor = document.createElement('div');
                    contenedor.id = 'contenedorToast';
                    contenedor.className = `contenedor-toast \${this.opciones.posicion}`;
                    document.body.appendChild(contenedor);
                }
                this.contenedor = contenedor;
            }

            mostrar() {
                if (this.estado.visible) return;
                
                this.estado.visible = true;
                this.estado.idToast = this.generarId();
                
                const toast = this.crearToast();
                this.contenedor.appendChild(toast);
                
                // Activar animación
                requestAnimationFrame(() => {
                    toast.classList.add('mostrar');
                });
                
                // Auto cerrar si está habilitado
                if (this.opciones.cerrarAuto) {
                    this.iniciarBarraProgreso(toast);
                    setTimeout(() => {
                        this.ocultar();
                    }, this.opciones.retrasoCerrar);
                }
                
                // Gestionar apilamiento
                if (this.opciones.habilitarApilamiento) {
                    this.gestionarApilamiento();
                }
            }

            crearToast() {
                const toast = document.createElement('div');
                toast.className = 'toast-cookies';
                toast.id = this.estado.idToast;
                
                toast.innerHTML = `
                    <button class="boton-cerrar" onclick="instanciaToastCookies.ocultar()" aria-label="Cerrar notificación">×</button>
                    
                    <div class="encabezado-toast">
                        <div class="icono-toast">🍪</div>
                        <div class="contenido-toast">
                            <div class="titulo-toast">
                                Preferencias de Cookies
                                <span class="etiqueta-toast">Privacidad</span>
                            </div>
                            <div class="mensaje-toast">
                                Utilizamos cookies para mejorar tu experiencia de navegación, servir contenido personalizado y analizar nuestro tráfico. 
                                Al hacer clic en "Aceptar Todo", consientes nuestro uso de cookies.
                            </div>
                        </div>
                    </div>
                    
                    <div class="acciones-toast">
                        <button class="btn-toast btn-aceptar" onclick="instanciaToastCookies.aceptarTodo()">
                            Aceptar Todo
                        </button>
                        <button class="btn-toast btn-configuracion" onclick="instanciaToastCookies.mostrarConfiguracion()">
                            Configuración
                        </button>
                        <button class="btn-toast btn-rechazar" onclick="instanciaToastCookies.rechazar()">
                            Rechazar
                        </button>
                    </div>
                    
                    \${this.opciones.cerrarAuto ? '<div class="progreso-toast"></div>' : ''}
                `;
                
                return toast;
            }

            iniciarBarraProgreso(toast) {
                const barraProgreso = toast.querySelector('.progreso-toast');
                if (barraProgreso) {
                    barraProgreso.style.animationDuration = `\${this.opciones.retrasoCerrar}ms`;
                    barraProgreso.classList.add('activo');
                }
            }

            ocultar() {
                if (!this.estado.visible) return;
                
                const toast = document.getElementById(this.estado.idToast);
                if (toast) {
                    toast.classList.add('ocultar');
                    
                    setTimeout(() => {
                        if (toast.parentNode) {
                            toast.parentNode.removeChild(toast);
                        }
                    }, 500);
                }
                
                this.estado.visible = false;
                this.estado.descartado = true;
                
                if (this.opciones.alCerrar) {
                    this.opciones.alCerrar();
                }
            }

            aceptarTodo() {
                this.guardarConsentimiento({
                    necesarias: true,
                    analiticas: true,
                    marketing: true,
                    funcionales: true
                });
                
                this.mostrarEstadoExito();
                
                setTimeout(() => {
                    this.ocultar();
                }, 1500);
                
                if (this.opciones.alAceptar) {
                    this.opciones.alAceptar({
                        necesarias: true,
                        analiticas: true,
                        marketing: true,
                        funcionales: true
                    });
                }
            }

            rechazar() {
                this.guardarConsentimiento({
                    necesarias: true,
                    analiticas: false,
                    marketing: false,
                    funcionales: false
                });
                
                this.ocultar();
                
                if (this.opciones.alRechazar) {
                    this.opciones.alRechazar({
                        necesarias: true,
                        analiticas: false,
                        marketing: false,
                        funcionales: false
                    });
                }
            }

            mostrarConfiguracion() {
                if (this.opciones.alConfiguracion) {
                    this.opciones.alConfiguracion();
                } else {
                    // Implementación por defecto del modal de configuración
                    alert('La configuración de cookies se abriría aquí. Implementa tu modal de configuración personalizado.');
                }
            }

            mostrarEstadoExito() {
                const toast = document.getElementById(this.estado.idToast);
                if (toast) {
                    const btnAceptar = toast.querySelector('.btn-aceptar');
                    if (btnAceptar) {
                        btnAceptar.innerHTML = `
                            <svg class="marca-exito" viewBox="0 0 52 52">
                                <circle class="circulo-marca" cx="26" cy="26" r="25" fill="none"/>
                                <path class="check-marca" fill="none" d="m14.1 27.2l7.1 7.2 16.7-16.8"/>
                            </svg>
                            ¡Guardado!
                        `;
                        btnAceptar.disabled = true;
                    }
                }
            }

            guardarConsentimiento(preferencias) {
                const datosConsentimiento = {
                    preferencias: preferencias,
                    timestamp: Date.now(),
                    version: '1.0.0'
                };
                
                localStorage.setItem(this.opciones.claveAlmacenamiento, JSON.stringify(datosConsentimiento));
            }

            cargarConsentimientoGuardado() {
                try {
                    const datos = localStorage.getItem(this.opciones.claveAlmacenamiento);
                    if (datos) {
                        const datosConsentimiento = JSON.parse(datos);
                        return datosConsentimiento.preferencias;
                    }
                } catch (error) {
                    console.warn('Error al cargar consentimiento de cookies:', error);
                }
                return null;
            }

            tieneConsentimiento() {
                const datos = localStorage.getItem(this.opciones.claveAlmacenamiento);
                if (!datos) return false;
                
                try {
                    const datosConsentimiento = JSON.parse(datos);
                    const ahora = Date.now();
                    const expiracion = datosConsentimiento.timestamp + (this.opciones.diasExpiracion * 24 * 60 * 60 * 1000);
                    
                    return ahora < expiracion;
                } catch (error) {
                    return false;
                }
            }

            gestionarApilamiento() {
                const toasts = this.contenedor.querySelectorAll('.toast-cookies');
                if (toasts.length > this.opciones.maxApilamiento) {
                    // Eliminar toasts más antiguos
                    for (let i = 0; i < toasts.length - this.opciones.maxApilamiento; i++) {
                        toasts[i].classList.add('ocultar');
                        setTimeout(() => {
                            if (toasts[i].parentNode) {
                                toasts[i].parentNode.removeChild(toasts[i]);
                            }
                        }, 500);
                    }
                }
            }

            generarId() {
                return 'toast-' + Math.random().toString(36).substr(2, 9);
            }

            // API Pública
            establecerPosicion(posicion) {
                this.opciones.posicion = posicion;
                this.contenedor.className = `contenedor-toast \${posicion}`;
            }

            obtenerPreferencias() {
                return this.cargarConsentimientoGuardado();
            }

            reiniciar() {
                localStorage.removeItem(this.opciones.claveAlmacenamiento);
                this.estado.descartado = false;
            }
        }

        // Funciones globales para demo
        let instanciaToastCookies;

        function mostrarToast(posicion = 'superior-derecha') {
            // Actualizar posición del contenedor
            const contenedor = document.getElementById('contenedorToast');
            contenedor.className = `contenedor-toast \${posicion}`;
            
            instanciaToastCookies = new BannerCookiesToastEsquina({
                posicion: posicion,
                mostrarAuto: false,
                alAceptar: (preferencias) => {
                    console.log('Cookies aceptadas:', preferencias);
                },
                alRechazar: (preferencias) => {
                    console.log('Cookies rechazadas:', preferencias);
                },
                alConfiguracion: () => {
                    console.log('Configuración solicitada');
                },
                alCerrar: () => {
                    console.log('Toast cerrado');
                }
            });
            
            instanciaToastCookies.mostrar();
        }

        function mostrarToastConTemporizador() {
            const contenedor = document.getElementById('contenedorToast');
            contenedor.className = 'contenedor-toast superior-derecha';
            
            instanciaToastCookies = new BannerCookiesToastEsquina({
                posicion: 'superior-derecha',
                mostrarAuto: false,
                cerrarAuto: true,
                retrasoCerrar: 8000,
                alAceptar: (preferencias) => {
                    console.log('Cookies aceptadas:', preferencias);
                },
                alRechazar: (preferencias) => {
                    console.log('Cookies rechazadas:', preferencias);
                }
            });
            
            instanciaToastCookies.mostrar();
        }

        // Auto-inicializar
        document.addEventListener('DOMContentLoaded', () => {
            instanciaToastCookies = new BannerCookiesToastEsquina();
        });
    </script>
</body>
</html>

              
950líneas
31046caracteres
HTMLIdioma

Fragmentos de Código Relacionados

Explora packs de plantillas

¿Necesitas bloques más grandes? Descubre landings y colecciones de componentes.

Abrir la biblioteca de plantillas →