Categoría · Interactivo Nivel de Dificultad · Intermedio Publicado el · 20 de enero de 2025

Componente FAQ con Pestañas

Un componente FAQ moderno con pestañas que organiza las preguntas por categorías con transiciones suaves

#faq #tabs #categories #questions #answers

Diseño Responsivo

Soporte para Modo Oscuro

No

líneas

3

Compatibilidad del Navegador

No

Vista Previa en Vivo

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

600px

Componente FAQ con Pestañas

Un componente FAQ moderno con pestañas que organiza las preguntas por categorías con transiciones suaves, estados activos y diseño responsivo.

Características

  • Organización por Categorías: Preguntas agrupadas por categorías relevantes
  • Transiciones Suaves: Cambio de pestañas animado y carga de contenido
  • Gestión de Estados Activos: Indicadores visuales claros para pestañas activas
  • Diseño Responsivo: Funciona perfectamente en todos los tamaños de dispositivo
  • Navegación por Teclado: Soporte completo de accesibilidad por teclado
  • Integración de Búsqueda: Búsqueda opcional dentro de la pestaña activa
  • Carga Perezosa: Contenido cargado solo cuando se activan las pestañas
  • Estilo Personalizable: Fácil de tematizar y personalizar
  • Soporte de Hash URL: Enlace directo a pestañas específicas
  • Indicadores de Insignia: Mostrar conteo de preguntas por categoría

