Skip to main content

Animation & Micro-Interactions

Micro-interactions are small UI responses: hover states, focus rings, subtle reveals, and feedback messages. They can make a site feel intentional, but they can also hurt performance and accessibility if overused.

Principles (So You Don't Overdo It)​

PrincipleWhat it meansExample
PurposeAnimation should communicate stateButton hover indicates clickability
RestraintUse a small set of motionsOne reveal style across the site
RespectFollow prefers-reduced-motionDisable large transitions for sensitive users
PerformanceAvoid layout-triggering animationAnimate opacity/transform, not height
caution

Avoid scroll-jacking animations and heavy "on-scroll" libraries on content-heavy pages. They often worsen INP and create accessibility issues.

Animate the Right Properties​

Some CSS properties are much cheaper to animate than others.

CategoryPreferAvoid
Safe (usually)transform, opacity-
Riskyfilter (sometimes), box-shadow (sometimes)Large, frequent shadow animations
Expensiveheight, width, top/left, margin, paddingThese often cause layout recalculation and CLS
tip

If an animation causes layout changes, it tends to cause jank on mobile. Try to keep motion as a layer on top of the layout, not something that changes layout.

A Safe Animation Starter Set​

These are usually safe:

  • Link underline transitions
  • Button hover/active states
  • Small opacity/transform reveal for non-critical decorative elements
CSS: micro-interactions for links and buttons
a {
text-decoration-thickness: 2px;
text-underline-offset: 0.18em;
transition: color 160ms ease, text-decoration-color 160ms ease;
}

.wp-block-button__link {
transition: transform 120ms ease, filter 120ms ease;
}

.wp-block-button__link:hover {
transform: translateY(-1px);
filter: brightness(1.02);
}

.wp-block-button__link:active {
transform: translateY(0);
}

Reduced Motion Support​

Always provide a reduced-motion fallback.

CSS: reduce motion globally when requested
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
transition: none !important;
scroll-behavior: auto !important;
}
}

Hands-On: Add One Reveal Pattern​

  1. Pick one non-critical section (testimonial row, logo row).
  2. Apply a single reveal animation (opacity + transform).
  3. Confirm it does not shift layout (no CLS).
  4. Confirm reduced motion disables it.
CSS: simple reveal class
.reveal {
opacity: 0;
transform: translateY(10px);
transition: opacity 260ms ease, transform 260ms ease;
}

.reveal.is-visible {
opacity: 1;
transform: translateY(0);
}

Here is a tiny IntersectionObserver snippet to toggle is-visible without scroll handlers:

JS: reveal elements when they enter the viewport
document.addEventListener('DOMContentLoaded', () => {
const els = document.querySelectorAll('.reveal');
if (!('IntersectionObserver' in window) || els.length === 0) return;

const obs = new IntersectionObserver(
(entries) => {
for (const e of entries) {
if (e.isIntersecting) {
e.target.classList.add('is-visible');
obs.unobserve(e.target);
}
}
},
{ rootMargin: '0px 0px -10% 0px', threshold: 0.1 }
);

els.forEach((el) => obs.observe(el));
});
tip

If you need JS to toggle classes, keep it tiny and avoid running on every scroll event. Prefer intersection observers over scroll handlers.

The best micro-interactions are clarity tools:

  • Dropdown menus: clear hover/focus states, no sudden jumps
  • Forms: clear focus ring, visible error states, disabled state looks disabled
  • Buttons: hover/active states that feel consistent
note

If you animate dropdown menus, ensure keyboard users can still navigate them and focus does not get trapped behind overlays.

Performance QA​

Before shipping motion changes:

  1. Test on mobile.
  2. Confirm reduced motion disables non-essential animation.
  3. Confirm the animation does not push content down/up (CLS).
  4. Confirm focus styles remain visible.

Troubleshooting​

SymptomLikely causeFix
Layout shifts during animationAnimating height/marginsAnimate transform/opacity instead
Animations feel "laggy"Too many animated elementsReduce count; stagger only a few
Keyboard users get lostFocus styling removedKeep strong focus-visible styles
Animation causes CLSAnimating layout propertiesSwitch to transform/opacity; reserve space

Quick Reference​

  • Prefer transform/opacity animations
  • Support reduced motion
  • Keep motion purposeful and consistent

What's Next​