← Blog · Tutorial

10 Animated CSS Buttons

Hover each button to see it move, then hit Copy CSS and paste it straight into your own project. Every example is pure HTML + CSS — no libraries, no build step — so you can read exactly how the effect works and tweak it.

New to this? Each snippet includes the button's HTML and its CSS together. Paste the CSS into your stylesheet (or a <style> tag) and drop the HTML wherever you want the button. Change the colours, radius, and timing to make it yours.

A solid fill slides in from the left on hover, driven by a pseudo-element and a transform transition. A confident choice for primary calls to action.

<button class="btn-fill">Hover me</button>

.btn-fill {
  position: relative; overflow: hidden;
  padding: 14px 30px; border: 2px solid #091315; border-radius: 8px;
  background: transparent; color: #091315;
  font: 600 15px/1 'DM Sans', sans-serif; cursor: pointer;
  z-index: 0;
}
.btn-fill::before {                 /* the sliding fill layer */
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: #091315; transform: translateX(-100%);
  transition: transform .4s cubic-bezier(.75,0,.175,1);
}
.btn-fill:hover { color: #fff; }
.btn-fill:hover::before { transform: translateX(0); }

Two pseudo-elements grow from opposite corners to draw the border on hover — a crisp, minimal effect that suits outline and secondary buttons.

<button class="btn-border">Hover me</button>

.btn-border {
  position: relative;
  padding: 14px 30px; background: transparent; border: 0;
  color: #091315; font: 600 15px/1 'DM Sans', sans-serif; cursor: pointer;
}
.btn-border::before, .btn-border::after {
  content: ""; position: absolute; inset: 0;
  border: 2px solid transparent; border-radius: 8px;
  transition: border-color .25s ease, width .25s ease, height .25s ease;
  width: 0; height: 0;
}
.btn-border::before { top: 0; left: 0; }      /* draws top + right */
.btn-border::after  { bottom: 0; right: 0; }  /* draws bottom + left */
.btn-border:hover::before,
.btn-border:hover::after {
  width: 100%; height: 100%; border-color: #54734e;
}

A diagonal highlight glides across the surface on hover, created with a skewed gradient that slides past. It gives buttons a glossy, premium feel.

<button class="btn-shine">Hover me</button>

.btn-shine {
  position: relative; overflow: hidden;
  padding: 14px 30px; border: 0; border-radius: 8px;
  background: #54734e; color: #fff;
  font: 600 15px/1 'DM Sans', sans-serif; cursor: pointer;
}
.btn-shine::after {                 /* the moving glint */
  content: ""; position: absolute; top: 0; left: -120%;
  width: 60%; height: 100%;
  background: linear-gradient(120deg, transparent, rgba(255,255,255,.45), transparent);
  transform: skewX(-20deg);
  transition: left .6s ease;
}
.btn-shine:hover::after { left: 130%; }

A stacked box-shadow gives the button real height, then it presses down into that shadow when clicked. Tactile and playful — great for bold, game-like interfaces.

<button class="btn-push">Click me</button>

.btn-push {
  padding: 14px 30px; border: 0; border-radius: 8px;
  background: #54734e; color: #fff;
  font: 600 15px/1 'DM Sans', sans-serif; cursor: pointer;
  box-shadow: 0 6px 0 #2c482e;      /* the "thickness" underneath */
  transition: transform .12s ease, box-shadow .12s ease;
}
.btn-push:hover  { transform: translateY(-2px); box-shadow: 0 8px 0 #2c482e; }
.btn-push:active { transform: translateY(4px);  box-shadow: 0 2px 0 #2c482e; }

The label nudges over as an arrow slides in on hover, hinting at forward motion. Ideal for "next", "continue", or "get started" actions.

<button class="btn-arrow">Get started <span class="arrow">→</span></button>

.btn-arrow {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 14px 30px; border: 0; border-radius: 8px;
  background: #091315; color: #fff;
  font: 600 15px/1 'DM Sans', sans-serif; cursor: pointer;
}
.btn-arrow .arrow {
  display: inline-block; width: 0; opacity: 0;
  transform: translateX(-6px);
  transition: width .3s ease, opacity .3s ease, transform .3s ease;
}
.btn-arrow:hover .arrow { width: 16px; opacity: 1; transform: translateX(0); }

The gradient background pans on a continuous loop with a keyframe animation, so the button subtly moves even at rest. A lively option for hero and landing-page CTAs.

<button class="btn-gradient">Hover me</button>

.btn-gradient {
  padding: 14px 30px; border: 0; border-radius: 8px;
  color: #fff; font: 600 15px/1 'DM Sans', sans-serif; cursor: pointer;
  background: linear-gradient(90deg, #6a8d5f, #44603c, #8ba47d, #6a8d5f);
  background-size: 300% 100%;
  animation: btnGradientPan 5s linear infinite;
  transition: transform .2s ease;
}
.btn-gradient:hover { transform: scale(1.04); }
@keyframes btnGradientPan { to { background-position: 300% 0; } }

On hover the icon rolls away and a second icon — here a checkmark — rolls in to take its place. A compact way to preview what a click will do.

<button class="btn-iconswap">
  <span class="iconswap-box">
    <!-- default icon (e.g. a download arrow) -->
    <svg class="ico ico-out" ...></svg>
    <!-- hover icon (e.g. a checkmark) -->
    <svg class="ico ico-in" ...></svg>
  </span>
  Download
</button>

.btn-iconswap {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 14px 30px; border: 0; border-radius: 8px;
  background: #54734e; color: #fff;
  font: 600 15px/1 'DM Sans', sans-serif; cursor: pointer;
}
.iconswap-box {                 /* fixed viewport that clips the icons */
  position: relative; overflow: hidden;
  width: 18px; height: 18px;
}
.iconswap-box .ico {
  position: absolute; inset: 0; width: 18px; height: 18px;
  transition: transform .3s cubic-bezier(.65,0,.35,1);
}
.iconswap-box .ico-in { transform: translateY(-120%); }        /* waits above */
.btn-iconswap:hover .ico-out { transform: translateY(120%); }  /* drops out   */
.btn-iconswap:hover .ico-in  { transform: translateY(0); }     /* drops in    */

The current label rolls up out of view while a new one rolls in from below, clipped by the button's edges. A fun touch for confirmations and downloads.

<button class="btn-swap">
  <span class="swap-out">Download</span>
  <span class="swap-in">Let's go!</span>
</button>

.btn-swap {
  position: relative; overflow: hidden;   /* clips the labels as they roll */
  display: inline-grid;                    /* stack both labels in one cell */
  border: 0; border-radius: 8px;
  background: #091315; color: #fff;
  font: 600 15px/1 'DM Sans', sans-serif; cursor: pointer;
  transition: background .35s ease;        /* darkens to green on hover */
}
.btn-swap:hover { background: #54734e; }
/* Padding lives on the LABEL so each one is the full button height —
   translateY(100%) then moves it completely out of view (no overlap). */
.btn-swap span {
  grid-area: 1 / 1;
  display: grid; place-items: center;
  padding: 14px 30px;
  transition: transform .35s cubic-bezier(.65,0,.35,1);
}
.btn-swap .swap-in { transform: translateY(100%); }   /* waits fully below */
.btn-swap:hover .swap-out { transform: translateY(-100%); }
.btn-swap:hover .swap-in  { transform: translateY(0); }

A soft glow expands around the button on hover using layered box-shadows, for a neon, dark-mode look. Designed to sit on dark backgrounds.

<button class="btn-glow">Hover me</button>

.btn-glow {
  padding: 14px 30px; border: 2px solid #8ba47d; border-radius: 8px;
  background: transparent; color: #8ba47d;
  font: 600 15px/1 'DM Sans', sans-serif; cursor: pointer;
  text-shadow: 0 0 6px rgba(139,164,125,.6);
  transition: box-shadow .3s ease, background .3s ease, color .3s ease;
}
.btn-glow:hover {
  background: #8ba47d; color: #0e1512;
  box-shadow: 0 0 12px #8ba47d, 0 0 28px rgba(139,164,125,.6);
}

A centred underline expands outward on hover — a quiet, text-first effect that works well for link-style or tertiary buttons.

<button class="btn-underline">Read more</button>

.btn-underline {
  position: relative;
  padding: 14px 4px; border: 0; background: transparent;
  color: #091315; font: 600 16px/1 'DM Sans', sans-serif; cursor: pointer;
}
.btn-underline::after {
  content: ""; position: absolute; left: 50%; bottom: 6px;
  width: 0; height: 2px; background: #54734e;
  transform: translateX(-50%);
  transition: width .3s ease;
}
.btn-underline:hover::after { width: 100%; }

Frequently asked questions

How do I add an animated button to my website?

Copy the CSS for the button you like and paste it into your stylesheet (or a <style> tag), then add the matching HTML button element where you want it to appear. Every example on this page includes both the HTML and the CSS together, so it works as a drop-in with no extra setup.

Do these animated buttons need JavaScript?

No. All ten buttons are built with pure HTML and CSS. The animations use CSS transitions, transforms, pseudo-elements and keyframes, so they run in every modern browser without any JavaScript or libraries.

Are these CSS buttons free to use?

Yes. You're free to copy, paste, edit and use every snippet in personal and commercial projects. No attribution is required.

How do I change a button's colour or size?

Edit the values in the CSS: change the background and color for colours, padding for size, border-radius for roundness, and the transition or animation duration to speed the effect up or slow it down.

Do CSS hover effects work on mobile?

Hover doesn't exist on touchscreens, so hover-triggered effects generally fire on tap instead. Effects that animate on their own (like the animated gradient) work everywhere. For key actions, pair a hover effect with an :active or :focus state so touch users get feedback too.

Copied to clipboard ✓