GODRICH
#css-animation#button#loader#transition#scroll#dashboard#ui#design-trends

9 css button design ideas — candy, push, loading

css button design means deciding a button's colors, shape, and mouse reaction in a few lines of code.

Auto-plays · click a tile to jump to its section · all nine in one zip

The nine are not ranked by popularity; they follow the life of a button on a real screen. First the ones that catch the eye — candy gradient (01), shine sweep (02), gooey blob (03). Then the css button hover ones — border draw (04), icon slide (05), duotone wipe (06). Then the moment of the click — ripple ring (07), 3D push (08) — and finally the wait after the click, the pill loader (09). The first-steps-only cut of this same ground is 9 CSS Button Animations, and the hover techniques award sites keep reusing are quarried in 9 button hover effects css.

01Candy gradient flow button

What this css button design code draws

A candy-colored sheet three times wider than the button slides left by exactly one stripe period per loop. The only thing moving is a single translateX, so no layout is ever recalculated, and because the period is exactly 240px you never see the seam where the pattern repeats.

Candy gradient flow button
Open in a new tab

01 · Candy gradient flow button — Measured area 9.899% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

repeating-linear-gradienttranslateX(-240px) 한 주기overflow: hidden

Where to look in the code

It fits the sign-up button on a landing-page hero.

.btn__bg {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 720px;
  background: repeating-linear-gradient(to right,
    $color 0 58px, $candy-b 64px 118px, $candy-c 124px 178px, $color 184px 240px);
}
.stage.is-demo .btn__bg { animation: gradient-flow $duration $easing infinite; }
@keyframes gradient-flow {
  0%   { transform: translateX(0); }
  100% { transform: translateX(-240px); }
}

02Shine sweep button

What happens on screen

A tilted white band crosses the dark button once, like light glancing across glass. The band is an ::after pseudo-element, laid at an angle with skewX(-20deg) and moved only with translateX, so it keeps working no matter what width the button becomes.

Shine sweep button
Open in a new tab

02 · Shine sweep button — Measured area 10.349% · moving frames 9/23Original by GODRICH · SCSS and React ship in the zip · See the password

translateX(±150%)skewX(-20deg)overflow: hidden

How to read the code

Use it on checkout and buy buttons that deserve one more look.

.btn::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: -60%;
  width: 60%;
  background: linear-gradient(115deg,
    transparent 0%, rgba(255, 255, 255, .85) 50%, transparent 100%);
  transform: skewX(-20deg) translateX(-10%);
}
@keyframes sweep {
  0%, 10% { transform: skewX(-20deg) translateX(-10%); }
  55%     { transform: skewX(-20deg) translateX(310%); }
  100%    { transform: skewX(-20deg) translateX(310%); }
}

03Gooey blob button

What moves

Two mint circles hidden behind the label swell left and right, so the button's silhouette stretches and shrinks like jelly. The circles share the button's exact color, so their edges merge into one outline, and swelling is a single scale.

Gooey blob button
Open in a new tab

03 · Gooey blob button — Measured area 3.052% · moving frames 14/23Original by GODRICH · SCSS and React ship in the zip · See the password

::before·::after scaleborder-radius: 50%$ease-pop

What to change

A natural fit for an add-to-cart button on a kids' shop.

.btn::before, .btn::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 56px; height: 56px;
  border-radius: 50%;
  background: $color;
  transform: translateY(-50%) scale(.3);
}
.stage.is-demo .btn::before { animation: blob-l $duration $easing infinite; }
@keyframes blob-l {
  0%, 12% { transform: translateY(-50%) scale(.3); }
  40%     { transform: translateY(-50%) scale(1.3) translateX(-8px); }
  100%    { transform: translateY(-50%) scale(.3); }
}

04Border-draw button

What you see

The button's outline draws itself in, stroke by stroke, as if by hand. Giving the SVG rectangle a pathLength="100" attribute turns any real length into a hundred-unit ruler, so dropping stroke-dashoffset from 100 to 0 is all it takes to draw the border.

