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

9 Micro Interactions CSS — One Line, Copy-Paste

Search for micro interactions css and the answer is always the same shape: a small, self-contained bit of motion — a button press, an icon swap, a toast

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

The grid above cycles through all nine on its own — watch it rather than trying to click anything, since each preview sits inside a fixed, non-scrollable iframe built only to loop. The nine below move from a single element noticing its own click (01–03), through state changes an interface makes on its own (04–06), into interactions that coordinate two or more elements at once (07–09). Each group adds one more moving part, so this is not the same trick repeated nine times. Scaling a state change up to the whole screen is what the CSS dark mode toggle animations handle, and floating a short explanation next to the control instead is the CSS tooltip bubbles set.

01Button press scale

What it does

The button scales down to 0.96 the instant it is pressed and springs back on release, so a tap reads as physically registered before any server response arrives.

Button press scale
Open in a new tab

01 · Button press scale — Measured area 1.256% · moving frames 7/23Original by GODRICH · SCSS and React ship in the zip · See the password

scale(0.96)스프링 이징active 상태

When css micro interactions need a press scale

Reach for it on submit buttons, clickable cards, and icon buttons — anywhere a plain hover color change is not enough proof that the click landed.

.btn {
  transform: scale(1);
  transition: transform $duration $easing, box-shadow $duration $easing;

  &:active {
    transform: scale(.96);
    box-shadow: $shadow-press;
  }
}

02Save-to-check animation

What you see

Clicking the save icon morphs the diskette outline into a checkmark while a colored ring pops in behind it, so the confirmation lives inside the icon itself instead of a separate toast.

Save-to-check animation
Open in a new tab

02 · Save-to-check animation — Measured area 0.583% · moving frames 11/23Original by GODRICH · SCSS and React ship in the zip · See the password

scaleX 획 긋기색상 전환0.3초

Where it fits

Form autosave buttons and note-app save indicators both suit it, since the icon is already where someone is looking when they press save.

&.is-saved &__ring  { animation: ring-pop $duration $easing forwards; }
&.is-saved &__disk  { animation: disk-out $duration $easing forwards; }
&.is-saved &__check-bar--short { animation: check-short $duration $easing forwards; animation-delay: $duration * .35; }
&.is-saved &__check-bar--long  { animation: check-long $duration $easing forwards; animation-delay: $duration * .5; }

@keyframes ring-pop  { from { opacity: 0; transform: scale(.4); } to { opacity: 1; transform: scale(1); } }
@keyframes disk-out  { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(.5); } }
@keyframes check-short { to { transform: rotate(45deg) scaleX(1); } }
@keyframes check-long  { to { transform: rotate(-52deg) scaleX(1); } }

03Copy-done feedback

The motion

Pressing the copy icon swaps it for a checkmark and floats a small tooltip up above the button before fading it out 1.2 seconds later.

Copy-done feedback
Open in a new tab

03 · Copy-done feedback — Measured area 1.494% · moving frames 14/23Original by GODRICH · SCSS and React ship in the zip · See the password

아이콘 스왑툴팁 페이드1.2초 유지

Good places for it

Code-block copy buttons and API-key copy buttons both need it, since the icon alone sits too small and too far from the cursor for a plain color change to register.

&.is-copied &__sheet--front { opacity: 0; transform: scale(.6); transition-delay: 0s; }
&.is-copied &__check { animation: check-in $duration $easing forwards; }
&.is-copied &__tip { animation: tip-float 1.4s $easing forwards; }

@keyframes tip-float {
  0%   { opacity: 0; transform: translate(-50%, 4px); }
  15%  { opacity: 1; transform: translate(-50%, -2px); }
  85%  { opacity: 1; transform: translate(-50%, -2px); }
  100% { opacity: 0; transform: translate(-50%, -10px); }
}

04Optimistic like with rollback

What it does

Tapping the heart fills it in red and bumps the count instantly, without waiting on a network response, and the loop below simulates a failed request by quietly rolling both back 0.8 seconds later.

Optimistic like with rollback
Open in a new tab

