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

Componentes de Tarjetas Animadas

Una colección de componentes de tarjetas modernas y animadas con efectos hover, transiciones e interacciones para aplicaciones web

#cards #animations #hover #transitions #components #ui

Diseño Responsivo

Soporte para Modo Oscuro

No

líneas

266

Compatibilidad del Navegador

No

Vista Previa en Vivo

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

500px

Componentes de Tarjetas Animadas

Una colección completa de componentes de tarjetas modernas y animadas con efectos hover suaves, transiciones elegantes y elementos interactivos perfectos para mostrar contenido, productos o servicios.

Características

  • Múltiples Estilos de Tarjetas: Tarjetas de productos, perfiles, blogs y características
  • Animaciones Suaves: Transiciones y efectos hover con CSS
  • Elementos Interactivos: Botones, insignias y áreas clicables
  • Diseño Responsivo: Se adapta a todos los tamaños de pantalla
  • Personalizable: Fácil modificación de colores, tamaños y animaciones
  • Accesibilidad: Navegación por teclado y soporte para lectores de pantalla
  • Optimizado para Rendimiento: Animaciones aceleradas por hardware
  • Diseño Moderno: Estética limpia y contemporánea

Demostración

<div class="cards-container">
  <!-- Tarjeta de Producto -->
  <div class="card product-card">
    <div class="card-image">
      <img src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=400" alt="Producto">
      <div class="card-badge">Nuevo</div>
      <div class="card-overlay">
        <button class="quick-view-btn">Vista Rápida</button>
      </div>
    </div>
    <div class="card-content">
      <div class="card-category">Zapatillas</div>
      <h3 class="card-title">Nike Air Max 270</h3>
      <p class="card-description">Zapatillas cómodas y elegantes perfectas para el uso diario.</p>
      <div class="card-price">
        <span class="current-price">€119.99</span>
        <span class="original-price">€149.99</span>
      </div>
      <div class="card-actions">
        <button class="btn-primary">Añadir al Carrito</button>
        <button class="btn-secondary" aria-label="Añadir a favoritos">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
          </svg>
        </button>
      </div>
    </div>
  </div>

  <!-- Tarjeta de Perfil -->
  <div class="card profile-card">
    <div class="card-header">
      <div class="profile-image">
        <img src="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=150" alt="Perfil">
        <div class="status-indicator online"></div>
      </div>
      <div class="profile-info">
        <h3 class="profile-name">Sarah Johnson</h3>
        <p class="profile-role">Diseñadora UI/UX</p>
        <div class="profile-stats">
          <div class="stat">
            <span class="stat-number">127</span>
            <span class="stat-label">Proyectos</span>
          </div>
          <div class="stat">
            <span class="stat-number">2.4k</span>
            <span class="stat-label">Seguidores</span>
          </div>
        </div>
      </div>
    </div>
    <div class="card-content">
      <p class="profile-bio">Diseñadora apasionada creando experiencias de usuario hermosas y funcionales. Me encanta trabajar con tecnologías modernas.</p>
      <div class="skills">
        <span class="skill-tag">Figma</span>
        <span class="skill-tag">Sketch</span>
        <span class="skill-tag">Prototipado</span>
      </div>
    </div>
    <div class="card-actions">
      <button class="btn-primary">Seguir</button>
      <button class="btn-secondary">Mensaje</button>
    </div>
  </div>

  <!-- Tarjeta de Blog -->
  <div class="card blog-card">
    <div class="card-image">
      <img src="https://images.unsplash.com/photo-1486312338219-ce68d2c6f44d?w=400" alt="Artículo del blog">
      <div class="read-time">5 min lectura</div>
    </div>
    <div class="card-content">
      <div class="card-meta">
        <span class="category">Tecnología</span>
        <span class="date">15 Ene, 2024</span>
      </div>
      <h3 class="card-title">El Futuro del Desarrollo Web</h3>
      <p class="card-excerpt">Explorando las últimas tendencias y tecnologías que están dando forma al futuro del desarrollo web...</p>
      <div class="author">
        <img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=40" alt="Autor" class="author-avatar">
        <div class="author-info">
          <span class="author-name">Juan Pérez</span>
          <span class="author-title">Desarrollador Senior</span>
        </div>
      </div>
    </div>
    <div class="card-footer">
      <div class="engagement">
        <button class="engagement-btn">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
          </svg>
          <span>24</span>
        </button>
        <button class="engagement-btn">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M21 6h-2l-1-2H6L5 6H3c-.55 0-1 .45-1 1s.45 1 1 1h1l1.5 9c.08.46.46.8.93.8h9.14c.47 0 .85-.34.93-.8L17 8h1c.55 0 1-.45 1-1s-.45-1-1-1z"/>
          </svg>
          <span>8</span>
        </button>
      </div>
      <button class="read-more-btn">Leer Más</button>
    </div>
  </div>

  <!-- Tarjeta de Características -->
  <div class="card feature-card">
    <div class="feature-icon">
      <svg viewBox="0 0 24 24" fill="currentColor">
        <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
      </svg>
    </div>
    <div class="card-content">
      <h3 class="card-title">Características Premium</h3>
      <p class="card-description">Desbloquea funcionalidades avanzadas con nuestro plan premium incluyendo soporte prioritario y características exclusivas.</p>
      <ul class="feature-list">
        <li>Análisis Avanzados</li>
        <li>Soporte Prioritario</li>
        <li>Integraciones Personalizadas</li>
        <li>Colaboración en Equipo</li>
      </ul>
    </div>
    <div class="card-actions">
      <button class="btn-primary">Actualizar Ahora</button>
      <a href="#" class="learn-more">Saber Más</a>
    </div>
  </div>