Border-draw button
Open in a new tab

04 · Border-draw button — Measured area 10.201% · moving frames 13/23Original by GODRICH · SCSS and React ship in the zip · See the password

stroke-dashoffsetpathLength=100vector-effect: non-scaling-stroke

Where to look in the code

A pale wash fills in right after, and that completes one cycle.

.btn__rect {
  fill: none;
  stroke: $color;
  stroke-width: 3;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
}
.stage.is-demo .btn__rect { animation: draw $duration $easing infinite; }
@keyframes draw {
  0%, 12% { stroke-dashoffset: 100; }
  55%     { stroke-dashoffset: 0; }
  100%    { stroke-dashoffset: 100; }
}

05Icon slide button

What happens on screen

Two cells sit side by side — one with the label, one with the label plus an arrow — and the whole track shifts by half its width. On hover the text slides out to the left while the arrow glides into its place from the right, and since the track is fixed at 200% width, no label length can break the alignment.

Icon slide button
Open in a new tab

05 · Icon slide button — Measured area 2.354% · moving frames 15/23Original by GODRICH · SCSS and React ship in the zip · See the password

격층 translateXoverflow: hidden$ease-spring

How to read the code

Good for read-more links that promise a next step.

.btn__track { display: flex; width: 200%; height: 100%; }
.btn__cell  { flex: 0 0 50%; display: flex; align-items: center; }
.stage.is-demo .btn__track { animation: slide-in $duration $easing infinite; }
@keyframes slide-in {
  0%, 18%  { transform: translateX(0); }
  55%, 72% { transform: translateX(-50%); }
  88%      { transform: translateX(-4%); }
  100%     { transform: translateX(0); }
}

06Duotone wipe button

What moves

The button splits into cream and peach, and the lower color unfolds from the left with scaleX, spreading across the whole face like paint. The label exists twice, stacked, and swaps with opacity, so it stays readable through the moment of the wipe.

Duotone wipe button
Open in a new tab

06 · Duotone wipe button — Measured area 10.103% · moving frames 16/23Original by GODRICH · SCSS and React ship in the zip · See the password

scaleX 레이어 와이프transform-origin: leftcolor 전환

What to change

Suits the get-started button on pricing cards.

.btn__wash {
  position: absolute;
  inset: 0;
  background: $color;
  transform: scaleX(0);
  transform-origin: left;
}
@keyframes wash {
  0%, 12%  { transform: scaleX(0); }
  48%, 70% { transform: scaleX(1); }
  100%     { transform: scaleX(0); }
}
.stage.is-demo .btn__t-on-dark { animation: swap-in $duration $easing infinite; }

07Ripple ring button

What you see

At the click, instead of a wave, an outlined ring spreads outward the way a bell's sound carries. A 3px ring sits in ::after and grows with scale while opacity erases it, so changing one color value drops it onto any icon button.

Ripple ring button
Open in a new tab

07 · Ripple ring button — Measured area 13.521% · moving frames 18/23Original by GODRICH · SCSS and React ship in the zip · See the password

::after 링 확산scale + opacity:active 트리거

Where to look in the code

The heart squashes and pops at the same instant, which makes it right for like and bookmark toggles.

.btn::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 3px solid $color;
  opacity: 0;
}
@keyframes ring {
  0%  { transform: scale(1);   opacity: .9; }
  34% { transform: scale(1.8); opacity: 0; }
  50% { transform: scale(1);   opacity: .9; }
  84% { transform: scale(1.8); opacity: 0; }
}

083D push-down button

What happens on screen

A hard 8px shadow is laid under the button so it reads as a physical key, and pressing it pushes the face down by that same 8px with translateY while the shadow vanishes. The loop adds a small rebound when it releases; in real use :active needs just two lines.

3D push-down button
Open in a new tab

