A gradient pill that gathers four-point stars around it on hover, each twinkling on its own beat, while the sparkle icon inside turns and grows. The stars are clip-path shapes, no images.
Also known as: sparkle hover effect, ai button css, twinkle stars button.
<button class="btn-sparkle"> <span class="btn-sparkle-star" style="--i:0" aria-hidden="true"></span> <span class="btn-sparkle-star" style="--i:1" aria-hidden="true"></span> <span class="btn-sparkle-star" style="--i:2" aria-hidden="true"></span> <span class="btn-sparkle-star" style="--i:3" aria-hidden="true"></span> <svg class="btn-sparkle-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2l2.3 7.7L22 12l-7.7 2.3L12 22l-2.3-7.7L2 12l7.7-2.3z"/></svg> Generate </button>
.btn-sparkle {
--c: var(--btn-color, #3b82f6);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5em;
font: 600 var(--btn-size, 18px)/1.2 system-ui, sans-serif;
cursor: pointer;
position: relative;
padding: 0.8em 1.7em;
border: 0;
border-radius: 2em;
background: linear-gradient(135deg, var(--c), color-mix(in srgb, var(--c) 45%, #d946ef));
color: #fff;
transition: transform 0.3s ease, box-shadow 0.3s ease;}
.btn-sparkle-icon {
width: 1.1em;
height: 1.1em;
transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.btn-sparkle-star {
position: absolute;
width: 1.3em;
height: 1.3em;
background: color-mix(in srgb, var(--c) 15%, #fbbf24);
clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
transform: scale(0);
pointer-events: none;
}
.btn-sparkle-star:nth-child(1) { top: -1.1em; left: 0.5em; }
.btn-sparkle-star:nth-child(2) { top: -0.85em; right: 0; width: 0.85em; height: 0.85em; background: var(--c); }
.btn-sparkle-star:nth-child(3) { bottom: -1.15em; right: 1.2em; }
.btn-sparkle-star:nth-child(4) { bottom: -0.55em; left: -0.8em; width: 0.85em; height: 0.85em; background: var(--c); }
.btn-sparkle:is(:hover, :focus-visible) {
transform: translateY(-0.1em);
box-shadow: 0 0.4em 1.2em color-mix(in srgb, var(--c) 40%, transparent);
}
.btn-sparkle:is(:hover, :focus-visible) .btn-sparkle-icon { transform: rotate(90deg) scale(1.2); }
.btn-sparkle:is(:hover, :focus-visible) .btn-sparkle-star {
animation: btn-sparkle-twinkle 1.2s ease-in-out calc(var(--i) * 0.3s) infinite;
}
@keyframes btn-sparkle-twinkle {
0%, 100% { transform: scale(0) rotate(0deg); }
35% { transform: scale(1) rotate(45deg); }
65% { transform: scale(0.85) rotate(90deg); }
}
@media (prefers-reduced-motion: reduce) {
.btn-sparkle, .btn-sparkle *, .btn-sparkle::before, .btn-sparkle::after, .btn-sparkle *::before, .btn-sparkle *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--btn-color on the element or a parent.
Currently previewing #3b82f6.--btn-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 18px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.