</div>
.cards-container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 2rem;
  padding: 2rem;
  max-width: 1400px;
  margin: 0 auto;
}

/* Estilos Base de Tarjetas */
.card {
  background: white;
  border-radius: 16px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
  overflow: hidden;
  transition: all 0.3s ease;
  position: relative;
  cursor: pointer;
}

.card:hover {
  transform: translateY(-8px);
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}

.card-content {
  padding: 1.5rem;
}

.card-title {
  font-size: 1.25rem;
  font-weight: 600;
  color: #1a1a1a;
  margin: 0 0 0.5rem 0;
  line-height: 1.3;
}

.card-description,
.card-excerpt {
  color: #6b7280;
  line-height: 1.6;
  margin: 0 0 1rem 0;
}

/* Tarjeta de Producto */
.product-card {
  max-width: 350px;
}

.product-card .card-image {
  position: relative;
  overflow: hidden;
  height: 250px;
}

.product-card .card-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.3s ease;
}

.product-card:hover .card-image img {
  transform: scale(1.1);
}

.card-badge {
  position: absolute;
  top: 1rem;
  left: 1rem;
  background: #ef4444;
  color: white;
  padding: 0.25rem 0.75rem;
  border-radius: 20px;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.card-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.product-card:hover .card-overlay {
  opacity: 1;
}

.quick-view-btn {
  background: white;
  color: #1a1a1a;
  border: none;
  padding: 0.75rem 1.5rem;
  border-radius: 25px;
  font-weight: 600;
  cursor: pointer;
  transform: translateY(20px);
  transition: all 0.3s ease;
}

.product-card:hover .quick-view-btn {
  transform: translateY(0);
}

.card-category {
  color: #3b82f6;
  font-size: 0.875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 0.5rem;
}

.card-price {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 1rem 0;
}

.current-price {
  font-size: 1.5rem;
  font-weight: 700;
  color: #1a1a1a;
}

.original-price {
  font-size: 1rem;
  color: #9ca3af;
  text-decoration: line-through;
}

.card-actions {
  display: flex;
  gap: 0.75rem;
  align-items: center;
}

.btn-primary {
  flex: 1;
  background: linear-gradient(135deg, #3b82f6, #1d4ed8);
  color: white;
  border: none;
  padding: 0.75rem 1rem;
  border-radius: 8px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
}

.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(59, 130, 246, 0.3);
}

.btn-secondary {
  width: 44px;
  height: 44px;
  background: #f3f4f6;
  border: none;
  border-radius: 8px;
  color: #6b7280;
  cursor: pointer;
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn-secondary:hover {
  background: #e5e7eb;
  color: #ef4444;
}

.btn-secondary svg {
  width: 20px;
  height: 20px;
}

/* Tarjeta de Perfil */
.profile-card {
  max-width: 350px;
  text-align: center;
}

.card-header {
  padding: 2rem 1.5rem 1rem;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  position: relative;
}

.profile-image {
  position: relative;
  display: inline-block;
  margin-bottom: 1rem;
}

.profile-image img {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  border: 4px solid white;
  object-fit: cover;
}

.status-indicator {
  position: absolute;
  bottom: 5px;
  right: 5px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid white;
}

.status-indicator.online {
  background: #10b981;
}

.profile-name {
  font-size: 1.5rem;
  font-weight: 700;
  margin: 0 0 0.25rem 0;
  color: white;
}

.profile-role {
  color: rgba(255, 255, 255, 0.8);
  margin: 0 0 1rem 0;
}

.profile-stats {
  display: flex;
  justify-content: center;
  gap: 2rem;
}

.stat {
  text-align: center;
}

.stat-number {
  display: block;
  font-size: 1.25rem;
  font-weight: 700;
  color: white;
}

.stat-label {
  font-size: 0.875rem;
  color: rgba(255, 255, 255, 0.7);
}

.profile-bio {
  text-align: left;
  margin-bottom: 1rem;
}

.skills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.skill-tag {
  background: #f3f4f6;
  color: #4b5563;
  padding: 0.25rem 0.75rem;
  border-radius: 20px;
  font-size: 0.75rem;
  font-weight: 500;
}

/* Tarjeta de Blog */
.blog-card {
  max-width: 400px;
}

.blog-card .card-image {
  position: relative;
  height: 200px;
  overflow: hidden;
}

.blog-card .card-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.3s ease;
}

.blog-card:hover .card-image img {
  transform: scale(1.05);
}

.read-time {
  position: absolute;
  top: 1rem;
  right: 1rem;
  background: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 0.25rem 0.75rem;
  border-radius: 20px;
  font-size: 0.75rem;
  font-weight: 500;
}

.card-meta {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.75rem;
  font-size: 0.875rem;
}

.category {
  color: #3b82f6;
  font-weight: 500;
}

.date {
  color: #9ca3af;
}

.author {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
}

.author-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
}

