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
Diseño Responsivo
Sí
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.
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 CSSestado: 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 CSScargando: 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-pressedpara botones togglearia-busypara estados de cargaaria-labelpara botones solo con iconos
Navegación por Teclado
- Soporte completo para
Tab,EnteryEspacio - 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
Navegadores Modernos
- 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
transformyopacitypara animaciones suaves will-changepara 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>