interactive
intermediate
buttons
animation
hover
effects
components
ui
Categoría · Interactivo Nivel de Dificultad · Intermedio Publicado el · 15 de enero de 2024

Componentes de Botones Animados

Una colección completa de botones modernos animados con varios estilos, efectos y características interactivas para mejorar la experiencia del usuario

#buttons #animation #hover #effects #components #ui

Diseño Responsivo

Soporte para Modo Oscuro

No

líneas

127

Compatibilidad del Navegador

No

Vista Previa en Vivo

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

700px

Componentes de Botones Animados

Una colección completa de botones modernos animados que incluye varios estilos, efectos hover, estados de carga y animaciones interactivas para mejorar la experiencia del usuario y el engagement de la interfaz.

Características

  • Múltiples Estilos de Botones: Botones primarios, secundarios, outline, ghost y gradient
  • Animaciones Suaves: Transiciones basadas en CSS con timing personalizable
  • Efectos Hover: Animaciones de escala, brillo, ripple y morphing
  • Estados de Carga: Indicadores de spinner, progreso y pulse
  • Integración de Iconos: Soporte para iconos con transiciones suaves
  • Diseño Responsivo: Se adapta a todos los tamaños de pantalla y dispositivos táctiles
  • Soporte de Accesibilidad: Atributos ARIA y navegación por teclado
  • Diseño Moderno: Estética limpia con efectos de sombra sutiles

Demo

<div class="button-demo-container">
  
  <div class="button-section">
    <h3>Estilos Básicos de Botones</h3>
    <div class="button-grid">
      <button class="btn btn-primary">
        <span class="btn-text">Botón Primario</span>
      </button>
      
      <button class="btn btn-secondary">
        <span class="btn-text">Botón Secundario</span>
      </button>
      
      <button class="btn btn-outline">
        <span class="btn-text">Botón Outline</span>
      </button>
      
      <button class="btn btn-ghost">
        <span class="btn-text">Botón Ghost</span>
      </button>
      
      <button class="btn btn-gradient">
        <span class="btn-text">Botón Gradient</span>
      </button>
      
      <button class="btn btn-danger">
        <span class="btn-text">Botón Peligro</span>
      </button>
    </div>
  </div>

  
  <div class="button-section">
    <h3>Efectos Animados</h3>
    <div class="button-grid">
      <button class="btn btn-primary btn-scale">
        <span class="btn-text">Efecto Escala</span>
      </button>
      
      <button class="btn btn-primary btn-glow">
        <span class="btn-text">Efecto Brillo</span>
      </button>
      
      <button class="btn btn-primary btn-ripple">
        <span class="btn-text">Efecto Ripple</span>
        <span class="btn-ripple"></span>
      </button>
      
      <button class="btn btn-primary btn-slide">
        <span class="btn-text">Efecto Deslizar</span>
        <span class="btn-slide-bg"></span>
      </button>
      
      <button class="btn btn-primary btn-bounce">
        <span class="btn-text">Efecto Rebote</span>
      </button>
      
      <button class="btn btn-primary btn-rotate">
        <span class="btn-text">Efecto Rotar</span>
      </button>
    </div>
  </div>

  
  <div class="button-section">
    <h3>Botones con Iconos</h3>
    <div class="button-grid">
      <button class="btn btn-primary btn-icon">
        <svg class="btn-icon-svg" viewBox="0 0 24 24" fill="currentColor">
          <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
        </svg>
        <span class="btn-text">Agregar Elemento</span>
      </button>
      
      <button class="btn btn-secondary btn-icon">
        <svg class="btn-icon-svg" viewBox="0 0 24 24" fill="currentColor">
          <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
        </svg>
        <span class="btn-text">Editar</span>
      </button>
      
      <button class="btn btn-danger btn-icon">
        <svg class="btn-icon-svg" viewBox="0 0 24 24" fill="currentColor">
          <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
        </svg>
        <span class="btn-text">Eliminar</span>
      </button>
      
      <button class="btn btn-outline btn-icon-only" aria-label="Configuración">
        <svg class="btn-icon-svg" viewBox="0 0 24 24" fill="currentColor">
          <path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/>
        </svg>
      </button>
      
      <button class="btn btn-ghost btn-icon-only" aria-label="Buscar">
        <svg class="btn-icon-svg" viewBox="0 0 24 24" fill="currentColor">
          <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
        </svg>
      </button>
      
      <button class="btn btn-primary btn-icon-only" aria-label="Favorito">
        <svg class="btn-icon-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 class="button-section">
    <h3>Estados de Carga</h3>
    <div class="button-grid">
      <button class="btn btn-primary btn-loading">
        <span class="btn-spinner"></span>
        <span class="btn-text">Cargando...</span>
      </button>
      
      <button class="btn btn-secondary btn-loading-dots">
        <span class="btn-text">Procesando</span>
        <span class="btn-dots">
          <span class="dot"></span>
          <span class="dot"></span>
          <span class="dot"></span>
        </span>
      </button>
      
      <button class="btn btn-outline btn-loading-progress">
        <span class="btn-text">Subiendo</span>
        <span class="btn-progress">
          <span class="btn-progress-bar"></span>
        </span>
      </button>
      
      <button class="btn btn-gradient btn-pulse">
        <span class="btn-text">Sincronizando</span>
      </button>
    </div>
  </div>

  
  <div class="button-section">
    <h3>Variaciones de Tamaño</h3>
    <div class="button-grid">
      <button class="btn btn-primary btn-small">
        <span class="btn-text">Pequeño</span>
      </button>
      
      <button class="btn btn-primary btn-medium">
        <span class="btn-text">Mediano</span>
      </button>
      
      <button class="btn btn-primary btn-large">
        <span class="btn-text">Grande</span>
      </button>
      
      <button class="btn btn-primary btn-extra-large">
        <span class="btn-text">Extra Grande</span>
      </button>
    </div>
  </div>

  
  <div class="button-section">
    <h3>Efectos Especiales</h3>
    <div class="button-grid">
      <button class="btn btn-neon">
        <span class="btn-text">Efecto Neón</span>
      </button>
      
      <button class="btn btn-glass">
        <span class="btn-text">Efecto Cristal</span>
      </button>
      
      <button class="btn btn-3d">
        <span class="btn-text">Efecto 3D</span>
      </button>
      
      <button class="btn btn-liquid">
        <span class="btn-text">Efecto Líquido</span>
        <span class="btn-liquid-bg"></span>
      </button>
      
      <button class="btn btn-magnetic">
        <span class="btn-text">Efecto Magnético</span>
      </button>
      
      <button class="btn btn-particle">
        <span class="btn-text">Efecto Partículas</span>
        <canvas class="btn-particle-canvas"></canvas>
      </button>
    </div>
  </div>

  
  <div class="button-section">
    <h3>Botones Toggle</h3>
    <div class="button-grid">
      <button class="btn btn-toggle" data-toggle="false">
        <span class="btn-text">Alternar</span>
      </button>
      
      <button class="btn btn-switch" data-switch="false">
        <span class="btn-switch-track">
          <span class="btn-switch-thumb"></span>
        </span>
        <span class="btn-text">Interruptor</span>
      </button>
      
      <button class="btn btn-checkbox" data-checked="false">
        <span class="btn-checkbox-mark">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
          </svg>
        </span>
        <span class="btn-text">Checkbox</span>
      </button>
    </div>
  </div>
