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;
}
}
flowchart TD
A[Interaction] --> B[Feedback]
B --> C{Reduced motion?}
C -->|Yes| D[No animation]
C -->|No| E[Small transform/opacity]

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