Estructura HTML

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Componente FAQ con Pestañas</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="faq-tabs-container">
        <!-- Encabezado -->
        <div class="faq-header">
            <h1>Preguntas Frecuentes</h1>
            <p>Encuentra respuestas a preguntas comunes organizadas por categoría</p>
        </div>

        <!-- Navegación de Pestañas -->
        <div class="tab-navigation">
            <div class="tab-list" role="tablist">
                <button class="tab-button active" 
                        role="tab" 
                        aria-selected="true" 
                        aria-controls="general-panel" 
                        id="general-tab"
                        data-tab="general">
                    <span class="tab-icon">🏠</span>
                    <span class="tab-text">General</span>
                    <span class="tab-badge">6</span>
                </button>
                
                <button class="tab-button" 
                        role="tab" 
                        aria-selected="false" 
                        aria-controls="facturacion-panel" 
                        id="facturacion-tab"
                        data-tab="facturacion">
                    <span class="tab-icon">💳</span>
                    <span class="tab-text">Facturación</span>
                    <span class="tab-badge">4</span>
                </button>
                
                <button class="tab-button" 
                        role="tab" 
                        aria-selected="false" 
                        aria-controls="tecnico-panel" 
                        id="tecnico-tab"
                        data-tab="tecnico">
                    <span class="tab-icon">⚙️</span>
                    <span class="tab-text">Técnico</span>
                    <span class="tab-badge">5</span>
                </button>
                
                <button class="tab-button" 
                        role="tab" 
                        aria-selected="false" 
                        aria-controls="soporte-panel" 
                        id="soporte-tab"
                        data-tab="soporte">
                    <span class="tab-icon">🎧</span>
                    <span class="tab-text">Soporte</span>
                    <span class="tab-badge">3</span>
                </button>
            </div>
            
            <!-- Indicador de Pestaña -->
            <div class="tab-indicator"></div>
        </div>

        <!-- Contenido de Pestañas -->
        <div class="tab-content">
            <!-- Panel de Pestaña General -->
            <div class="tab-panel active" 
                 role="tabpanel" 
                 id="general-panel" 
                 aria-labelledby="general-tab">
                
                <div class="panel-search">
                    <input type="text" 
                           class="search-input" 
                           placeholder="Buscar preguntas generales..."
                           data-search-target="general">
                    <svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                        <circle cx="11" cy="11" r="8"></circle>
                        <path d="m21 21-4.35-4.35"></path>
                    </svg>
                </div>
                
                <div class="questions-list">
                    <div class="question-item" data-category="general">
                        <div class="question-header">
                            <h3>¿Cuál es su política de devoluciones?</h3>
                            <button class="toggle-btn" aria-expanded="false">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                                    <polyline points="6,9 12,15 18,9"></polyline>
                                </svg>
                            </button>
                        </div>
                        <div class="question-answer">
                            <p>Ofrecemos una política de devolución de 30 días para todos los artículos no utilizados en su empaque original. Simplemente contacte a nuestro equipo de servicio al cliente para iniciar una devolución, y le proporcionaremos una etiqueta de envío prepagada.</p>
                            <ul>
                                <li>Los artículos deben estar sin usar y en condición original</li>
                                <li>Se requiere el empaque original</li>
                                <li>Reembolso procesado dentro de 5-7 días hábiles</li>
                            </ul>
                        </div>
                    </div>
                    
                    <div class="question-item" data-category="general">
                        <div class="question-header">
                            <h3>¿Envían internacionalmente?</h3>
                            <button class="toggle-btn" aria-expanded="false">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                                    <polyline points="6,9 12,15 18,9"></polyline>
                                </svg>
                            </button>
                        </div>
                        <div class="question-answer">
                            <p>¡Sí! Enviamos a más de 50 países en todo el mundo. Las tarifas de envío internacional y los tiempos de entrega varían según el destino.</p>
                            <div class="info-grid">
                                <div class="info-item">
                                    <strong>Costos de Envío:</strong>
                                    <span>Calculados al finalizar la compra basados en peso y destino</span>
                                </div>
                                <div class="info-item">
                                    <strong>Tiempo de Entrega:</strong>
                                    <span>7-21 días hábiles dependiendo de la ubicación</span>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <div class="question-item" data-category="general">
                        <div class="question-header">
                            <h3>¿Cómo puedo rastrear mi pedido?</h3>
                            <button class="toggle-btn" aria-expanded="false">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                                    <polyline points="6,9 12,15 18,9"></polyline>
                                </svg>
                            </button>
                        </div>
                        <div class="question-answer">
                            <p>Una vez que su pedido sea enviado, recibirá un número de seguimiento por correo electrónico. Puede usar este número para rastrear su paquete en nuestro sitio web o en el sitio web del transportista.</p>
                            <div class="tracking-steps">
                                <div class="step">1. Revise su correo electrónico para información de seguimiento</div>
                                <div class="step">2. Visite nuestra página de seguimiento de pedidos</div>
                                <div class="step">3. Ingrese su número de seguimiento</div>
                                <div class="step">4. Vea actualizaciones en tiempo real</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Panel de Pestaña Facturación -->
            <div class="tab-panel" 
                 role="tabpanel" 
                 id="facturacion-panel" 
                 aria-labelledby="facturacion-tab">
                
                <div class="panel-search">
                    <input type="text" 
                           class="search-input" 
                           placeholder="Buscar preguntas de facturación..."
                           data-search-target="facturacion">
                    <svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                        <circle cx="11" cy="11" r="8"></circle>
                        <path d="m21 21-4.35-4.35"></path>
                    </svg>
                </div>
                
                <div class="questions-list">
                    <div class="question-item" data-category="facturacion">
                        <div class="question-header">
                            <h3>¿Qué métodos de pago aceptan?</h3>
                            <button class="toggle-btn" aria-expanded="false">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                                    <polyline points="6,9 12,15 18,9"></polyline>
                                </svg>
                            </button>
                        </div>
                        <div class="question-answer">
                            <p>Aceptamos todos los métodos de pago principales para hacer su experiencia de compra conveniente:</p>
                            <div class="payment-grid">
                                <div class="payment-category">
                                    <h4>Tarjetas de Crédito</h4>
                                    <p>Visa, MasterCard, American Express, Discover</p>
                                </div>
                                <div class="payment-category">
                                    <h4>Billeteras Digitales</h4>
                                    <p>PayPal, Apple Pay, Google Pay</p>
                                </div>
                                <div class="payment-category">
                                    <h4>Transferencia Bancaria</h4>
                                    <p>Transferencias ACH y wire aceptadas</p>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <div class="question-item" data-category="facturacion">
                        <div class="question-header">
                            <h3>¿Cuándo se me cobrará?</h3>
                            <button class="toggle-btn" aria-expanded="false">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                                    <polyline points="6,9 12,15 18,9"></polyline>
                                </svg>
                            </button>
                        </div>
                        <div class="question-answer">
                            <p>Su método de pago será cobrado cuando su pedido sea procesado y esté listo para enviar. Esto típicamente ocurre dentro de 24 horas de realizar su pedido.</p>
                            <div class="billing-timeline">
                                <div class="timeline-item">
                                    <div class="timeline-dot"></div>
                                    <div class="timeline-content">
                                        <strong>Pedido Realizado</strong>
                                        <span>Pago autorizado</span>
                                    </div>
                                </div>
                                <div class="timeline-item">
                                    <div class="timeline-dot"></div>
                                    <div class="timeline-content">
                                        <strong>Pedido Procesado</strong>
                                        <span>Pago cobrado</span>
                                    </div>
                                </div>
                                <div class="timeline-item">
                                    <div class="timeline-dot"></div>
                                    <div class="timeline-content">
                                        <strong>Pedido Enviado</strong>
                                        <span>Información de seguimiento enviada</span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Panel de Pestaña Técnico -->
            <div class="tab-panel" 
                 role="tabpanel" 
                 id="tecnico-panel" 
                 aria-labelledby="tecnico-tab">
                
                <div class="panel-search">
                    <input type="text" 
                           class="search-input" 
                           placeholder="Buscar preguntas técnicas..."
                           data-search-target="tecnico">
                    <svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                        <circle cx="11" cy="11" r="8"></circle>
                        <path d="m21 21-4.35-4.35"></path>
                    </svg>
                </div>
                
                <div class="questions-list">
                    <div class="question-item" data-category="tecnico">
                        <div class="question-header">
                            <h3>¿Cuáles son los requisitos del sistema?</h3>
                            <button class="toggle-btn" aria-expanded="false">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                                    <polyline points="6,9 12,15 18,9"></polyline>
                                </svg>
                            </button>
                        </div>
                        <div class="question-answer">
                            <p>Nuestra plataforma está diseñada para funcionar en dispositivos y navegadores modernos:</p>
                            <div class="requirements-grid">
                                <div class="req-section">
                                    <h4>Navegadores Compatibles</h4>
                                    <ul>
                                        <li>Chrome 80+</li>
                                        <li>Firefox 75+</li>
                                        <li>Safari 13+</li>
                                        <li>Edge 80+</li>
                                    </ul>
                                </div>
                                <div class="req-section">
                                    <h4>Dispositivos Móviles</h4>
                                    <ul>
                                        <li>iOS 13+</li>
                                        <li>Android 8+</li>
                                        <li>Diseño responsivo</li>
                                        <li>Optimizado para táctil</li>
                                    </ul>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <div class="question-item" data-category="tecnico">
                        <div class="question-header">
                            <h3>¿Cómo restablezco mi contraseña?</h3>
                            <button class="toggle-btn" aria-expanded="false">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                                    <polyline points="6,9 12,15 18,9"></polyline>
                                </svg>
                            </button>
                        </div>
                        <div class="question-answer">
                            <p>Siga estos pasos para restablecer su contraseña:</p>
                            <ol class="reset-steps">
                                <li>Vaya a la página de inicio de sesión y haga clic en "Olvidé mi contraseña"</li>
                                <li>Ingrese su dirección de correo electrónico</li>
                                <li>Revise su correo electrónico para un enlace de restablecimiento</li>
                                <li>Haga clic en el enlace y cree una nueva contraseña</li>
                                <li>Inicie sesión con su nueva contraseña</li>
                            </ol>
                            <div class="note">
                                <strong>Nota:</strong> Los enlaces de restablecimiento expiran después de 24 horas por razones de seguridad.
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Panel de Pestaña Soporte -->
            <div class="tab-panel" 
                 role="tabpanel" 
                 id="soporte-panel" 
                 aria-labelledby="soporte-tab">
                
                <div class="panel-search">
                    <input type="text" 
                           class="search-input" 
                           placeholder="Buscar preguntas de soporte..."
                           data-search-target="soporte">
                    <svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                        <circle cx="11" cy="11" r="8"></circle>
                        <path d="m21 21-4.35-4.35"></path>
                    </svg>
                </div>
                
                <div class="questions-list">
                    <div class="question-item" data-category="soporte">
                        <div class="question-header">
                            <h3>¿Cómo puedo contactar al soporte al cliente?</h3>
                            <button class="toggle-btn" aria-expanded="false">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                                    <polyline points="6,9 12,15 18,9"></polyline>
                                </svg>
                            </button>
                        </div>
                        <div class="question-answer">
                            <p>¡Estamos aquí para ayudar! Contacte a nuestro equipo de soporte a través de múltiples canales:</p>
                            <div class="contact-grid">
                                <div class="contact-method">
                                    <div class="contact-icon">📧</div>
                                    <h4>Soporte por Email</h4>
                                    <p>soporte@ejemplo.com</p>
                                    <span>Respuesta dentro de 24 horas</span>
                                </div>
                                <div class="contact-method">
                                    <div class="contact-icon">💬</div>
                                    <h4>Chat en Vivo</h4>
                                    <p>Disponible en nuestro sitio web</p>
                                    <span>Lun-Vie 9AM-6PM EST</span>
                                </div>
                                <div class="contact-method">
                                    <div class="contact-icon">📞</div>
                                    <h4>Soporte Telefónico</h4>
                                    <p>1-800-123-4567</p>
                                    <span>Lun-Vie 9AM-6PM EST</span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script src="script.js"></script>