08 · 3D push-down button — Measured area 5.983% · moving frames 12/23Original by GODRICH · SCSS and React ship in the zip · See the password

translateY 눌림box-shadow 두께:active 트리거

How to read the code

It belongs on game-start buttons and big tap targets in kids' apps.

$depth: 8px;
.btn {
  box-shadow: 0 $depth 0 darken($color, 22%),
              0 #{$depth + 6px} #{$sp-3} rgba(23, 20, 26, .22);
}
.btn:active { transform: translateY($depth); }
@keyframes press {
  0%, 20%  { transform: translateY(0); }
  35%, 55% { transform: translateY($depth); }
  70%      { transform: translateY(-3px); }
  85%,100% { transform: translateY(0); }
}

09Pill loader button

What moves

The moment you send a file, the label fades, the button deepens to a locked indigo, and inside its pill a short bar slides across twice. The bar moves only with translateX, and when the wait ends the label returns.

Pill loader button
Open in a new tab

09 · Pill loader button — Measured area 10.092% · moving frames 15/23Original by GODRICH · SCSS and React ship in the zip · See the password

라벨 opacity 스왑translateX 로더 막대$dur-loop 주기

What to change

In real use, toggling an is-busy class during your submit handler is all it takes.

.btn__bar {
  position: absolute;
  top: 0; bottom: 0;
  width: 60px;
  border-radius: $r-pill;
  background: $subject-cream;
  transform: translateX(-60px);
}
@keyframes bar-run {
  0%   { transform: translateX(-60px); }
  100% { transform: translateX(168px); }
}
@keyframes busy-bg {
  0%, 15%  { background: $color; }
  25%, 85% { background: $busy; }
  95%,100% { background: $color; }
}

Where it breaks — soft pastels are invisible to the machine

Item 01 was originally built the other way around: the background gradient itself was moved with background-position, and the palette was a softly blending pastel set. To the eye it clearly flowed, yet machine-measuring the rendered frames returned intensity 0.0 and zero moving frames. Between frames the gradient traveled 10px, and the color change across that distance stayed under the measurement threshold, so no pixel counted as changed. Redrawing it with crisp-edged stripes and moving the whole layer with translateX is what finally passed. The sweep distance in 02 is likewise set to several times the band width for the same reason. The archive passWhen it is the whole screen that waits instead of one button, 9 Loading Screen Designs takes over. The password word for the nine folders is 3vxxw8yh — type it exactly or the zip will not open. The gradient's stripe period (240px) must also match the travel distance exactly, which this item proved the hard way: off by as little as 4px and the pattern jumps once per cycle.

Accessibility

All nine fall back to animation: none under prefers-reduced-motion: reduce, and each one gets a still frame that still explains itself. Item 01 stops at a position where the stripes show, item 04 stops with the border fully drawn, and item 05 stops on the cell where label and arrow sit together. Item 09 parks the bar in the middle so color and shape still say "loading". Every button shows a :focus-visible outline for keyboard users. How to toggle the setting is documented in MDN's prefers-reduced-motion page.

FAQ

Can I use these without knowing JavaScript?

Yes — all nine run on CSS (SCSS) alone, without a single line of JavaScript. Even 09 only needs an is-busy class added and removed on the button in real use. If you want the basics like ripples and hovers, the 9 CSS button animations post covers them.

Where do I change these css button styles to my brand color?

The $color variable at the top of each item's SCSS is the base. Item 01 adds two helper colors besides $color, and item 08 derives its dark edge automatically with darken($color, 22%). In item 06, $color is the under-layer peach, so change the button's background line too if you want to replace the top color.

What is inside the zip?

Each of the nine items ships its index.html, style.scss, and compiled style.css, plus a React-ready .tsx and .module.scss pair. Duration, easing, and color are exposed as props, so tweaking a value turns any of them into a variant. You can find more button material through site search. The pieces that stand on a single icon are assembled in 9 Icon Button CSS Patterns.

Enter the archive password

The password is inside this article. You will find it as you read.