.author-info {
  display: flex;
  flex-direction: column;
}

.author-name {
  font-weight: 600;
  color: #1a1a1a;
  font-size: 0.875rem;
}

.author-title {
  color: #6b7280;
  font-size: 0.75rem;
}

.card-footer {
  padding: 1rem 1.5rem;
  border-top: 1px solid #f3f4f6;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.engagement {
  display: flex;
  gap: 1rem;
}

.engagement-btn {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  background: none;
  border: none;
  color: #6b7280;
  cursor: pointer;
  transition: color 0.3s ease;
  font-size: 0.875rem;
}

.engagement-btn:hover {
  color: #3b82f6;
}

.engagement-btn svg {
  width: 16px;
  height: 16px;
}

.read-more-btn {
  background: none;
  border: none;
  color: #3b82f6;
  font-weight: 600;
  cursor: pointer;
  transition: color 0.3s ease;
}

.read-more-btn:hover {
  color: #1d4ed8;
}

/* Tarjeta de Características */
.feature-card {
  max-width: 350px;
  text-align: center;
  border: 2px solid #f3f4f6;
  transition: all 0.3s ease;
}

.feature-card:hover {
  border-color: #3b82f6;
  transform: translateY(-8px);
}

.feature-icon {
  width: 80px;
  height: 80px;
  background: linear-gradient(135deg, #3b82f6, #1d4ed8);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 2rem auto 1.5rem;
  transition: transform 0.3s ease;
}

.feature-card:hover .feature-icon {
  transform: scale(1.1);
}

.feature-icon svg {
  width: 40px;
  height: 40px;
  color: white;
}

.feature-list {
  list-style: none;
  padding: 0;
  margin: 1.5rem 0;
  text-align: left;
}

.feature-list li {
  padding: 0.5rem 0;
  color: #4b5563;
  position: relative;
  padding-left: 1.5rem;
}

.feature-list li::before {
  content: '✓';
  position: absolute;
  left: 0;
  color: #10b981;
  font-weight: bold;
}

.learn-more {
  color: #6b7280;
  text-decoration: none;
  font-weight: 500;
  transition: color 0.3s ease;
}

.learn-more:hover {
  color: #3b82f6;
}

/* Tema Oscuro */
.dark .card {
  background: #1f2937;
  border: 1px solid #374151;
}

.dark .card-title {
  color: white;
}

.dark .card-description,
.dark .card-excerpt {
  color: #d1d5db;
}

.dark .btn-secondary {
  background: #374151;
  color: #d1d5db;
}

.dark .btn-secondary:hover {
  background: #4b5563;
}

.dark .skill-tag {
  background: #374151;
  color: #d1d5db;
}

.dark .card-footer {
  border-top-color: #374151;
}

.dark .feature-card {
  border-color: #374151;
}

.dark .feature-card:hover {
  border-color: #3b82f6;
}

/* Diseño Responsivo */
@media (max-width: 768px) {
  .cards-container {
    grid-template-columns: 1fr;
    padding: 1rem;
    gap: 1.5rem;
  }
  
  .card {
    max-width: none;
  }
  
  .profile-stats {
    gap: 1rem;
  }
  
  .card-actions {
    flex-direction: column;
  }
  
  .btn-secondary {
    width: 100%;
    height: 44px;
  }
}

@media (max-width: 480px) {
  .card-content {
    padding: 1rem;
  }
  
  .card-header {
    padding: 1.5rem 1rem 1rem;
  }
  
  .card-title {
    font-size: 1.125rem;
  }
}

/* Keyframes de Animación */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

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

.card {
  animation: fadeInUp 0.6s ease;
}

.feature-icon:hover {
  animation: pulse 1s infinite;
}
class TarjetasAnimadas {
  constructor(selector) {
    this.container = document.querySelector(selector);
    this.cards = this.container.querySelectorAll('.card');
    this.observerOptions = {
      threshold: 0.1,
      rootMargin: '0px 0px -50px 0px'
    };
    
    this.init();
  }

  init() {
    this.setupIntersectionObserver();
    this.setupEventListeners();
    this.setupLazyLoading();
    this.setupAccessibility();
  }

  setupIntersectionObserver() {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          entry.target.classList.add('animate-in');
          observer.unobserve(entry.target);
        }
      });
    }, this.observerOptions);

    this.cards.forEach(card => {
      observer.observe(card);
    });
  }

  setupEventListeners() {
    this.cards.forEach(card => {
      // Funcionalidad añadir al carrito
      const addToCartBtn = card.querySelector('.btn-primary');
      if (addToCartBtn && card.classList.contains('product-card')) {
        addToCartBtn.addEventListener('click', (e) => {
          e.stopPropagation();
          this.handleAddToCart(card);
        });
      }

      // Funcionalidad lista de deseos
      const wishlistBtn = card.querySelector('.btn-secondary');
      if (wishlistBtn && card.classList.contains('product-card')) {
        wishlistBtn.addEventListener('click', (e) => {
          e.stopPropagation();
          this.handleWishlist(card, wishlistBtn);
        });
      }

      // Funcionalidad seguir
      const followBtn = card.querySelector('.btn-primary');
      if (followBtn && card.classList.contains('profile-card')) {
        followBtn.addEventListener('click', (e) => {
          e.stopPropagation();
          this.handleFollow(card, followBtn);
        });
      }

      // Funcionalidad leer más
      const readMoreBtn = card.querySelector('.read-more-btn');
      if (readMoreBtn) {
        readMoreBtn.addEventListener('click', (e) => {
          e.stopPropagation();
          this.handleReadMore(card);
        });
      }

      // Botones de interacción
      const engagementBtns = card.querySelectorAll('.engagement-btn');
      engagementBtns.forEach(btn => {
        btn.addEventListener('click', (e) => {
          e.stopPropagation();
          this.handleEngagement(btn);
        });
      });

      // Manejador de clic en tarjeta
      card.addEventListener('click', () => {
        this.handleCardClick(card);
      });

      // Navegación por teclado
      card.addEventListener('keydown', (e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          this.handleCardClick(card);
        }
      });
    });
  }

  setupLazyLoading() {
    const images = this.container.querySelectorAll('img[data-src]');
    const imageObserver = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const img = entry.target;
          img.src = img.dataset.src;
          img.classList.remove('lazy');
          imageObserver.unobserve(img);
        }
      });
    });

    images.forEach(img => {
      imageObserver.observe(img);
    });
  }

  setupAccessibility() {
    this.cards.forEach((card, index) => {
      // Añadir tabindex para navegación por teclado
      card.setAttribute('tabindex', '0');
      card.setAttribute('role', 'article');
      
      // Añadir aria-label para lectores de pantalla
      const title = card.querySelector('.card-title')?.textContent;
      if (title) {
        card.setAttribute('aria-label', `Tarjeta: ${title}`);
      }
    });
  }

  handleAddToCart(card) {
    const btn = card.querySelector('.btn-primary');
    const originalText = btn.textContent;
    
    // Retroalimentación visual
    btn.textContent = '¡Añadido!';
    btn.style.background = '#10b981';
    
    // Disparar evento personalizado
    const productTitle = card.querySelector('.card-title').textContent;
    window.dispatchEvent(new CustomEvent('addToCart', {
      detail: { product: productTitle, card }
    }));
    
    // Restablecer botón después de un retraso
    setTimeout(() => {
      btn.textContent = originalText;
      btn.style.background = '';
    }, 2000);
  }

  handleWishlist(card, btn) {
    const isActive = btn.classList.contains('active');
    
    if (isActive) {
      btn.classList.remove('active');
      btn.style.color = '';
    } else {
      btn.classList.add('active');
      btn.style.color = '#ef4444';
    }
    
    // Disparar evento personalizado
    const productTitle = card.querySelector('.card-title').textContent;
    window.dispatchEvent(new CustomEvent('toggleWishlist', {
      detail: { product: productTitle, added: !isActive }
    }));
  }

  handleFollow(card, btn) {
    const isFollowing = btn.classList.contains('following');
    
    if (isFollowing) {
      btn.textContent = 'Seguir';
      btn.classList.remove('following');
    } else {
      btn.textContent = 'Siguiendo';
      btn.classList.add('following');
    }
    
    // Disparar evento personalizado
    const profileName = card.querySelector('.profile-name').textContent;
    window.dispatchEvent(new CustomEvent('toggleFollow', {
      detail: { profile: profileName, following: !isFollowing }
    }));
  }

  handleReadMore(card) {
    // Disparar evento personalizado
    const title = card.querySelector('.card-title').textContent;
    window.dispatchEvent(new CustomEvent('readMore', {
      detail: { title, card }
    }));
  }

  handleEngagement(btn) {
    const countSpan = btn.querySelector('span');
    if (countSpan) {
      const currentCount = parseInt(countSpan.textContent);
      const isActive = btn.classList.contains('active');
      
      if (isActive) {
        countSpan.textContent = currentCount - 1;
        btn.classList.remove('active');
      } else {
        countSpan.textContent = currentCount + 1;
        btn.classList.add('active');
        
        // Añadir animación
        btn.style.transform = 'scale(1.2)';
        setTimeout(() => {
          btn.style.transform = '';
        }, 200);
      }
    }
  }

  handleCardClick(card) {
    // Añadir animación de clic
    card.style.transform = 'scale(0.98)';
    setTimeout(() => {
      card.style.transform = '';
    }, 150);
    
    // Disparar evento personalizado
    const title = card.querySelector('.card-title')?.textContent;
    window.dispatchEvent(new CustomEvent('cardClick', {
      detail: { title, card }
    }));
  }

  // Métodos de API pública
  addCard(cardHTML, position = 'end') {
    const tempDiv = document.createElement('div');
    tempDiv.innerHTML = cardHTML;
    const newCard = tempDiv.firstElementChild;
    
    if (position === 'start') {
      this.container.insertBefore(newCard, this.container.firstChild);
    } else {
      this.container.appendChild(newCard);
    }
    
    // Configurar event listeners para la nueva tarjeta
    this.setupEventListeners();
    
    // Animar entrada
    newCard.style.opacity = '0';
    newCard.style.transform = 'translateY(30px)';
    setTimeout(() => {
      newCard.style.transition = 'all 0.3s ease';
      newCard.style.opacity = '1';
      newCard.style.transform = 'translateY(0)';
    }, 100);
  }

  removeCard(cardElement) {
    cardElement.style.transition = 'all 0.3s ease';
    cardElement.style.opacity = '0';
    cardElement.style.transform = 'translateY(-30px)';
    
    setTimeout(() => {
      cardElement.remove();
    }, 300);
  }

  filterCards(filterFn) {
    this.cards.forEach(card => {
      const shouldShow = filterFn(card);
      card.style.display = shouldShow ? 'block' : 'none';
    });
  }

  sortCards(sortFn) {
    const cardsArray = Array.from(this.cards);
    cardsArray.sort(sortFn);
    
    cardsArray.forEach(card => {
      this.container.appendChild(card);
    });
  }

  destroy() {
    // Remover todos los event listeners y observers
    this.cards.forEach(card => {
      card.removeEventListener('click', this.handleCardClick);
      card.removeEventListener('keydown', this.handleKeydown);
    });
  }
}