</body>
</html>

Estilos CSS

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

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    min-height: 100vh;
    padding: 20px;
}

.faq-tabs-container {
    max-width: 1000px;
    margin: 0 auto;
    background: white;
    border-radius: 16px;
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
    overflow: hidden;
}

/* Estilos del Encabezado */
.faq-header {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    padding: 40px 30px;
    text-align: center;
}

.faq-header h1 {
    font-size: 2.5rem;
    font-weight: 700;
    margin-bottom: 8px;
}

.faq-header p {
    font-size: 1.1rem;
    opacity: 0.9;
}

/* Navegación de Pestañas */
.tab-navigation {
    position: relative;
    background: #f8fafc;
    border-bottom: 1px solid #e2e8f0;
}

.tab-list {
    display: flex;
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.tab-list::-webkit-scrollbar {
    display: none;
}

.tab-button {
    background: none;
    border: none;
    padding: 20px 24px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-weight: 500;
    color: #64748b;
    transition: all 0.3s ease;
    white-space: nowrap;
    position: relative;
    min-width: 140px;
    justify-content: center;
}

.tab-button:hover {
    color: #475569;
    background: rgba(102, 126, 234, 0.05);
}

.tab-button.active {
    color: #667eea;
    background: rgba(102, 126, 234, 0.1);
}

.tab-icon {
    font-size: 18px;
}

.tab-text {
    font-weight: 600;
}

.tab-badge {
    background: #667eea;
    color: white;
    font-size: 12px;
    padding: 2px 6px;
    border-radius: 10px;
    min-width: 20px;
    text-align: center;
}

.tab-button.active .tab-badge {
    background: #4f46e5;
}

/* Indicador de Pestaña */
.tab-indicator {
    position: absolute;
    bottom: 0;
    left: 0;
    height: 3px;
    background: linear-gradient(90deg, #667eea, #764ba2);
    transition: all 0.3s ease;
    border-radius: 3px 3px 0 0;
}

/* Contenido de Pestañas */
.tab-content {
    position: relative;
    min-height: 400px;
}

.tab-panel {
    display: none;
    padding: 30px;
    animation: fadeIn 0.3s ease;
}

.tab-panel.active {
    display: block;
}

/* Búsqueda del Panel */
.panel-search {
    position: relative;
    margin-bottom: 30px;
    max-width: 400px;
}

.search-input {
    width: 100%;
    padding: 12px 20px 12px 50px;
    border: 2px solid #e2e8f0;
    border-radius: 25px;
    font-size: 16px;
    background: #f8fafc;
    transition: all 0.3s ease;
}

.search-input:focus {
    outline: none;
    border-color: #667eea;
    background: white;
    box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}

.search-icon {
    position: absolute;
    left: 18px;
    top: 50%;
    transform: translateY(-50%);
    color: #94a3b8;
}

/* Lista de Preguntas */
.questions-list {
    space-y: 16px;
}

.question-item {
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 12px;
    margin-bottom: 16px;
    transition: all 0.3s ease;
    overflow: hidden;
}

.question-item:hover {
    border-color: #cbd5e1;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}

.question-header {
    padding: 20px 24px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    transition: background 0.3s ease;
}

.question-header:hover {
    background: #f1f5f9;
}

.question-header h3 {
    font-size: 1.1rem;
    font-weight: 600;
    color: #1e293b;
    margin: 0;
    flex: 1;
}

.toggle-btn {
    background: #667eea;
    border: none;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.3s ease;
    flex-shrink: 0;
}

.toggle-btn:hover {
    background: #5a67d8;
    transform: scale(1.05);
}

.toggle-btn svg {
    color: white;
    transition: transform 0.3s ease;
}

.question-item.active .toggle-btn svg {
    transform: rotate(180deg);
}

.question-answer {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease;
    background: white;
}

.question-item.active .question-answer {
    max-height: 1000px;
    padding: 0 24px 24px;
}

.question-answer p {
    color: #64748b;
    line-height: 1.6;
    margin-bottom: 16px;
}

.question-answer ul,
.question-answer ol {
    color: #64748b;
    line-height: 1.6;
    margin: 16px 0;
    padding-left: 20px;
}

.question-answer li {
    margin-bottom: 8px;
}

/* Cuadrícula de Información */
.info-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 16px;
    margin: 16px 0;
}

.info-item {
    background: #f8fafc;
    padding: 16px;
    border-radius: 8px;
    border-left: 4px solid #667eea;
}

.info-item strong {
    display: block;
    color: #374151;
    margin-bottom: 4px;
}

.info-item span {
    color: #64748b;
    font-size: 14px;
}

/* Pasos de Seguimiento */
.tracking-steps {
    margin: 20px 0;
}

.step {
    background: #f8fafc;
    padding: 12px 16px;
    border-radius: 8px;
    margin-bottom: 8px;
    border-left: 4px solid #667eea;
    color: #374151;
    font-weight: 500;
}

/* Cuadrícula de Pagos */
.payment-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
    margin: 20px 0;
}

.payment-category {
    background: #f8fafc;
    padding: 20px;
    border-radius: 12px;
    text-align: center;
    border: 2px solid #e2e8f0;
    transition: all 0.3s ease;
}

.payment-category:hover {
    border-color: #667eea;
    transform: translateY(-2px);
}

.payment-category h4 {
    color: #374151;
    margin-bottom: 8px;
    font-size: 16px;
}

.payment-category p {
    color: #64748b;
    font-size: 14px;
    margin: 0;
}

/* Cronología de Facturación */
.billing-timeline {
    margin: 20px 0;
}

.timeline-item {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 16px;
}

.timeline-dot {
    width: 12px;
    height: 12px;
    background: #667eea;
    border-radius: 50%;
    flex-shrink: 0;
}

.timeline-content strong {
    display: block;
    color: #374151;
    margin-bottom: 4px;
}

.timeline-content span {
    color: #64748b;
    font-size: 14px;
}

/* Cuadrícula de Requisitos */
.requirements-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 20px;
    margin: 20px 0;
}

