<a href="https://youtu.be/may0MddSzNc" target="_blank" data-keyframers-credit style="color: #000"></a>
<script src="https://codepen.io/shshaw/pen/QmZYMG.js"></script>
<button class="like-button">
<div class="like-wrapper">
<div class="ripple"></div>
<svg class="heart" width="24" height="24" viewBox="0 0 24 24">
<path d="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"></path>
</svg>
<div class="particles" style="--total-particles: 6">
<div class="particle" style="--i: 1; --color: #7642F0"></div>
<div class="particle" style="--i: 2; --color: #AFD27F"></div>
<div class="particle" style="--i: 3; --color: #DE8F4F"></div>
<div class="particle" style="--i: 4; --color: #D0516B"></div>
<div class="particle" style="--i: 5; --color: #5686F2"></div>
<div class="particle" style="--i: 6; --color: #D53EF3"></div>
</div>
</div>
</button>
*, *:before, *:after {
position: relative;
box-sizing: border-box;
}
:root {
--color-bg: #FDF1F2;
--color-heart: #EA442B;
--easing: cubic-bezier(.7,0,.3,1);
--duration: .5s;
}
html, body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.like-button {
font-size: 35vmin;
appearance: none;
border: none;
border-radius: 50%;
background: white;
width: 1em;
height: 1em;
padding: 0;
margin: 0;
outline: none;
z-index: 2;
transition: transform var(--duration) var(--easing);
cursor: pointer;
&:before {
z-index: -1;
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow: 0 .3em 0.6em rgba(black, 0.3);
border-radius: inherit;
transition: inherit;
}
&:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #fff;
border-radius: inherit;
z-index: -1;
}
&:active {
&:before {
animation: depress-shadow var(--duration) var(--easing) both;
}
}
&:focus:after {
animation: depress var(--duration) var(--easing) both;
}
@keyframes depress {
from, to {
transform: none;
}
50% {
transform: translateY(5%) scale(0.9);
}
}
@keyframes depress-shadow {
from, to {
transform: none;
}
50% {
transform: scale(0.5);
}
}
}
.like-wrapper {
display: grid;
align-items: center;
justify-content: center;
z-index: 1;
> * {
margin: auto;
grid-area: 1 / 1;
}
}
.heart {
width: .5em;
height: .5em;
display: block;
> path {
stroke: var(--color-heart);
stroke-width: 2;
fill: transparent;
transition: fill var(--duration) var(--easing);
.like-button:focus & {
fill: var(--color-heart);
}
}
transform-origin: center 80%;
.like-button:focus & {
animation: heart-bounce var(--duration) var(--easing);
@keyframes heart-bounce {
40% { transform: scale(0.7); }
0%, 80%, 100% { transform: scale(1); }
}
}
}
/* Added wrapper to prevent layout jank with resizing particles */
.particles {
width: 1px;
height: 1px;
}
.particle {
position: absolute;
top: 0;
left: 0;
height: .1em;
width: .1em;
border-radius: .05em;
background-color: var(--color);
--percentage: calc( var(--i) / var(--total-particles) );
--Θ: calc( var(--percentage) * 1turn );
transform: translate(-50%, -50%) rotate( var(--Θ) ) translateY(0) scaleY(0);
transition: all var(--duration) var(--easing);
.like-button:focus & {
animation: particles-out calc(var(--duration) * 1.2) var(--easing) forwards;
@keyframes particles-out {
50% {
height: .3em;
}
50%, 60% {
height: .3em;
transform:
translate(-50%, -50%)
rotate( var(--Θ) )
translateY(.8em)
scale(1)
;
}
60% {
height: .2em;
}
100% {
transform:
translate(-50%, -50%)
rotate( var(--Θ) )
translateY(1em)
scale(0)
;
}
}
}
}
.ripple {
height: 1em;
width: 1em;
border-radius: 50%;
overflow: hidden;
z-index: 1;
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: .4em solid var(--color-heart);
border-radius: inherit;
transform: scale(0);
}
.like-button:focus & {
&:before {
animation: ripple-out var(--duration) var(--easing);
@keyframes ripple-out {
from { transform: scale(0); }
to { transform: scale(5); }
}
}
}
}
body {
display: flex;
justify-content: center;
align-items: center;
background-color: var(--color-bg);
}
/* Reset the animation when clicking again! */
.like-button:focus {
pointer-events: none;
cursor: normal;
}
View Compiled
// Nope
// ...except to show the animation on load
let b = document.querySelector('button');
setTimeout(()=>b.focus(), 100);
setTimeout(()=>b.blur(), 1000);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.