// Auto-inicialización
document.addEventListener('DOMContentLoaded', () => {
  const cardsContainer = document.querySelector('.cards-container');
  if (cardsContainer) {
    const tarjetasAnimadas = new TarjetasAnimadas('.cards-container');
    
    // Hacer disponible globalmente
    window.tarjetasAnimadas = tarjetasAnimadas;
    
    // Ejemplos de event listeners
    window.addEventListener('addToCart', (e) => {
      console.log('Producto añadido al carrito:', e.detail.product);
    });
    
    window.addEventListener('toggleWishlist', (e) => {
      console.log('Lista de deseos modificada:', e.detail);
    });
    
    window.addEventListener('toggleFollow', (e) => {
      console.log('Seguimiento modificado:', e.detail);
    });
    
    window.addEventListener('cardClick', (e) => {
      console.log('Tarjeta clicada:', e.detail.title);
    });
  }
});

Ejemplos de Uso

Implementación Básica

// Inicializar tarjetas animadas
const cards = new TarjetasAnimadas('.cards-container');

// Añadir event listeners
window.addEventListener('addToCart', (e) => {
  // Manejar añadir al carrito
  console.log('Añadido al carrito:', e.detail.product);
});

Gestión Dinámica de Tarjetas

// Añadir nueva tarjeta
const newCardHTML = `
  <div class="card product-card">
    <!-- Contenido de la tarjeta -->
  </div>
`;
cards.addCard(newCardHTML);