04 · Optimistic like with rollback — Measured area 0.354% · moving frames 9/23Original by GODRICH · SCSS and React ship in the zip · See the password

낙관적 업데이트롤백 애니메이션0.8초

When to use it

Social apps use this same fill-then-maybe-rollback shape for every like and bookmark toggle, since waiting on the server first would make every tap feel a beat slow.

var b = document.querySelector('.like');
b.addEventListener('click', function () {
  b.classList.remove('is-demo');
  var liked = b.classList.toggle('is-liked');
  b.setAttribute('aria-pressed', liked);
  if (liked) setTimeout(function () { b.classList.remove('is-liked'); b.setAttribute('aria-pressed', 'false'); }, 800);
});

05Skeleton to content crossfade

What you see

The gray skeleton blocks and the real avatar, title, and body text sit stacked in the same card and simply swap opacity, so the loading state resolves without any layout jump.

Skeleton to content crossfade
Open in a new tab

05 · Skeleton to content crossfade — Measured area 3.401% · moving frames 10/23Original by GODRICH · SCSS and React ship in the zip · See the password

opacity 크로스페이드겹친 레이어0.4초

Where it fits

It fits a card list's first load or a profile page's initial render — anywhere a skeleton needs to hand off to real content in place.

.card__layer--skeleton {
  flex-direction: row;
  opacity: 1;
  transform: scale(1);
  transition: opacity $duration $easing, transform $duration $easing;
}
.card__layer--content {
  flex-direction: row;
  align-items: flex-start;
  opacity: 0;
  transform: scale(.98);
  transition: opacity $duration $easing, transform $duration $easing;
}

06Empty-state icon bounce

The motion

The icon shown above an empty list gently bounces up and down on a two-second loop, using nothing more than a translateY keyframe on the icon itself.

Empty-state icon bounce
Open in a new tab

06 · Empty-state icon bounce — Measured area 1.361% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

translateY 바운스ease-out 이징무한 반복

Good places for it

It tells someone a search with no results or an empty cart is the intended state, not a broken page they should refresh.

&__icon {
  position: relative;
  width: 52px;
  height: 40px;
  transform: translateY(0);
}

&.is-demo &__icon { animation: bounce $duration $easing infinite; }

@keyframes bounce {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-10px); }
}

07Input error shake

What it does

Submitting an invalid value turns the input's border red through a separate overlay ring and gives the whole field a quick left-right shake, so the mistake cannot be scrolled past unnoticed.

Input error shake
Open in a new tab

07 · Input error shake — Measured area 3.311% · moving frames 10/23Original by GODRICH · SCSS and React ship in the zip · See the password

translateX 흔들림빨강 테두리0.4초

When to use it

Login password fields and checkout card-number inputs both lean on the pairing of color and motion, since a red border alone is easy to miss on a long form.

&.is-error &__frame { animation: shake $duration $easing both; }
&.is-error &__ring  { opacity: 1; }

@keyframes shake {
  10%, 90% { transform: translateX(-$amount * .5); }
  20%, 80% { transform: translateX($amount); }
  30%, 70% { transform: translateX(-$amount); }
  40%, 60% { transform: translateX($amount * .7); }
  50%      { transform: translateX(-$amount * .7); }
}

08Tab indicator slide

What you see

Clicking a tab writes its index into a single CSS custom property, and the underline indicator's transform reads that property back to slide over with both width and position already interpolated.

Tab indicator slide
Open in a new tab

08 · Tab indicator slide — Measured area 0.5% · moving frames 11/23Original by GODRICH · SCSS and React ship in the zip · See the password

transform 슬라이드width 보간0.25초

Where it fits

Settings-page tabs and dashboard category tabs both prefer it over animating left or width directly, since a transform never triggers layout.

&__indicator {
  position: absolute;
  left: 4px; bottom: 4px;
  width: calc((100% - 8px) / 3);
  height: 3px;
  border-radius: $r-pill;
  background: $color;
  transform: translateX(calc(var(--i, 0) * 100%));
  transition: transform $duration $easing;
}