</div>
.button-demo-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
  min-height: 100vh;
}

.button-section {
  margin-bottom: 4rem;
  padding: 2rem;
  background: white;
  border-radius: 16px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
}

.button-section h3 {
  margin: 0 0 2rem 0;
  font-size: 1.5rem;
  font-weight: 700;
  color: #1a1a1a;
  text-align: center;
  position: relative;
}

.button-section h3::after {
  content: '';
  position: absolute;
  bottom: -0.5rem;
  left: 50%;
  transform: translateX(-50%);
  width: 60px;
  height: 3px;
  background: linear-gradient(90deg, #3b82f6, #8b5cf6);
  border-radius: 2px;
}

.button-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1.5rem;
  align-items: center;
  justify-items: center;
}.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 8px;
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: all 0.3s ease;
  overflow: hidden;
  user-select: none;
  outline: none;
  min-width: 120px;
  background: transparent;
}

.btn:focus {
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
}

.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  transform: none !important;
}

.btn-text {
  position: relative;
  z-index: 2;
  transition: all 0.3s ease;
}

.btn-icon-svg {
  width: 20px;
  height: 20px;
  transition: all 0.3s ease;
}.btn-primary {
  background: linear-gradient(135deg, #3b82f6, #1d4ed8);
  color: white;
  box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
}

.btn-primary:hover {
  background: linear-gradient(135deg, #2563eb, #1e40af);
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(59, 130, 246, 0.4);
}

.btn-secondary {
  background: linear-gradient(135deg, #6b7280, #4b5563);
  color: white;
  box-shadow: 0 4px 15px rgba(107, 114, 128, 0.3);
}

.btn-secondary:hover {
  background: linear-gradient(135deg, #4b5563, #374151);
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(107, 114, 128, 0.4);
}

.btn-outline {
  background: transparent;
  color: #3b82f6;
  border: 2px solid #3b82f6;
}

.btn-outline:hover {
  background: #3b82f6;
  color: white;
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(59, 130, 246, 0.3);
}

.btn-ghost {
  background: transparent;
  color: #6b7280;
  border: 1px solid transparent;
}

.btn-ghost:hover {
  background: #f3f4f6;
  color: #374151;
  border-color: #e5e7eb;
  transform: translateY(-1px);
}

.btn-gradient {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}

.btn-gradient:hover {
  background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%);
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}

.btn-danger {
  background: linear-gradient(135deg, #ef4444, #dc2626);
  color: white;
  box-shadow: 0 4px 15px rgba(239, 68, 68, 0.3);
}

.btn-danger:hover {
  background: linear-gradient(135deg, #dc2626, #b91c1c);
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(239, 68, 68, 0.4);
}.btn-scale:hover {
  transform: scale(1.05);
}

.btn-glow {
  position: relative;
}

.btn-glow::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  border-radius: 8px;
  background: linear-gradient(135deg, #3b82f6, #1d4ed8);
  opacity: 0;
  transition: opacity 0.3s ease;
  z-index: -1;
  filter: blur(10px);
}

.btn-glow:hover::before {
  opacity: 0.7;
}

.btn-ripple {
  overflow: hidden;
}

.btn-ripple .btn-ripple {
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.6);
  transform: scale(0);
  animation: ripple 0.6s linear;
  pointer-events: none;
}

@keyframes ripple {
  to {
    transform: scale(4);
    opacity: 0;
  }
}

.btn-slide {
  overflow: hidden;
}

.btn-slide-bg {
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: rgba(255, 255, 255, 0.1);
  transition: left 0.3s ease;
  z-index: 1;
}

.btn-slide:hover .btn-slide-bg {
  left: 0;
}

.btn-bounce:hover {
  animation: bounce 0.6s ease;
}

@keyframes bounce {
  0%, 20%, 60%, 100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-10px);
  }
  80% {
    transform: translateY(-5px);
  }
}

.btn-rotate:hover {
  transform: rotateY(180deg);
}.btn-icon {
  gap: 0.75rem;
}

.btn-icon-only {
  width: 48px;
  height: 48px;
  padding: 0;
  border-radius: 50%;
  min-width: auto;
}

.btn-icon:hover .btn-icon-svg {
  transform: scale(1.1);
}.btn-loading {
  pointer-events: none;
}

.btn-spinner {
  width: 20px;
  height: 20px;
  border: 2px solid transparent;
  border-top: 2px solid currentColor;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

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

.btn-loading-dots .btn-dots {
  display: flex;
  gap: 4px;
  margin-left: 8px;
}

.btn-dots .dot {
  width: 6px;
  height: 6px;
  background: currentColor;
  border-radius: 50%;
  animation: dotPulse 1.4s ease-in-out infinite both;
}

.btn-dots .dot:nth-child(1) { animation-delay: -0.32s; }
.btn-dots .dot:nth-child(2) { animation-delay: -0.16s; }

@keyframes dotPulse {
  0%, 80%, 100% {
    transform: scale(0.8);
    opacity: 0.5;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}

.btn-loading-progress {
  position: relative;
  overflow: hidden;
}

.btn-progress {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: rgba(255, 255, 255, 0.2);
}

.btn-progress-bar {
  display: block;
  width: 0;
  height: 100%;
  background: currentColor;
  animation: progressBar 2s ease-in-out infinite;
}

@keyframes progressBar {
  0% { width: 0; }
  50% { width: 70%; }
  100% { width: 100%; }
}

.btn-pulse {
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.7;
  }
}.btn-small {
  padding: 0.5rem 1rem;
  font-size: 0.875rem;
  min-width: 80px;
}

.btn-medium {
  padding: 0.75rem 1.5rem;
  font-size: 1rem;
  min-width: 120px;
}

.btn-large {
  padding: 1rem 2rem;
  font-size: 1.125rem;
  min-width: 160px;
}

.btn-extra-large {
  padding: 1.25rem 2.5rem;
  font-size: 1.25rem;
  min-width: 200px;
}.btn-neon {
  background: transparent;
  color: #00ffff;
  border: 2px solid #00ffff;
  text-shadow: 0 0 10px #00ffff;
  box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
}

.btn-neon:hover {
  background: #00ffff;
  color: #000;
  text-shadow: none;
  box-shadow: 0 0 30px rgba(0, 255, 255, 0.6);
}

.btn-glass {
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: #333;
}

.btn-glass:hover {
  background: rgba(255, 255, 255, 0.2);
  transform: translateY(-2px);
}

.btn-3d {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  box-shadow: 0 8px 0 #5a67d8, 0 12px 20px rgba(0, 0, 0, 0.3);
  transform: translateY(0);
}

.btn-3d:hover {
  transform: translateY(4px);
  box-shadow: 0 4px 0 #5a67d8, 0 8px 15px rgba(0, 0, 0, 0.3);
}

.btn-3d:active {
  transform: translateY(8px);
  box-shadow: 0 0 0 #5a67d8, 0 4px 10px rgba(0, 0, 0, 0.3);
}

.btn-liquid {
  background: #3b82f6;
  color: white;
  overflow: hidden;
}

.btn-liquid-bg {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  height: 100%;
  background: #1d4ed8;
  transition: top 0.3s ease;
  z-index: 1;
}

.btn-liquid:hover .btn-liquid-bg {
  top: 0;
}

.btn-magnetic {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  transition: all 0.1s ease;
}

.btn-particle {
  background: #1a1a1a;
  color: white;
  position: relative;
  overflow: hidden;
}

.btn-particle-canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
}.btn-toggle {
  background: #e5e7eb;
  color: #6b7280;
  transition: all 0.3s ease;
}

.btn-toggle.active {
  background: #3b82f6;
  color: white;
}

.btn-switch {
  background: transparent;
  color: #6b7280;
  padding: 0.5rem;
  gap: 1rem;
}

.btn-switch-track {
  width: 48px;
  height: 24px;
  background: #e5e7eb;
  border-radius: 12px;
  position: relative;
  transition: background 0.3s ease;
}

.btn-switch-thumb {
  width: 20px;
  height: 20px;
  background: white;
  border-radius: 50%;
  position: absolute;
  top: 2px;
  left: 2px;
  transition: transform 0.3s ease;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.btn-switch.active .btn-switch-track {
  background: #3b82f6;
}

.btn-switch.active .btn-switch-thumb {
  transform: translateX(24px);
}

.btn-checkbox {
  background: transparent;
  color: #6b7280;
  border: 2px solid #d1d5db;
  gap: 1rem;
}

.btn-checkbox-mark {
  width: 20px;
  height: 20px;
  border: 2px solid #d1d5db;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;
}

.btn-checkbox-mark svg {
  width: 14px;
  height: 14px;
  opacity: 0;
  transform: scale(0);
  transition: all 0.3s ease;
}

.btn-checkbox.active {
  border-color: #3b82f6;
  color: #3b82f6;
}

.btn-checkbox.active .btn-checkbox-mark {
  background: #3b82f6;
  border-color: #3b82f6;
}

.btn-checkbox.active .btn-checkbox-mark svg {
  opacity: 1;
  transform: scale(1);
  color: white;
}.dark .button-section {
  background: #1f2937;
  border-color: #374151;
}

.dark .button-section h3 {
  color: #f9fafb;
}

.dark .btn-ghost {
  color: #d1d5db;
}

.dark .btn-ghost:hover {
  background: #374151;
  color: #f3f4f6;
  border-color: #4b5563;
}

.dark .btn-glass {
  background: rgba(0, 0, 0, 0.2);
  color: #e5e7eb;
  border-color: rgba(255, 255, 255, 0.1);
}

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

.dark .btn-switch-track {
  background: #374151;
}

.dark .btn-checkbox {
  border-color: #4b5563;
  color: #d1d5db;
}

.dark .btn-checkbox-mark {
  border-color: #4b5563;
}@media (max-width: 768px) {
  .button-demo-container {
    padding: 1rem;
  }
  
  .button-section {
    padding: 1.5rem;
    margin-bottom: 2rem;
  }
  
  .button-grid {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 1rem;
  }
  
  .btn {
    min-width: 100px;
    padding: 0.625rem 1.25rem;
    font-size: 0.875rem;
  }
  
  .btn-large,
  .btn-extra-large {
    padding: 0.75rem 1.5rem;
    font-size: 1rem;
    min-width: 120px;
  }
}

@media (max-width: 480px) {
  .button-grid {
    grid-template-columns: 1fr;
  }
  
  .btn {
    width: 100%;
    max-width: 280px;
  }
}@media (prefers-reduced-motion: reduce) {
  .btn,
  .btn-spinner,
  .btn-dots .dot,
  .btn-progress-bar,
  .btn-switch-thumb,
  .btn-checkbox-mark svg {
    transition: none;
    animation: none;
  }
}.btn:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}@media (prefers-contrast: high) {
  .btn {
    border: 2px solid;
  }
  
  .btn-primary {
    background: #0000ff;
    border-color: #0000ff;
  }
  
  .btn-secondary {
    background: #666666;
    border-color: #666666;
  }
  
  .btn-danger {
    background: #ff0000;
    border-color: #ff0000;
  }
}
class ComponentesBotonesAnimados {
  constructor() {
    this.botones = new Map();
    this.botonesRipple = [];
    this.botonesParticulas = [];
    this.botonesMagneticos = [];
    
    this.init();
  }

  init() {
    this.configurarEventListeners();
    this.inicializarEfectoRipple();
    this.inicializarEfectoParticulas();
    this.inicializarEfectoMagnetico();
    this.inicializarBotonesToggle();
    this.configurarAccesibilidad();
  }

  configurarEventListeners() {
    document.addEventListener('click', (e) => {
      const boton = e.target.closest('.btn');
      if (!boton) return;

      if (boton.classList.contains('btn-toggle')) {
        this.manejarToggle(boton);
      }
      
      if (boton.classList.contains('btn-switch')) {
        this.manejarSwitch(boton);
      }
      
      if (boton.classList.contains('btn-checkbox')) {
        this.manejarCheckbox(boton);
      }

      if (boton.classList.contains('btn-ripple')) {
        this.crearRipple(e, boton);
      }

      this.dispararEventoBoton('clicBoton', {
        boton,
        tipo: this.obtenerTipoBoton(boton)
      });
    });

    document.addEventListener('keydown', (e) => {
      if (e.key === 'Enter' || e.key === ' ') {
        const boton = e.target.closest('.btn');
        if (boton) {
          e.preventDefault();
          boton.click();
        }
      }
    });
  }

  inicializarEfectoRipple() {
    const botonesRipple = document.querySelectorAll('.btn-ripple');
    botonesRipple.forEach(boton => {
      this.botonesRipple.push(boton);
    });
  }

  crearRipple(evento, boton) {
    const ripple = document.createElement('span');
    const rect = boton.getBoundingClientRect();
    const tamaño = Math.max(rect.width, rect.height);
    const x = evento.clientX - rect.left - tamaño / 2;
    const y = evento.clientY - rect.top - tamaño / 2;
    
    ripple.className = 'btn-ripple';
    ripple.style.width = ripple.style.height = tamaño + 'px';
    ripple.style.left = x + 'px';
    ripple.style.top = y + 'px';

    const ripplesExistentes = boton.querySelectorAll('.btn-ripple');
    ripplesExistentes.forEach(r => r.remove());
    
    boton.appendChild(ripple);
    
    setTimeout(() => {
      ripple.remove();
    }, 600);
  }

  inicializarEfectoParticulas() {
    const botonesParticulas = document.querySelectorAll('.btn-particle');
    botonesParticulas.forEach(boton => {
      const canvas = boton.querySelector('.btn-particle-canvas');
      if (canvas) {
        this.configurarCanvasParticulas(boton, canvas);
        this.botonesParticulas.push({ boton, canvas });
      }
    });
  }

  configurarCanvasParticulas(boton, canvas) {
    const ctx = canvas.getContext('2d');
    const particulas = [];
    
    const redimensionarCanvas = () => {
      const rect = boton.getBoundingClientRect();
      canvas.width = rect.width;
      canvas.height = rect.height;
    };
    
    redimensionarCanvas();
    window.addEventListener('resize', redimensionarCanvas);
    
    boton.addEventListener('mouseenter', () => {
      this.iniciarAnimacionParticulas(ctx, canvas, particulas);
    });
    
    boton.addEventListener('mouseleave', () => {
      particulas.length = 0;
    });
  }

  iniciarAnimacionParticulas(ctx, canvas, particulas) {
    const crearParticula = () => {
      return {
        x: Math.random() * canvas.width,
        y: Math.random() * canvas.height,
        vx: (Math.random() - 0.5) * 2,
        vy: (Math.random() - 0.5) * 2,
        vida: 1,
        decaimiento: Math.random() * 0.02 + 0.01
      };
    };
    
    const animar = () => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      if (particulas.length < 20 && Math.random() < 0.3) {
        particulas.push(crearParticula());
      }

      for (let i = particulas.length - 1; i >= 0; i--) {
        const particula = particulas[i];
        
        particula.x += particula.vx;
        particula.y += particula.vy;
        particula.vida -= particula.decaimiento;
        
        if (particula.vida <= 0) {
          particulas.splice(i, 1);
          continue;
        }
        
        ctx.save();
        ctx.globalAlpha = particula.vida;
        ctx.fillStyle = '#ffffff';
        ctx.beginPath();
        ctx.arc(particula.x, particula.y, 2, 0, Math.PI * 2);
        ctx.fill();
        ctx.restore();
      }
      
      if (particulas.length > 0) {
        requestAnimationFrame(animar);
      }
    };
    
    animar();
  }

  inicializarEfectoMagnetico() {
    const botonesMagneticos = document.querySelectorAll('.btn-magnetic');
    botonesMagneticos.forEach(boton => {
      this.configurarEfectoMagnetico(boton);
      this.botonesMagneticos.push(boton);
    });
  }

  configurarEfectoMagnetico(boton) {
    boton.addEventListener('mousemove', (e) => {
      const rect = boton.getBoundingClientRect();
      const x = e.clientX - rect.left - rect.width / 2;
      const y = e.clientY - rect.top - rect.height / 2;
      
      const distancia = Math.sqrt(x * x + y * y);
      const distanciaMaxima = Math.max(rect.width, rect.height) / 2;
      
      if (distancia < distanciaMaxima) {
        const fuerza = (distanciaMaxima - distancia) / distanciaMaxima;
        const moverX = x * fuerza * 0.3;
        const moverY = y * fuerza * 0.3;
        
        boton.style.transform = `translate(${moverX}px, ${moverY}px)`;
      }
    });
    
    boton.addEventListener('mouseleave', () => {
      boton.style.transform = 'translate(0, 0)';
    });
  }

  inicializarBotonesToggle() {
    const botonesToggle = document.querySelectorAll('.btn-toggle, .btn-switch, .btn-checkbox');
    botonesToggle.forEach(boton => {
      const estadoInicial = boton.dataset.toggle === 'true' || 
                          boton.dataset.switch === 'true' || 
                          boton.dataset.checked === 'true';
      
      if (estadoInicial) {
        boton.classList.add('active');
      }
      
      this.botones.set(boton, {
        tipo: this.obtenerTipoBoton(boton),
        estado: estadoInicial
      });
    });
  }

  manejarToggle(boton) {
    const datosBoton = this.botones.get(boton);
    const nuevoEstado = !datosBoton.estado;
    
    datosBoton.estado = nuevoEstado;
    boton.dataset.toggle = nuevoEstado.toString();
    
    if (nuevoEstado) {
      boton.classList.add('active');
    } else {
      boton.classList.remove('active');
    }
    
    this.dispararEventoBoton('toggleBoton', {
      boton,
      estado: nuevoEstado
    });
  }

  manejarSwitch(boton) {
    const datosBoton = this.botones.get(boton);
    const nuevoEstado = !datosBoton.estado;
    
    datosBoton.estado = nuevoEstado;
    boton.dataset.switch = nuevoEstado.toString();
    
    if (nuevoEstado) {
      boton.classList.add('active');
    } else {
      boton.classList.remove('active');
    }
    
    this.dispararEventoBoton('switchBoton', {
      boton,
      estado: nuevoEstado
    });
  }

  manejarCheckbox(boton) {
    const datosBoton = this.botones.get(boton);
    const nuevoEstado = !datosBoton.estado;
    
    datosBoton.estado = nuevoEstado;
    boton.dataset.checked = nuevoEstado.toString();
    
    if (nuevoEstado) {
      boton.classList.add('active');
    } else {
      boton.classList.remove('active');
    }
    
    this.dispararEventoBoton('checkBoton', {
      boton,
      marcado: nuevoEstado
    });
  }

  obtenerTipoBoton(boton) {
    if (boton.classList.contains('btn-toggle')) return 'toggle';
    if (boton.classList.contains('btn-switch')) return 'switch';
    if (boton.classList.contains('btn-checkbox')) return 'checkbox';
    if (boton.classList.contains('btn-primary')) return 'primary';
    if (boton.classList.contains('btn-secondary')) return 'secondary';
    if (boton.classList.contains('btn-outline')) return 'outline';
    if (boton.classList.contains('btn-ghost')) return 'ghost';
    if (boton.classList.contains('btn-gradient')) return 'gradient';
    if (boton.classList.contains('btn-danger')) return 'danger';
    return 'default';
  }

  configurarAccesibilidad() {

    const botones = document.querySelectorAll('.btn');
    botones.forEach(boton => {
      if (!boton.getAttribute('role')) {
        boton.setAttribute('role', 'button');
      }
      
      if (!boton.hasAttribute('tabindex')) {
        boton.setAttribute('tabindex', '0');
      }

      if (boton.classList.contains('btn-toggle') || 
          boton.classList.contains('btn-switch') || 
          boton.classList.contains('btn-checkbox')) {
        const estaPresionado = boton.classList.contains('active');
        boton.setAttribute('aria-pressed', estaPresionado.toString());
      }
    });
  }

  dispararEventoBoton(nombreEvento, detalle) {
    const evento = new CustomEvent(nombreEvento, {
      detail: detalle,
      bubbles: true,
      cancelable: true
    });
    document.dispatchEvent(evento);
  }

  establecerEstadoBoton(boton, estado) {
    if (typeof boton === 'string') {
      boton = document.querySelector(boton);
    }
    
    if (!boton) return;
    
    const datosBoton = this.botones.get(boton);
    if (!datosBoton) return;
    
    datosBoton.estado = estado;
    
    if (boton.classList.contains('btn-toggle')) {
      boton.dataset.toggle = estado.toString();
    } else if (boton.classList.contains('btn-switch')) {
      boton.dataset.switch = estado.toString();
    } else if (boton.classList.contains('btn-checkbox')) {
      boton.dataset.checked = estado.toString();
    }
    
    if (estado) {
      boton.classList.add('active');
    } else {
      boton.classList.remove('active');
    }
    
    boton.setAttribute('aria-pressed', estado.toString());
  }

  obtenerEstadoBoton(boton) {
    if (typeof boton === 'string') {
      boton = document.querySelector(boton);
    }
    
    const datosBoton = this.botones.get(boton);
    return datosBoton ? datosBoton.estado : null;
  }

  establecerEstadoCarga(boton, cargando = true) {
    if (typeof boton === 'string') {
      boton = document.querySelector(boton);
    }
    
    if (!boton) return;
    
    if (cargando) {
      boton.classList.add('btn-loading');
      boton.disabled = true;
      boton.setAttribute('aria-busy', 'true');
    } else {
      boton.classList.remove('btn-loading');
      boton.disabled = false;
      boton.setAttribute('aria-busy', 'false');
    }
  }

  destruir() {

    this.botonesRipple = [];
    this.botonesParticulas = [];
    this.botonesMagneticos = [];
    this.botones.clear();
  }
}

let componentesBotonesAnimados;
document.addEventListener('DOMContentLoaded', () => {
  componentesBotonesAnimados = new ComponentesBotonesAnimados();
});

if (typeof module !== 'undefined' && module.exports) {
  module.exports = ComponentesBotonesAnimados;
}

window.ComponentesBotonesAnimados = ComponentesBotonesAnimados;

## Ejemplos de Uso

### Botón Básico
```html
<button class="btn btn-primary">
  <span class="btn-text">Mi Botón</span>
</button>

Botón con Icono

<button class="btn btn-primary btn-icon">
  <svg class="btn-icon-svg" viewBox="0 0 24 24" fill="currentColor">
    <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
  </svg>
  <span class="btn-text">Agregar</span>
</button>

Botón de Carga

<button class="btn btn-primary btn-loading">
  <span class="btn-spinner"></span>
  <span class="btn-text">Cargando...</span>
</button>

Integración con JavaScript


const botones = new ComponentesBotonesAnimados();

botones.establecerEstadoCarga('#mi-boton', true);

document.addEventListener('clicBoton', (e) => {
  console.log('Botón clickeado:', e.detail.boton);
});

botones.establecerEstadoBoton('#toggle-btn', true);

const estado = botones.obtenerEstadoBoton('#toggle-btn');
console.log('Estado del botón:', estado);

Crear Botón Personalizado


function crearBotonPersonalizado(texto, tipo = 'primary') {
  const boton = document.createElement('button');
  boton.className = `btn btn-${tipo}`;
  boton.innerHTML = `<span class="btn-text">${texto}</span>`;

  document.body.appendChild(boton);
  
  return boton;
}

const miBoton = crearBotonPersonalizado('Botón Dinámico', 'gradient');

Referencia de API

Métodos

establecerEstadoBoton(boton, estado)

Establece el estado de un botón toggle/switch/checkbox.

  • boton: Elemento del botón o selector CSS
  • estado: Boolean - true para activo, false para inactivo

obtenerEstadoBoton(boton)

Obtiene el estado actual de un botón.

  • boton: Elemento del botón o selector CSS
  • Retorna: Boolean o null si no se encuentra

establecerEstadoCarga(boton, cargando)

Establece el estado de carga de un botón.

  • boton: Elemento del botón o selector CSS
  • cargando: Boolean - true para mostrar carga, false para ocultar

destruir()

Limpia todos los event listeners y animaciones.

Eventos

clicBoton

Se dispara cuando se hace clic en cualquier botón.

document.addEventListener('clicBoton', (e) => {
  console.log(e.detail.boton); // Elemento del botón
  console.log(e.detail.tipo);  // Tipo de botón
});

toggleBoton

Se dispara cuando cambia el estado de un botón toggle.

document.addEventListener('toggleBoton', (e) => {
  console.log(e.detail.estado); // Nuevo estado
});

switchBoton

Se dispara cuando cambia el estado de un switch.

document.addEventListener('switchBoton', (e) => {
  console.log(e.detail.estado); // Nuevo estado
});

checkBoton

Se dispara cuando cambia el estado de un checkbox.

document.addEventListener('checkBoton', (e) => {
  console.log(e.detail.marcado); // Estado marcado
});

Clases CSS

Tipos de Botones

  • .btn-primary - Botón primario azul
  • .btn-secondary - Botón secundario gris
  • .btn-outline - Botón con borde
  • .btn-ghost - Botón transparente
  • .btn-gradient - Botón con gradiente
  • .btn-danger - Botón de peligro rojo

Efectos de Animación

  • .btn-scale - Efecto de escala al hover
  • .btn-glow - Efecto de brillo
  • .btn-ripple - Efecto ripple al hacer clic
  • .btn-slide - Efecto de deslizamiento
  • .btn-bounce - Efecto de rebote
  • .btn-rotate - Efecto de rotación

Efectos Especiales

  • .btn-neon - Efecto neón
  • .btn-glass - Efecto cristal
  • .btn-3d - Efecto 3D
  • .btn-liquid - Efecto líquido
  • .btn-magnetic - Efecto magnético
  • .btn-particle - Efecto de partículas

Tamaños

  • .btn-small - Botón pequeño
  • .btn-medium - Botón mediano (por defecto)
  • .btn-large - Botón grande
  • .btn-extra-large - Botón extra grande

Estados

  • .btn-loading - Estado de carga
  • .btn-loading-dots - Carga con puntos
  • .btn-loading-progress - Carga con barra de progreso
  • .btn-pulse - Efecto de pulso
  • .active - Estado activo para toggles

Personalización

Variables CSS

:root {
  --btn-primary-bg: linear-gradient(135deg, #3b82f6, #1d4ed8);
  --btn-primary-hover: linear-gradient(135deg, #2563eb, #1e40af);
  --btn-border-radius: 8px;
  --btn-padding: 0.75rem 1.5rem;
  --btn-font-size: 1rem;
  --btn-font-weight: 600;
  --btn-transition: all 0.3s ease;
  --btn-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
  --btn-shadow-hover: 0 8px 25px rgba(59, 130, 246, 0.4);
}

Animaciones Personalizadas

@keyframes miAnimacion {
  0% { transform: scale(1); }
  50% { transform: scale(1.1); }
  100% { transform: scale(1); }
}

.btn-mi-efecto:hover {
  animation: miAnimacion 0.3s ease;
}

Integración con Temas

.tema-oscuro .btn-primary {
  --btn-primary-bg: linear-gradient(135deg, #1e40af, #1e3a8a);
  --btn-primary-hover: linear-gradient(135deg, #1d4ed8, #1e40af);
}

.tema-colorido .btn-primary {
  --btn-primary-bg: linear-gradient(135deg, #f59e0b, #d97706);
  --btn-primary-hover: linear-gradient(135deg, #d97706, #b45309);
}

Accesibilidad

Soporte ARIA

  • Atributos role="button" automáticos
  • aria-pressed para botones toggle
  • aria-busy para estados de carga
  • aria-label para botones solo con iconos
  • Soporte completo para Tab, Enter y Espacio
  • Indicadores de foco visibles
  • Navegación lógica entre elementos

Soporte para Lectores de Pantalla

  • Texto descriptivo para todos los botones
  • Anuncios de cambios de estado
  • Contenido semánticamente estructurado

Movimiento Reducido

@media (prefers-reduced-motion: reduce) {
  .btn {
    transition: none;
    animation: none;
  }
}

Soporte de Navegadores

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

Características con Fallback

  • CSS Grid → Flexbox
  • CSS Variables → Valores estáticos
  • Backdrop Filter → Fondo sólido
  • CSS Animations → Transiciones simples

Polyfills Recomendados


<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>

Consideraciones de Rendimiento

Técnicas de Optimización

  • Uso de transform y opacity para animaciones suaves
  • will-change para elementos animados
  • Debounce en eventos de mouse frecuentes
  • Lazy loading para efectos complejos

Gestión de Memoria


componentesBotonesAnimados.destruir();

document.removeEventListener('clicBoton', miManejador);

Optimización de Animaciones

.btn {
  transform: translateZ(0);
  backface-visibility: hidden;
}.btn-animado {
  will-change: transform, opacity;
}

Ejemplos de Integración

React

x
import React, { useEffect, useRef } from 'react';

function BotonAnimado({ children, tipo = 'primary', onClick }) {
  const botonRef = useRef(null);
  
  useEffect(() => {
    const botones = new ComponentesBotonesAnimados();
    
    return () => {
      botones.destruir();
    };
  }, []);
  
  return (
    <button 
      ref={botonRef}
      className={`btn btn-${tipo}`}
      onClick={onClick}
    >
      <span className="btn-text">{children}</span>
    </button>
  );
}

Vue

<template>
  <button 
    :class="claseBoton"
    @click="manejarClic"
    ref="boton"
  >
    <span class="btn-text">
      <slot></slot>
    </span>
  </button>
</template>

<script>
export default {
  props: {
    tipo: {
      type: String,
      default: 'primary'
    }
  },
  computed: {
    claseBoton() {
      return `btn btn-${this.tipo}`;
    }
  },
  mounted() {
    this.botones = new ComponentesBotonesAnimados();
  },
  beforeDestroy() {
    if (this.botones) {
      this.botones.destruir();
    }
  },
  methods: {
    manejarClic() {
      this.$emit('click');
    }
  }
};
</script>

Angular

import { Component, Input, OnInit, OnDestroy, ElementRef } from '@angular/core';

@Component({
  selector: 'app-boton-animado',
  template: `
    <button [class]="claseBoton" (click)="onClick()">
      <span class="btn-text">
        <ng-content></ng-content>
      </span>
    </button>
  `
})
export class BotonAnimadoComponent implements OnInit, OnDestroy {
  @Input() tipo: string = 'primary';
  
  private botones: any;
  
  constructor(private elementRef: ElementRef) {}
  
  ngOnInit() {
    this.botones = new (window as any).ComponentesBotonesAnimados();
  }
  
  ngOnDestroy() {
    if (this.botones) {
      this.botones.destruir();
    }
  }
  
  get claseBoton() {
    return `btn btn-${this.tipo}`;
  }
  
  onClick() {

  }
}

HTML

14

líneas

CSS

113

líneas


                <div class="button-showcase">
  <div class="button-group">
    <button class="btn btn-primary">Botón Primario</button>
    <button class="btn btn-secondary">Botón Secundario</button>
    <button class="btn btn-success">Botón Éxito</button>
    <button class="btn btn-danger">Botón Peligro</button>
  </div>
  <div class="button-group">
    <button class="btn btn-outline">Botón Outline</button>
    <button class="btn btn-gradient">Botón Gradiente</button>
    <button class="btn btn-glow">Botón Resplandor</button>
    <button class="btn btn-pulse">Botón Pulso</button>
  </div>
</div>

              
14líneas
587caracteres
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 ->