.req-section {
    background: #f8fafc;
    padding: 20px;
    border-radius: 12px;
    border: 1px solid #e2e8f0;
}

.req-section h4 {
    color: #374151;
    margin-bottom: 12px;
    font-size: 16px;
}

.req-section ul {
    margin: 0;
    padding-left: 16px;
}

.req-section li {
    color: #64748b;
    margin-bottom: 6px;
}

/* Pasos de Restablecimiento */
.reset-steps {
    background: #f8fafc;
    padding: 20px;
    border-radius: 12px;
    margin: 16px 0;
}

.reset-steps li {
    margin-bottom: 12px;
    color: #374151;
    font-weight: 500;
}

.note {
    background: #fef3cd;
    border: 1px solid #fde68a;
    padding: 12px 16px;
    border-radius: 8px;
    margin: 16px 0;
    color: #92400e;
}

/* Cuadrícula de Contacto */
.contact-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 20px;
    margin: 20px 0;
}

.contact-method {
    background: #f8fafc;
    padding: 24px;
    border-radius: 12px;
    text-align: center;
    border: 2px solid #e2e8f0;
    transition: all 0.3s ease;
}

.contact-method:hover {
    border-color: #667eea;
    transform: translateY(-2px);
    box-shadow: 0 8px 25px rgba(102, 126, 234, 0.15);
}

