Button with Rotating Icon
A CSS-only button where hover rotates the icon background 90°, slides a list of three arrow SVGs to reveal a fresh arrow, lifts the text via text-shadow, and sweeps a background panel up from below. All animated targets share a single transition rule via a data attribute selector.
Code
<div class="btn-group">
<a href="#" class="btn-icon-link w-inline-block">
<div class="btn-icon-content">
<div class="btn-icon-content__mask">
<span data-button-anim-target="" class="btn-icon-content__text">Button with Icon</span>
</div>
<div data-icon-size="normal" class="btn-icon-icon">
<div data-button-anim-target="" class="btn-icon-icon__bg"></div>
<div class="btn-icon-icon__wrap">
<div class="btn-icon-icon__list">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 10 8" fill="none" data-button-anim-target="" class="btn-icon-icon__arrow"><path d="M4.45231 0.385986H6.02531L9.30131 3.99999L6.02531 7.61399H4.45231L7.40331 4.58499H0.695312V3.42799H7.41631L4.45231 0.385986Z" fill="currentColor"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 10 8" fill="none" data-button-anim-target="" class="btn-icon-icon__arrow"><path d="M4.45231 0.385986H6.02531L9.30131 3.99999L6.02531 7.61399H4.45231L7.40331 4.58499H0.695312V3.42799H7.41631L4.45231 0.385986Z" fill="currentColor"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 10 8" fill="none" data-button-anim-target="" class="btn-icon-icon__arrow"><path d="M4.45231 0.385986H6.02531L9.30131 3.99999L6.02531 7.61399H4.45231L7.40331 4.58499H0.695312V3.42799H7.41631L4.45231 0.385986Z" fill="currentColor"></path></svg>
</div>
</div>
</div>
<div data-button-anim-target="" class="btn-icon-content__bg"></div>
</div>
</a>
<a href="#" class="btn-icon-link w-inline-block">
<div class="btn-icon-content__mask">
<span data-button-anim-target="" class="btn-icon-content__text">Button with Icon</span>
</div>
<div data-icon-size="normal" class="btn-icon-icon">
<div data-button-anim-target="" class="btn-icon-icon__bg"></div>
<div class="btn-icon-icon__wrap color--white">
<div class="btn-icon-icon__list">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 10 8" fill="none" data-button-anim-target="" class="btn-icon-icon__arrow"><path d="M4.45231 0.385986H6.02531L9.30131 3.99999L6.02531 7.61399H4.45231L7.40331 4.58499H0.695312V3.42799H7.41631L4.45231 0.385986Z" fill="currentColor"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 10 8" fill="none" data-button-anim-target="" class="btn-icon-icon__arrow"><path d="M4.45231 0.385986H6.02531L9.30131 3.99999L6.02531 7.61399H4.45231L7.40331 4.58499H0.695312V3.42799H7.41631L4.45231 0.385986Z" fill="currentColor"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 10 8" fill="none" data-button-anim-target="" class="btn-icon-icon__arrow"><path d="M4.45231 0.385986H6.02531L9.30131 3.99999L6.02531 7.61399H4.45231L7.40331 4.58499H0.695312V3.42799H7.41631L4.45231 0.385986Z" fill="currentColor"></path></svg>
</div>
</div>
</div>
</a>
</div>.btn-group {
grid-column-gap: 3em;
grid-row-gap: 3em;
justify-content: center;
display: flex;
}
.btn-icon-link {
grid-column-gap: .5em;
grid-row-gap: .5em;
color: #a29a65;
font-size: 1em;
line-height: 1.2;
text-decoration: none;
display: flex;
}
.btn-icon-icon {
z-index: 1;
flex: none;
justify-content: center;
align-items: center;
width: 1em;
height: 1em;
display: flex;
position: relative;
}
.btn-icon-icon__bg {
background-color: currentColor;
border-radius: .125em;
width: 100%;
height: 100%;
position: absolute;
}
.btn-icon-icon__wrap {
color: #a29a65;
justify-content: flex-end;
align-items: center;
width: 100%;
height: 100%;
display: flex;
position: relative;
overflow: hidden;
}
.btn-icon-icon__wrap.color--white {
color: #fff;
}
.btn-icon-icon__list {
flex: none;
justify-content: flex-start;
align-items: center;
height: 100%;
display: flex;
}
.btn-icon-icon__arrow {
flex: none;
width: 1em;
height: 100%;
padding: .2em;
}
.btn-icon-content {
grid-column-gap: .5em;
grid-row-gap: .5em;
color: #fff;
background-color: #a29a65;
border-radius: .25em;
justify-content: flex-start;
align-items: center;
padding: .6125em .75em;
display: flex;
position: relative;
overflow: hidden;
}
.btn-icon-content__text {
font-size: .875em;
}
.btn-icon-content__mask {
z-index: 1;
flex: none;
justify-content: flex-start;
align-items: center;
display: flex;
position: relative;
overflow: hidden;
}
.btn-icon-content__bg {
z-index: 0;
background-color: #302d1c;
width: 120%;
height: 100%;
position: absolute;
bottom: 0%;
left: -10%;
transform: translate(0, 175%) rotate(15deg);
}
/* Global attribute to easily control easing and timing of all targeted elements */
[data-button-anim-target] {
transition: transform 0.525s cubic-bezier(0.625, 0.05, 0, 1);
}
/* Fake a duplicate text element using text-shadow without blur */
.btn-icon-content__text {
--text-duplicate-distance: 1.5em;
text-shadow: 0px var(--text-duplicate-distance) currentColor;
}
/* Only apply hover animations on devices that support hover */
@media (hover: hover) and (pointer: fine) {
.btn-icon-link:hover .btn-icon-content__text { transform: translate(0px, calc(-1 * var(--text-duplicate-distance))); }
.btn-icon-link:hover .btn-icon-icon__bg { transform: rotate(90deg); }
.btn-icon-link:hover .btn-icon-icon__arrow { transform: translate(200%, 0px); }
.btn-icon-link:hover .btn-icon-content__bg { transform: translate(0px, 0%) rotate(0deg); }
}Guide
Overview
Four independent animations fire together on hover: the icon background rotates 90°, the arrow list translates 200% to reveal a fresh arrow from the overflow-hidden container, the text slides up to show its text-shadow duplicate, and the background panel sweeps up from below.
Shared Transition
All animated elements have data-button-anim-target. The single [data-button-anim-target] rule sets the transition for every target at once, so you control timing and easing from one place.
Text Duplicate
The text uses text-shadow with 0 blur at --text-duplicate-distance below. On hover the span shifts up by that distance, pulling the shadow into view while the original exits above the overflow:hidden mask.
Arrow List
Three identical arrow SVGs sit in a horizontal flex row inside an overflow:hidden container. On hover all three translate 200% to the right, making the first arrow exit and the second slide into the visible area — giving the illusion of a new arrow arriving.
Icon Colors
Arrow fills use currentColor, so changing color on .btn-icon-icon__wrap changes the arrows. Add .color--white to the wrap for white arrows. Adjust the hover color in the same selector if needed.
Text-Only Variant
Pull .btn-icon-content__mask and .btn-icon-icon outside of .btn-icon-content to get a text-link-only layout with no background pill. The .btn-icon-content__bg wipe is also optional — remove it for a simpler look.