// Filtrar tarjetas
cards.filterCards(card => {
  return card.classList.contains('product-card');
});

// Ordenar tarjetas por precio
cards.sortCards((a, b) => {
  const priceA = parseFloat(a.querySelector('.current-price').textContent.replace('€', ''));
  const priceB = parseFloat(b.querySelector('.current-price').textContent.replace('€', ''));
  return priceA - priceB;
});

Estilización Personalizada

/* Tema personalizado de tarjeta */
.card.custom-theme {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}

.card.custom-theme .card-title {
  color: white;
}

.card.custom-theme .card-description {
  color: rgba(255, 255, 255, 0.8);
}

Métodos de API

MétodoDescripciónParámetros
addCard(html, position)Añadir nueva tarjeta al contenedorhtml: HTML de la tarjeta, position: ‘start’ o ‘end’
removeCard(element)Remover tarjeta con animaciónelement: Elemento DOM de la tarjeta
filterCards(filterFn)Filtrar tarjetas visiblesfilterFn: Función que retorna boolean
sortCards(sortFn)Ordenar tarjetas en el contenedorsortFn: Función de comparación
destroy()Limpiar event listeners-

Opciones de Personalización

Variables CSS

:root {
  --card-bg: white;
  --card-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
  --card-hover-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
  --card-border-radius: 16px;
  --card-padding: 1.5rem;
  --primary-color: #3b82f6;
  --text-color: #1a1a1a;
  --text-secondary: #6b7280;
}

