CSS
.cle-heartbeat {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 4px 6px;
color: #f0eeff;
font:
600 15px/1.2 system-ui,
sans-serif;
text-decoration: none;
border-bottom: 1px solid transparent;
transition:
border-color 0.25s,
color 0.25s;
}
.cle-heartbeat-dot {
position: relative;
width: 9px;
height: 9px;
border-radius: 50%;
background: #ff3d6e;
animation: cle-heartbeat-thump 1.6s ease-in-out infinite;
flex-shrink: 0;
}
.cle-heartbeat-dot::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: inherit;
animation: cle-heartbeat-ring 1.6s ease-out infinite;
}
.cle-heartbeat:hover,
.cle-heartbeat:focus-visible {
color: #fff;
border-bottom-color: rgba(255, 61, 110, 0.5);
}
/* Real heartbeat curve: lub (small bump) → DUB (big bump) → long pause */
@keyframes cle-heartbeat-thump {
0% {
transform: scale(1);
}
10% {
transform: scale(1.18);
}
20% {
transform: scale(1);
}
30% {
transform: scale(1.32);
}
45% {
transform: scale(1);
}
100% {
transform: scale(1);
}
}
@keyframes cle-heartbeat-ring {
0% {
transform: scale(1);
opacity: 0;
}
30% {
opacity: 0.55;
}
100% {
transform: scale(3);
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.cle-heartbeat-dot,
.cle-heartbeat-dot::after {
animation: none;
}
}