A solid '+ Follow' button that turns into a quiet outlined 'Following' when clicked, with the plus folding into a check. The two strokes of the plus rotate and resize into the two legs of the check.
Also known as: follow unfollow button css, following button toggle, follow button animation.
<label class="btn-follow">
<input class="btn-follow-input" type="checkbox" aria-label="Follow">
<span class="btn-follow-face" aria-hidden="true">
<span class="btn-follow-icon"><i></i><i></i></span>
<span class="btn-follow-text"><span>Follow</span><span>Following</span></span>
</span>
</label>.btn-follow {
--c: var(--btn-color, #3b82f6);
position: relative;
display: inline-grid;
place-items: center;
font: 600 var(--btn-size, 18px)/1.1 system-ui, sans-serif;
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
.btn-follow-input {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
cursor: pointer;
}
.btn-follow-face {
display: inline-flex;
align-items: center;
gap: 0.5em;
padding: 0.7em 1.35em 0.7em 1.1em;
border-radius: 2em;
background-color: var(--c);
color: #fff;
box-shadow: inset 0 0 0 2px var(--c);
transition: background-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease, transform 0.2s ease;
}
.btn-follow-icon {
position: relative;
width: 1em;
height: 1em;
}
.btn-follow-icon i {
position: absolute;
top: 50%;
left: 50%;
width: 0.8em;
height: 0.16em;
margin: -0.08em 0 0 -0.4em;
border-radius: 0.1em;
background: currentColor;
transition: transform 0.4s cubic-bezier(0.3, 1.5, 0.5, 1);
}
.btn-follow-icon i:first-child { transform: rotate(90deg); }
.btn-follow-text { display: grid; }
.btn-follow-text span {
grid-area: 1 / 1;
transition: opacity 0.25s ease, transform 0.35s cubic-bezier(0.3, 0.7, 0.4, 1);
}
.btn-follow-text span:last-child {
opacity: 0;
transform: translateY(0.6em);
}
.btn-follow:active .btn-follow-face { transform: scale(0.95); }
.btn-follow-input:focus-visible ~ .btn-follow-face {
outline: 2px solid var(--c);
outline-offset: 0.2em;
}
.btn-follow-input:checked ~ .btn-follow-face {
background-color: transparent;
color: var(--c);
box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--c) 40%, transparent);
}
.btn-follow-input:checked ~ .btn-follow-face .btn-follow-icon i:first-child {
transform: translate(-0.24em, 0.14em) rotate(45deg) scaleX(0.45);
}
.btn-follow-input:checked ~ .btn-follow-face .btn-follow-icon i:last-child {
transform: translate(0.1em, -0.02em) rotate(-50deg) scaleX(0.95);
}
.btn-follow-input:checked ~ .btn-follow-face .btn-follow-text span:first-child {
opacity: 0;
transform: translateY(-0.6em);
}
.btn-follow-input:checked ~ .btn-follow-face .btn-follow-text span:last-child {
opacity: 1;
transform: none;
}
@media (prefers-reduced-motion: reduce) {
.btn-follow, .btn-follow *, .btn-follow::before, .btn-follow::after, .btn-follow *::before, .btn-follow *::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.