09Toast stacking

The motion

Each new toast pushes the ones already on screen up by one slot and shrinks them slightly, so several notifications stack visibly instead of piling on top of each other.

Toast stacking
Open in a new tab

09 · Toast stacking — Measured area 19.122% · moving frames 10/23Original by GODRICH · SCSS and React ship in the zip · See the password

translateY 밀림scale 축소순차 스택

Good places for it

Save-confirmation toasts and multi-file upload alerts both need the stacking, since a second notification arriving mid-animation would otherwise just cover the first one.

.toasts__list.is-demo {
  .toast--3 { animation: slot-3 $duration $easing infinite; }
}

@keyframes slot-3 {
  0%, 70%   { opacity: 0; transform: translateY(16px) scale(1); }
  80%, 92%  { opacity: 1; transform: translateY(0) scale(1); }
  98%, 100% { opacity: 0; transform: translateY(0) scale(1); }
}

Where it breaks — the trap

The one fixed budget

Only one micro interaction css file here ships with a genuinely fixed budget baked in, and it is the one most likely to get copied into a real app with extra content added on top. Item 09's toast list sits inside a container that is exactly 118px tall — one 34px slot plus two 42px slots for the pair of toasts a new arrival pushes upward. That height, along with the -42px and -84px offsets inside each slot's own keyframes, is sized for exactly three toasts at once. Stack a fourth without adjusting all three numbers together, and the oldest toast either gets clipped by the container's bottom edge or drifts up past the newest one instead of fading out cleanly above it. When a press or a check should answer with texture rather than a plain scale, the skeuomorphism design kit grows 01's press into tactile parts. The archive folder carries the full nine with their variables already broken out, so a fourth slot becomes one more offset to add rather than math to re-derive from scratch. Unzip it with dawuywyc typed exactly as it reads here, no spaces added, no characters swapped.

Accessibility

Reduced motion

All nine drop their motion under prefers-reduced-motion, but each keeps a different amount of the interaction rather than only switching transitions off. 01, 06, 07, and 08 — the ones built from a single transform loop — just stop moving and hold whichever end state was already showing. A shaken field still turns red, and a slid tab indicator still lands under the active tab, only without the trip getting there. 02, 03, 05, and 09 are multi-step reveals, so instead of skipping the transition they jump straight to the finished frame. The save icon shows its checkmark immediately, the skeleton never shows at all, and the toast stack renders already stacked. Item 04's rollback keeps working exactly the same either way, because the 0.8-second delay comes from a JavaScript setTimeout rather than a CSS animation. Reduced motion only removes the easing on the heart's fill itself. MDN's reference for prefers-reduced-motion covers the operating-system settings that turn the media query on.

Browse more micro interactions examples and the rest of the collection on the CSS category page, and see what's actually inside the zip versus what's free to read here on the about page.

FAQ

What are micro interactions in CSS?

A micro interaction in CSS is a short, single-purpose animation triggered by one user action or app-state change — pressing a button, saving a file, an empty search. It confirms something happened without needing any explanatory text nearby. The animation is usually built from a handful of properties like transform and opacity animating for well under a second, since anything longer starts to read as a delay rather than feedback. The controls that answer a click most often — checkboxes and radios — have their own collection in the CSS checkbox and radio animations, bound by the same rule.

Do these micro interactions need JavaScript?

A few of the nine do, and the demos above are honest about which ones. Item 04's optimistic-like rollback and item 08's tab indicator both need a few lines of JavaScript to toggle a class or write a CSS custom property on click. The other seven run on CSS alone once a class like is-saved or is-error gets added, whether that class comes from a framework's state binding or a plain addEventListener.

Will nine looping animations slow a page down?

Not meaningfully, since only the one actually shown to a visitor plays at any moment. Every keyframe here animates transform, opacity, or a registered custom property rather than a layout-triggering value like width or top. The grid at the top of this page runs all nine at once inside iframes specifically to prove that out; a real page only ever runs the one or two a person is currently looking at.

Enter the archive password

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