Configuración de Animaciones

.card {
  --hover-transform: translateY(-8px);
  --hover-duration: 0.3s;
  --hover-easing: ease;
}

Características de Accesibilidad

  • Navegación por Teclado: Soporte completo con Tab y Enter
  • Soporte para Lectores de Pantalla: Etiquetas ARIA y roles apropiados
  • Gestión de Foco: Indicadores de foco claros
  • Contraste de Color: Cumple con WCAG 2.1 AA
  • HTML Semántico: Jerarquía de encabezados y estructura apropiada

Soporte de Navegadores

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

Consideraciones de Rendimiento

  • Aceleración por Hardware: Transformaciones CSS para animaciones suaves
  • Intersection Observer: Animaciones eficientes basadas en scroll
  • Carga Perezosa: Imágenes cargadas solo cuando es necesario
  • Delegación de Eventos: Manejo optimizado de eventos
  • Reflows Mínimos: Animaciones solo con CSS donde es posible

Ejemplo de Integración

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Demo de Tarjetas Animadas</title>
  <link rel="stylesheet" href="cards.css">
</head>
<body>
  <div class="cards-container">
    <!-- Las tarjetas van aquí -->
  </div>
  
  <script src="cards.js"></script>
  <script>
    // Implementación personalizada
    document.addEventListener('DOMContentLoaded', () => {
      const cards = new TarjetasAnimadas('.cards-container');
      
      // Manejo personalizado de eventos
      window.addEventListener('addToCart', (e) => {
        // Mostrar notificación
        mostrarNotificacion(`¡${e.detail.product} añadido al carrito!`);
      });
    });
  </script>
