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">
<!-- Estilos Básicos de Botones -->
<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>
<!-- Efectos Animados -->
<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>
<!-- Botones con Iconos -->
<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>
<!-- Estados de Carga -->
<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>
<!-- Variaciones de Tamaño -->
<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>
<!-- Efectos Especiales -->
<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>
<!-- Botones Toggle -->
<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;
}
/* Estilos Base de Botones */
.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;
}
/* Variantes de Botones */
.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);
}
/* Efectos Animados */
.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);
}
/* Estilos de Botones con Iconos */
.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);
}
/* Estados de Carga */
.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;
}
}
/* Variaciones de Tamaño */
.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;
}
/* Efectos Especiales */
.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;
}
/* Botones Toggle */
.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;
}
/* Tema Oscuro */
.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;
}
/* Diseño Responsivo */
@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;
}
}
/* Movimiento Reducido */
@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;
}
}
/* Estilos de Foco */
.btn:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
/* Modo de Alto Contraste */
@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;
// Manejar botones toggle
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);
}
// Manejar efecto ripple
if (boton.classList.contains('btn-ripple')) {
this.crearRipple(e, boton);
}
// Disparar eventos personalizados
this.dispararEventoBoton('clicBoton', {
boton,
tipo: this.obtenerTipoBoton(boton)
});
});
// Manejar navegación por teclado
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';
// Remover ripples existentes
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);
// Agregar nuevas partículas
if (particulas.length < 20 && Math.random() < 0.3) {
particulas.push(crearParticula());
}
// Actualizar y dibujar partículas
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() {
// Agregar atributos ARIA
const botones = document.querySelectorAll('.btn');
botones.forEach(boton => {
if (!boton.getAttribute('role')) {
boton.setAttribute('role', 'button');
}
if (!boton.hasAttribute('tabindex')) {
boton.setAttribute('tabindex', '0');
}
// Agregar aria-pressed para botones toggle
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);
}
// API Pública
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() {
// Limpiar event listeners y animaciones
this.botonesRipple = [];
this.botonesParticulas = [];
this.botonesMagneticos = [];
this.botones.clear();
}
}
// Auto-inicializar
let componentesBotonesAnimados;
document.addEventListener('DOMContentLoaded', () => {
componentesBotonesAnimados = new ComponentesBotonesAnimados();
});
// Exportar para uso de módulos
if (typeof module !== 'undefined' && module.exports) {
module.exports = ComponentesBotonesAnimados;
}
// API Global
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
// Inicializar componente
const botones = new ComponentesBotonesAnimados();
// Establecer estado de carga
botones.establecerEstadoCarga('#mi-boton', true);
// Manejar eventos
document.addEventListener('clicBoton', (e) => {
console.log('Botón clickeado:', e.detail.boton);
});
// Establecer estado de toggle
botones.establecerEstadoBoton('#toggle-btn', true);
// Obtener estado
const estado = botones.obtenerEstadoBoton('#toggle-btn');
console.log('Estado del botón:', estado);Crear Botón Personalizado
// Crear botón dinámicamente
function crearBotonPersonalizado(texto, tipo = 'primary') {
const boton = document.createElement('button');
boton.className = `btn btn-${tipo}`;
boton.innerHTML = `<span class="btn-text">${texto}</span>`;
// Agregar al DOM
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
/* Animación personalizada */
@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 personalizado */
.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
<!-- Para navegadores más antiguos -->
<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
// Limpiar al destruir
componentesBotonesAnimados.destruir();
// Remover event listeners específicos
document.removeEventListener('clicBoton', miManejador);Optimización de Animaciones
/* Optimizar para GPU */
.btn {
transform: translateZ(0);
backface-visibility: hidden;
}
/* Reducir repaints */
.btn-animado {
will-change: transform, opacity;
}Ejemplos de Integración
React
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() {
// Lógica del clic
}
}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>