20 CSS Animated Buttons 02 / 20
CSS Background Slide Hover Button
Four directional background-slide hover buttons — left, right, top, and bottom — where a solid fill sweeps across the button face on hover using CSS pseudo-element transforms.
The code
<div class="ab-02">
<p class="ab-02__title">Background Slide Directions</p>
<div class="ab-02__grid">
<button class="ab-02__btn ab-02__btn--left"><span>Slide Left</span></button>
<button class="ab-02__btn ab-02__btn--right"><span>Slide Right</span></button>
<button class="ab-02__btn ab-02__btn--top"><span>Slide Top</span></button>
<button class="ab-02__btn ab-02__btn--bottom"><span>Slide Bottom</span></button>
</div>
</div> <div class="ab-02">
<p class="ab-02__title">Background Slide Directions</p>
<div class="ab-02__grid">
<button class="ab-02__btn ab-02__btn--left"><span>Slide Left</span></button>
<button class="ab-02__btn ab-02__btn--right"><span>Slide Right</span></button>
<button class="ab-02__btn ab-02__btn--top"><span>Slide Top</span></button>
<button class="ab-02__btn ab-02__btn--bottom"><span>Slide Bottom</span></button>
</div>
</div>.ab-02,.ab-02 *,.ab-02 *::before,.ab-02 *::after{box-sizing:border-box;margin:0;padding:0}
.ab-02 ::selection{background:#6c63ff;color:#fff}
.ab-02{
--fill:#6c63ff;
--text:#1a1a2e;
--border:#6c63ff;
font-family:system-ui,sans-serif;
background:#f0eeff;
min-height:100vh;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
gap:2rem;
padding:2rem;
}
.ab-02__title{
font-size:.8rem;
font-weight:600;
letter-spacing:.12em;
text-transform:uppercase;
color:#6c63ff;
opacity:.7;
}
.ab-02__grid{
display:grid;
grid-template-columns:1fr 1fr;
gap:1rem;
}
.ab-02__btn{
position:relative;
overflow:hidden;
padding:.8rem 2rem;
font-size:.95rem;
font-weight:700;
color:var(--fill);
background:transparent;
border:2px solid var(--border);
border-radius:8px;
cursor:pointer;
outline:none;
transition:color .35s;
letter-spacing:.03em;
}
.ab-02__btn span{
position:relative;
z-index:1;
}
.ab-02__btn::before{
content:'';
position:absolute;
inset:0;
background:var(--fill);
transition:transform .35s cubic-bezier(.4,0,.2,1);
z-index:0;
}
.ab-02__btn:hover{color:#fff}
.ab-02__btn--left::before{transform:translateX(-100%)}
.ab-02__btn--left:hover::before{transform:translateX(0)}
.ab-02__btn--right::before{transform:translateX(100%)}
.ab-02__btn--right:hover::before{transform:translateX(0)}
.ab-02__btn--top::before{transform:translateY(-100%)}
.ab-02__btn--top:hover::before{transform:translateY(0)}
.ab-02__btn--bottom::before{transform:translateY(100%)}
.ab-02__btn--bottom:hover::before{transform:translateY(0)}
.ab-02__btn:active{transform:scale(.97)}
@media(prefers-reduced-motion:reduce){
.ab-02__btn::before{transition:none}
.ab-02__btn{transition:none}
} .ab-02,.ab-02 *,.ab-02 *::before,.ab-02 *::after{box-sizing:border-box;margin:0;padding:0}
.ab-02 ::selection{background:#6c63ff;color:#fff}
.ab-02{
--fill:#6c63ff;
--text:#1a1a2e;
--border:#6c63ff;
font-family:system-ui,sans-serif;
background:#f0eeff;
min-height:100vh;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
gap:2rem;
padding:2rem;
}
.ab-02__title{
font-size:.8rem;
font-weight:600;
letter-spacing:.12em;
text-transform:uppercase;
color:#6c63ff;
opacity:.7;
}
.ab-02__grid{
display:grid;
grid-template-columns:1fr 1fr;
gap:1rem;
}
.ab-02__btn{
position:relative;
overflow:hidden;
padding:.8rem 2rem;
font-size:.95rem;
font-weight:700;
color:var(--fill);
background:transparent;
border:2px solid var(--border);
border-radius:8px;
cursor:pointer;
outline:none;
transition:color .35s;
letter-spacing:.03em;
}
.ab-02__btn span{
position:relative;
z-index:1;
}
.ab-02__btn::before{
content:'';
position:absolute;
inset:0;
background:var(--fill);
transition:transform .35s cubic-bezier(.4,0,.2,1);
z-index:0;
}
.ab-02__btn:hover{color:#fff}
.ab-02__btn--left::before{transform:translateX(-100%)}
.ab-02__btn--left:hover::before{transform:translateX(0)}
.ab-02__btn--right::before{transform:translateX(100%)}
.ab-02__btn--right:hover::before{transform:translateX(0)}
.ab-02__btn--top::before{transform:translateY(-100%)}
.ab-02__btn--top:hover::before{transform:translateY(0)}
.ab-02__btn--bottom::before{transform:translateY(100%)}
.ab-02__btn--bottom:hover::before{transform:translateY(0)}
.ab-02__btn:active{transform:scale(.97)}
@media(prefers-reduced-motion:reduce){
.ab-02__btn::before{transition:none}
.ab-02__btn{transition:none}
}How this works
Each button contains a ::before pseudo-element that covers the full button area and holds the hover fill colour. By default the pseudo-element is pushed out of view using transform: translateX(-100%) (left variant), translateX(100%) (right), translateY(-100%) (top), or translateY(100%) (bottom). On :hover the transform resets to translate(0) which slides the fill into place over a transition of 0.35 s with a smooth ease curve.
The button text sits in a position: relative; z-index: 1 span so it always renders above the sliding fill layer. overflow: hidden on the button itself clips the pseudo-element as it enters from its starting edge, making the slide feel contained and intentional. Only transform is animated, so the browser compositor handles all motion without repaints.
Customize
- Change the slide direction by swapping the initial
transformon::before— usetranslateY(-100%)for a top-down sweep. - Adjust slide speed by editing the
transitionduration on.ab-02__btn::before(default.35s; try.2sfor snappier feel). - Use a gradient fill instead of a solid by replacing the
backgroundon::beforewithlinear-gradient(135deg, #6c63ff, #c471ed). - Change text colour on hover by adding a
transition: color .35sto the button and setting.ab-02__btn:hover { color: #fff }. - Stack two pseudo-elements (
::beforeand::after) with offset delays to create a two-tone layered slide effect.
Watch out for
- The
overflow: hiddenon the button clipsbox-shadow— move the shadow to a wrapper element if you need both an outer glow and the slide effect. - If the button has
border-radius, the pseudo-element corners are correctly clipped, but verify on Safari < 15 where overflow clipping with transforms occasionally mis-clips. - Avoid
width: autoon::before— always use100%explicitly, as percentage widths in absolutely-positioned pseudo-elements can calculate incorrectly inside flex or grid containers.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 49+ | 9.1+ | 44+ | 49+ |
Universally supported; relies only on CSS transforms and pseudo-elements.