SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/button-hover-effects

7 CSS Button Hover Effects

6 Pure CSS1 with JSPublished 2026-10-05Free · no sign-up

Seven hover effects for buttons: a fill that rises from the bottom, a label that slides away for an arrow, corner borders that grow into a frame, a hard offset shadow that presses flat, colour that closes in from both sides, a button that is pulled toward the pointer and a slanted fill.

01. Fill from the Bottom

ButtonHover

An outlined button fills with colour from the bottom edge up on hover and focus, and the text colour flips to stay readable. The fill is a pseudo-element scaled on the y axis, kept behind the label with a negative z-index inside an isolated button.

View code
<button class="bh1">Explore the range</button>
.bh1 { position: relative; isolation: isolate; overflow: hidden; padding: 14px 26px; border: 2px solid #0f766e; border-radius: 10px; background: transparent; color: #0f766e; font: 700 15px Inter, system-ui, sans-serif; cursor: pointer; transition: color .3s; }
.bh1::before { content: ""; position: absolute; inset: 0; z-index: -1; background: #0f766e; transform: scaleY(0); transform-origin: bottom; transition: transform .3s ease; }
.bh1:hover, .bh1:focus-visible { color: #fff; }
.bh1:hover::before, .bh1:focus-visible::before { transform: scaleY(1); }
.bh1:focus-visible { outline: 3px solid #5eead4; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .bh1::before { transition: none; } }

02. Label Slides Out, Icon Slides In

ButtonIcon

On hover the label slides off to the right and an arrow slides in from the left to take its place. Both live in the button the whole time; the arrow is aria-hidden, so the accessible name is always the label text.

View code
<button class="bh2"><span class="l">Next step</span><span class="i" aria-hidden="true">&rarr;</span></button>
.bh2 { position: relative; overflow: hidden; min-width: 150px; padding: 14px 26px; border: 0; border-radius: 999px; background: #e2e8f0; color: #0f172a; font: 700 15px Inter, system-ui, sans-serif; cursor: pointer; }
.bh2 span { display: block; transition: transform .3s cubic-bezier(.2,.8,.2,1); }
.bh2 .i { position: absolute; inset: 0; display: grid; place-items: center; font-size: 20px; transform: translateX(-100%); }
.bh2:hover .l, .bh2:focus-visible .l { transform: translateX(150%); }
.bh2:hover .i, .bh2:focus-visible .i { transform: none; }
.bh2:focus-visible { outline: 3px solid #93c5fd; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .bh2 span { transition: none; } }

03. Corner Borders Meet on Hover

ButtonBorder

Two L-shaped borders sit in opposite corners and grow along the edges until they meet, forming a full frame. They are the button's ::before and ::after with only two borders each, and their width and height transition together.

View code
<button class="bh3">View collection</button>
.bh3 { position: relative; padding: 15px 28px; border: 0; background: transparent; color: #f8fafc; font: 700 15px Inter, system-ui, sans-serif; letter-spacing: .04em; cursor: pointer; }
.bh3::before, .bh3::after { content: ""; position: absolute; width: 14px; height: 14px; border: 2px solid #f59e0b; transition: width .35s ease, height .35s ease; }
.bh3::before { top: 0; left: 0; border-right: 0; border-bottom: 0; }
.bh3::after { right: 0; bottom: 0; border-left: 0; border-top: 0; }
.bh3:hover::before, .bh3:hover::after, .bh3:focus-visible::before, .bh3:focus-visible::after { width: calc(100% - 2px); height: calc(100% - 2px); }
.bh3:focus-visible { outline: 2px dashed #fcd34d; outline-offset: 6px; }
@media (prefers-reduced-motion: reduce) { .bh3::before, .bh3::after { transition: none; } }

04. Offset Shadow Press

ButtonBrutalist

A bold neo-brutalist button with a hard offset shadow. Hover pulls it further from its shadow and pressing pushes it flat into the shadow, so the button feels physical. The shadow is a box-shadow with no blur.

View code
<button class="bh4">Get the kit</button>
.bh4 { padding: 14px 26px; border: 2px solid #111; border-radius: 8px; background: #a3e635; color: #111; font: 800 16px Inter, system-ui, sans-serif; cursor: pointer; box-shadow: 4px 4px 0 #111; transition: transform .15s, box-shadow .15s; }
.bh4:hover, .bh4:focus-visible { transform: translate(-2px, -2px); box-shadow: 6px 6px 0 #111; }
.bh4:active { transform: translate(4px, 4px); box-shadow: 0 0 0 #111; }
.bh4:focus-visible { outline: 3px solid #2563eb; outline-offset: 4px; }
@media (prefers-reduced-motion: reduce) { .bh4 { transition: none; } }

05. Split Fill from the Middle

ButtonHover

Two halves of colour slide in from both sides and meet in the middle on hover. Each half is a pseudo-element half the button's width, scaled from its own outer edge, which gives a curtain-closing feel.

View code
<button class="bh5">Start building</button>
.bh5 { position: relative; isolation: isolate; overflow: hidden; padding: 14px 28px; border: 2px solid #a78bfa; border-radius: 10px; background: transparent; color: #ddd6fe; font: 700 15px Inter, system-ui, sans-serif; cursor: pointer; transition: color .3s; }
.bh5::before, .bh5::after { content: ""; position: absolute; top: 0; bottom: 0; width: 51%; z-index: -1; background: #7c3aed; transform: scaleX(0); transition: transform .3s ease; }
.bh5::before { left: 0; transform-origin: left; } .bh5::after { right: 0; transform-origin: right; }
.bh5:hover, .bh5:focus-visible { color: #fff; }
.bh5:hover::before, .bh5:hover::after, .bh5:focus-visible::before, .bh5:focus-visible::after { transform: scaleX(1); }
.bh5:focus-visible { outline: 3px solid #c4b5fd; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .bh5::before, .bh5::after { transition: none; } }

06. Magnetic Button

ButtonJavaScript

The button leans toward the pointer as it approaches and springs back when it leaves, a playful effect for one hero action. Script moves it a fraction of the pointer's distance from its centre with transform, and does nothing when reduced motion is set.

View code
<div class="bh6"><button>Let’s talk</button></div>
.bh6 { display: grid; place-items: center; width: 260px; max-width: 100%; height: 160px; }
.bh6 button { padding: 18px 32px; border: 0; border-radius: 999px; background: #101828; color: #fff; font: 700 16px Inter, system-ui, sans-serif; cursor: pointer; transition: transform .25s cubic-bezier(.2,.8,.2,1); }
.bh6 button:focus-visible { outline: 3px solid #84adff; outline-offset: 3px; }
var area = document.querySelector('.bh6'), b = area.querySelector('button');
if (!matchMedia('(prefers-reduced-motion: reduce)').matches) {
  area.addEventListener('pointermove', function (e) { var r = b.getBoundingClientRect(), x = e.clientX - (r.left + r.width / 2), y = e.clientY - (r.top + r.height / 2); b.style.transform = 'translate(' + x * .25 + 'px,' + y * .35 + 'px)'; });
  area.addEventListener('pointerleave', function () { b.style.transform = ''; });
}

07. Diagonal Skewed Fill

ButtonHover

A slanted block of colour slides across the button from the left, with its leading edge angled by skewX. The block starts outside the button, wider than it so the slant never leaves a gap, and overflow: hidden trims it to the rounded shape.

View code
<button class="bh7">Watch the film</button>
.bh7 { position: relative; isolation: isolate; overflow: hidden; padding: 14px 28px; border: 2px solid #f43f5e; border-radius: 6px; background: transparent; color: #fecdd3; font: 700 15px Inter, system-ui, sans-serif; cursor: pointer; transition: color .35s; }
.bh7::before { content: ""; position: absolute; top: 0; bottom: 0; left: -20%; width: 140%; z-index: -1; background: #e11d48; transform: translateX(-100%) skewX(-25deg); transition: transform .4s cubic-bezier(.7,0,.3,1); }
.bh7:hover, .bh7:focus-visible { color: #fff; }
.bh7:hover::before, .bh7:focus-visible::before { transform: translateX(0) skewX(-25deg); }
.bh7:focus-visible { outline: 3px solid #fda4af; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .bh7::before { transition: none; } }

About these CSS Button Hover Effects

Fills behind the label

Put the fill in a pseudo-element with z-index: -1 and give the button isolation: isolate. The fill then sits above the button's background but below its text, without wrapping the label in another element.

Hover is a bonus

Touch screens do not hover, so the button must already look like a button at rest. Copy every hover state to :focus-visible and keep a clear outline for keyboard focus.

Check contrast in both states

When the fill changes the background, the text colour must change with it. Test the label against the starting background and the filled one.

Short and on transform

Hover transitions of 200 to 350ms feel responsive. Animate transform, opacity and colour; avoid animating padding, width or border width, which shift the layout.

Frequently asked questions

Can I combine these effects?

Yes, but keep one idea per button. Two competing motions on hover feel busy.

Is the magnetic effect accessible?

It only moves the button visually and is disabled for reduced motion; the button works normally with keyboard and touch.

Where are the buttons from the main buttons page?

The CSS Buttons collection has base styles; this page is about hover motion only.