</body>
</html>

HTML

47

líneas

CSS

219

líneas


                <div class="cards-container">
  <div class="card card-hover">
    <div class="card-image">
      <img src="https://via.placeholder.com/300x200" alt="Imagen de ejemplo">
      <div class="card-overlay">
        <button class="btn-action">Ver más</button>
      </div>
    </div>
    <div class="card-content">
      <h3>Tarjeta Animada</h3>
      <p>Esta es una tarjeta con efectos hover suaves y transiciones elegantes.</p>
      <div class="card-footer">
        <span class="card-date">15 Ene 2024</span>
        <span class="card-category">Diseño</span>
      </div>
    </div>
  </div>
  
  <div class="card card-flip">
    <div class="card-inner">
      <div class="card-front">
        <h3>Tarjeta con Flip</h3>
        <p>Pasa el cursor para ver el reverso</p>
      </div>
      <div class="card-back">
        <h3>Información adicional</h3>
        <p>Contenido del reverso de la tarjeta con más detalles.</p>
        <button class="btn-secondary">Acción</button>
      </div>
    </div>
  </div>
  
  <div class="card card-scale">
    <div class="card-header">
      <div class="card-icon">🎨</div>
      <h3>Tarjeta con Escala</h3>
    </div>
    <div class="card-body">
      <p>Efecto de escala al hacer hover con animación suave.</p>
      <ul class="card-features">
        <li>Animación fluida</li>
        <li>Diseño moderno</li>
        <li>Fácil personalización</li>
      </ul>
    </div>
  </div>
</div>

              
47líneas
1425caracteres
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 →