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)
| Principle | What it means | Example |
|---|---|---|
| Purpose | Animation should communicate state | Button hover indicates clickability |
| Restraint | Use a small set of motions | One reveal style across the site |
| Respect | Follow prefers-reduced-motion | Disable large transitions for sensitive users |
| Performance | Avoid layout-triggering animation | Animate opacity/transform, not height |
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.
| Category | Prefer | Avoid |
|---|---|---|
| Safe (usually) | transform, opacity | - |
| Risky | filter (sometimes), box-shadow (sometimes) | Large, frequent shadow animations |
| Expensive | height, width, top/left, margin, padding | These often cause layout recalculation and CLS |
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
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.
@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
- Pick one non-critical section (testimonial row, logo row).
- Apply a single reveal animation (opacity + transform).
- Confirm it does not shift layout (no CLS).
- Confirm reduced motion disables it.
.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:
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));
});
If you need JS to toggle classes, keep it tiny and avoid running on every scroll event. Prefer intersection observers over scroll handlers.
Navigation and Forms: Micro-Interactions That Matter
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
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:
- Test on mobile.
- Confirm reduced motion disables non-essential animation.
- Confirm the animation does not push content down/up (CLS).
- Confirm focus styles remain visible.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Layout shifts during animation | Animating height/margins | Animate transform/opacity instead |
| Animations feel "laggy" | Too many animated elements | Reduce count; stagger only a few |
| Keyboard users get lost | Focus styling removed | Keep strong focus-visible styles |
| Animation causes CLS | Animating layout properties | Switch to transform/opacity; reserve space |
Quick Reference
- Prefer transform/opacity animations
- Support reduced motion
- Keep motion purposeful and consistent
What's Next
- Next module: Advanced Developer Topics