.contact-icon {
    font-size: 32px;
    margin-bottom: 12px;
}

.contact-method h4 {
    color: #374151;
    margin-bottom: 8px;
    font-size: 16px;
}

.contact-method p {
    color: #667eea;
    font-weight: 600;
    margin-bottom: 4px;
}

.contact-method span {
    color: #64748b;
    font-size: 14px;
}

/* Animaciones */
@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes slideIn {
    from {
        opacity: 0;
        transform: translateX(-20px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

.question-item {
    animation: slideIn 0.3s ease;
}

/* Diseño Responsivo */
@media (max-width: 768px) {
    body {
        padding: 10px;
    }
    
    .faq-tabs-container {
        border-radius: 12px;
    }
    
    .faq-header {
        padding: 30px 20px;
    }
    
    .faq-header h1 {
        font-size: 2rem;
    }
    
    .tab-button {
        padding: 16px 20px;
        min-width: 120px;
    }
    
    .tab-panel {
        padding: 20px;
    }
    
    .question-header {
        padding: 16px 20px;
    }
    
    .question-item.active .question-answer {
        padding: 0 20px 20px;
    }
    
    .info-grid,
    .payment-grid,
    .requirements-grid,
    .contact-grid {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 480px) {
    .faq-header h1 {
        font-size: 1.75rem;
    }
    
    .tab-button {
        padding: 12px 16px;
        min-width: 100px;
        font-size: 13px;
    }
    
    .tab-icon {
        font-size: 16px;
    }
    
    .question-header h3 {
        font-size: 1rem;
    }
}

Funcionalidad JavaScript

class TabsFAQ {
    constructor() {
        this.tabButtons = document.querySelectorAll('.tab-button');
        this.tabPanels = document.querySelectorAll('.tab-panel');
        this.tabIndicator = document.querySelector('.tab-indicator');
        this.searchInputs = document.querySelectorAll('.search-input');
        this.questionItems = document.querySelectorAll('.question-item');
        
        this.init();
    }
    
    init() {
        this.bindEvents();
        this.setupKeyboardNavigation();
        this.updateIndicator();
        this.handleURLHash();
    }
    
    bindEvents() {
        // Cambio de pestañas
        this.tabButtons.forEach(button => {
            button.addEventListener('click', (e) => {
                this.switchTab(e.target.closest('.tab-button'));
            });
        });
        
        // Alternar preguntas
        document.addEventListener('click', (e) => {
            const questionHeader = e.target.closest('.question-header');
            if (questionHeader) {
                this.toggleQuestion(questionHeader.parentElement);
            }
        });
        
        // Funcionalidad de búsqueda
        this.searchInputs.forEach(input => {
            input.addEventListener('input', (e) => {
                this.filterQuestions(e.target.value, e.target.dataset.searchTarget);
            });
        });
        
        // Manejar navegador atrás/adelante
        window.addEventListener('hashchange', () => {
            this.handleURLHash();
        });
    }
    
    setupKeyboardNavigation() {
        // Navegación de pestañas
        document.addEventListener('keydown', (e) => {
            if (e.target.classList.contains('tab-button')) {
                this.handleTabKeyboard(e);
            } else if (e.target.closest('.question-header')) {
                this.handleQuestionKeyboard(e);
            }
        });
    }
    
    handleTabKeyboard(e) {
        const currentTab = e.target;
        const tabList = Array.from(this.tabButtons);
        const currentIndex = tabList.indexOf(currentTab);
        
        switch (e.key) {
            case 'ArrowLeft':
                e.preventDefault();
                const prevIndex = currentIndex === 0 ? tabList.length - 1 : currentIndex - 1;
                this.switchTab(tabList[prevIndex]);
                tabList[prevIndex].focus();
                break;
            case 'ArrowRight':
                e.preventDefault();
                const nextIndex = (currentIndex + 1) % tabList.length;
                this.switchTab(tabList[nextIndex]);
                tabList[nextIndex].focus();
                break;
            case 'Home':
                e.preventDefault();
                this.switchTab(tabList[0]);
                tabList[0].focus();
                break;
            case 'End':
                e.preventDefault();
                this.switchTab(tabList[tabList.length - 1]);
                tabList[tabList.length - 1].focus();
                break;
        }
    }
    
    handleQuestionKeyboard(e) {
        if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            const questionItem = e.target.closest('.question-item');
            this.toggleQuestion(questionItem);
        }
    }
    
    switchTab(targetButton) {
        const targetTab = targetButton.dataset.tab;
        
        // Actualizar estados de botones
        this.tabButtons.forEach(button => {
            button.classList.remove('active');
            button.setAttribute('aria-selected', 'false');
        });
        
        targetButton.classList.add('active');
        targetButton.setAttribute('aria-selected', 'true');
        
        // Actualizar estados de paneles
        this.tabPanels.forEach(panel => {
            panel.classList.remove('active');
        });
        
        const targetPanel = document.getElementById(`${targetTab}-panel`);
        if (targetPanel) {
            targetPanel.classList.add('active');
        }
        
        // Actualizar posición del indicador
        this.updateIndicator();
        
        // Actualizar hash URL
        history.replaceState(null, null, `#${targetTab}`);
        
        // Limpiar búsqueda en nueva pestaña
        const searchInput = targetPanel.querySelector('.search-input');
        if (searchInput) {
            searchInput.value = '';
            this.filterQuestions('', targetTab);
        }
    }
    
    updateIndicator() {
        const activeButton = document.querySelector('.tab-button.active');
        if (activeButton && this.tabIndicator) {
            const buttonRect = activeButton.getBoundingClientRect();
            const containerRect = activeButton.parentElement.getBoundingClientRect();
            
            this.tabIndicator.style.left = `${activeButton.offsetLeft}px`;
            this.tabIndicator.style.width = `${activeButton.offsetWidth}px`;
        }
    }
    
    toggleQuestion(questionItem) {
        const isActive = questionItem.classList.contains('active');
        const toggleBtn = questionItem.querySelector('.toggle-btn');
        const answer = questionItem.querySelector('.question-answer');
        
        if (isActive) {
            questionItem.classList.remove('active');
            toggleBtn.setAttribute('aria-expanded', 'false');
            answer.style.maxHeight = '0';
        } else {
            questionItem.classList.add('active');
            toggleBtn.setAttribute('aria-expanded', 'true');
            answer.style.maxHeight = answer.scrollHeight + 'px';
            
            // Desplazamiento suave a la pregunta
            setTimeout(() => {
                questionItem.scrollIntoView({
                    behavior: 'smooth',
                    block: 'nearest'
                });
            }, 150);
        }
    }
    
    filterQuestions(searchTerm, category) {
        const term = searchTerm.toLowerCase().trim();
        const categoryQuestions = document.querySelectorAll(`[data-category="${category}"]`);
        let visibleCount = 0;
        
        categoryQuestions.forEach(item => {
            const question = item.querySelector('h3').textContent.toLowerCase();
            const answer = item.querySelector('.question-answer').textContent.toLowerCase();
            
            const matches = question.includes(term) || answer.includes(term);
            
            if (matches || term === '') {
                item.style.display = 'block';
                visibleCount++;
                
                // Resaltar términos de búsqueda
                if (term !== '') {
                    this.highlightSearchTerm(item, term);
                } else {
                    this.removeHighlights(item);
                }
            } else {
                item.style.display = 'none';
                // Cerrar elementos ocultos
                if (item.classList.contains('active')) {
                    this.toggleQuestion(item);
                }
            }
        });
        
        // Mostrar mensaje sin resultados si es necesario
        const activePanel = document.querySelector('.tab-panel.active');
        const questionsList = activePanel.querySelector('.questions-list');
        
        let noResultsMsg = activePanel.querySelector('.no-results-message');
        if (visibleCount === 0 && term !== '') {
            if (!noResultsMsg) {
                noResultsMsg = document.createElement('div');
                noResultsMsg.className = 'no-results-message';
                noResultsMsg.innerHTML = `
                    <div style="text-align: center; padding: 40px; color: #64748b;">
                        <div style="font-size: 48px; margin-bottom: 16px; opacity: 0.5;">🔍</div>
                        <h3 style="margin-bottom: 8px; color: #374151;">No se encontraron preguntas</h3>
                        <p>Intente ajustar sus términos de búsqueda o navegue todas las preguntas arriba.</p>
                    </div>
                `;
                questionsList.appendChild(noResultsMsg);
            }
            noResultsMsg.style.display = 'block';
        } else if (noResultsMsg) {
            noResultsMsg.style.display = 'none';
        }
    }
    
    highlightSearchTerm(item, term) {
        const question = item.querySelector('h3');
        const originalText = question.textContent;
        const regex = new RegExp(`(${term})`, 'gi');
        const highlightedText = originalText.replace(regex, '<mark style="background: #fef08a; padding: 2px 4px; border-radius: 3px;">$1</mark>');
        
        if (originalText !== highlightedText) {
            question.innerHTML = highlightedText;
        }
    }
    
    removeHighlights(item) {
        const question = item.querySelector('h3');
        const marks = question.querySelectorAll('mark');
        marks.forEach(mark => {
            mark.outerHTML = mark.textContent;
        });
    }
    
    handleURLHash() {
        const hash = window.location.hash.substring(1);
        if (hash) {
            const targetButton = document.querySelector(`[data-tab="${hash}"]`);
            if (targetButton) {
                this.switchTab(targetButton);
            }
        }
    }
}

// Inicializar las pestañas FAQ cuando el DOM esté cargado
document.addEventListener('DOMContentLoaded', () => {
    new TabsFAQ();
    
    // Agregar desplazamiento suave
    document.documentElement.style.scrollBehavior = 'smooth';
    
    // Agregar animación de carga
    const questionItems = document.querySelectorAll('.question-item');
    questionItems.forEach((item, index) => {
        item.style.opacity = '0';
        item.style.transform = 'translateY(20px)';
        
        setTimeout(() => {
            item.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
            item.style.opacity = '1';
            item.style.transform = 'translateY(0)';
        }, index * 100);
    });
    
    // Manejar redimensionamiento de ventana para indicador
    window.addEventListener('resize', () => {
        const faq = new TabsFAQ();
        faq.updateIndicator();
    });
});

Ejemplos de Uso

Implementación Básica

// Inicializar las pestañas FAQ
const faq = new TabsFAQ();

// Cambiar programáticamente a una pestaña específica
faq.switchTab(document.querySelector('[data-tab="facturacion"]'));

// Buscar programáticamente dentro de una categoría
faq.filterQuestions('pago', 'facturacion');

Configuración Personalizada

// Opciones de configuración extendidas
const faq = new TabsFAQ({
    autoCollapse: true,        // Auto-colapsar otras preguntas al abrir una
    searchHighlight: true,     // Habilitar resaltado de términos de búsqueda
    urlHash: true,            // Habilitar navegación por hash URL
    animationDuration: 300,   // Duración de animación en ms
    scrollOffset: 100         // Desplazamiento al abrir preguntas
});

Integración con Analytics

// Rastrear cambios de pestañas e interacciones de preguntas
class AnalyticsTabsFAQ extends TabsFAQ {
    switchTab(targetButton) {
        super.switchTab(targetButton);
        
        // Rastrear cambio de pestaña
        gtag('event', 'faq_tab_switch', {
            'tab_name': targetButton.dataset.tab
        });
    }
    
    toggleQuestion(questionItem) {
        super.toggleQuestion(questionItem);
        
        const question = questionItem.querySelector('h3').textContent;
        const isOpening = questionItem.classList.contains('active');
        
        // Rastrear interacción de pregunta
        gtag('event', 'faq_question_toggle', {
            'question': question,
            'action': isOpening ? 'open' : 'close'
        });
    }
}

Compatibilidad del Navegador

  • Chrome 60+
  • Firefox 55+
  • Safari 12+
  • Edge 79+

Características Incluidas

✅ Organización por pestañas basada en categorías
✅ Transiciones suaves de pestañas con indicador
✅ Búsqueda individual por pestaña
✅ Soporte de navegación por teclado
✅ Navegación por hash URL
✅ Insignias de preguntas con conteos
✅ Diseño responsivo
✅ Atributos de accesibilidad ARIA
✅ Opciones de estilo personalizado
✅ Sin dependencias externas
✅ Estructura amigable para SEO
✅ Interfaz táctil amigable
✅ Animaciones de carga
✅ Resaltado de términos de búsqueda

Opciones de Personalización

  • Colores: Modificar propiedades personalizadas CSS para tematización
  • Velocidad de Animación: Ajustar duraciones de transición
  • Diseño: Personalizar espaciado y tipografía
  • Iconos: Reemplazar iconos de pestañas con personalizados
  • Búsqueda: Configurar comportamiento de búsqueda y resaltado
  • Pestañas: Agregar o quitar categorías según sea necesario

Este componente FAQ con pestañas proporciona una forma organizada y profesional de mostrar preguntas frecuentes con interacciones suaves y soporte completo de accesibilidad.

HTML

1

líneas

CSS

1

líneas

JavaScript

1

líneas


                <!-- HTML content will be added here -->

              
1líneas
41caracteres
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 →