Micro-interactions are the subtle moments of feedback and interaction that make your interface feel alive and responsive. They guide users, provide feedback, and add delight to the user experience.
What are Micro-interactions?
Micro-interactions are small, focused moments of interaction with a single task:
- Button click feedback
- Toggle switches
- Form validation
- Loading indicators
- Notification badges
- Pull-to-refresh
- Like/heart animations
Button Interactions
Basic Button Feedback
/* Scale down on click */
.btn {
transition: transform 0.1s ease;
}
.btn:active {
transform: scale(0.95);
}
/* Or with slight depression */
.btn:active {
transform: translateY(2px);
}
Button Hover Effects
/* Background transition */
.btn {
background: #2563eb;
transition: background 0.2s ease, transform 0.1s ease;
}
.btn:hover {
background: #1d4ed8;
}
.btn:active {
transform: scale(0.98);
}
/* Lift effect */
.btn-lift:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
}
Icon Button Animation
.btn-icon {
position: relative;
width: 40px;
height: 40px;
border-radius: 50%;
border: none;
background: transparent;
cursor: pointer;
transition: background 0.2s ease;
}
.btn-icon:hover {
background: rgba(0, 0, 0, 0.05);
}
/* Ripple effect */
.btn-icon::after {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
background: rgba(0, 0, 0, 0.1);
transform: scale(0);
transition: transform 0.3s ease;
}
.btn-icon:active::after {
transform: scale(1);
transition: transform 0s;
}
Toggle & Switch
Custom Toggle Switch
.toggle {
position: relative;
width: 48px;
height: 24px;
background: #e2e8f0;
border-radius: 12px;
cursor: pointer;
transition: background 0.2s ease;
}
.toggle::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
background: white;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
transition: transform 0.2s ease;
}
.toggle.active {
background: #2563eb;
}
.toggle.active::after {
transform: translateX(24px);
}
Animated Checkbox
.checkbox {
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
}
.checkbox input {
display: none;
}
.checkbox-box {
width: 20px;
height: 20px;
border: 2px solid #cbd5e1;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.checkbox-box svg {
width: 14px;
height: 14px;
stroke: white;
stroke-width: 3;
transform: scale(0);
transition: transform 0.2s ease;
}
.checkbox input:checked + .checkbox-box {
background: #2563eb;
border-color: #2563eb;
}
.checkbox input:checked + .checkbox-box svg {
transform: scale(1);
}
<label class="checkbox">
<input type="checkbox">
<span class="checkbox-box">
<svg viewBox="0 0 24 24" fill="none">
<path d="M5 12l5 5L20 7" />
</svg>
</span>
<span>Accept terms</span>
</label>
Form Feedback
Input Focus Animation
.input-field {
position: relative;
}
.input-field input {
width: 100%;
padding: 1rem;
border: 2px solid #e2e8f0;
border-radius: 8px;
font-size: 1rem;
transition: border-color 0.2s ease;
}
.input-field input:focus {
outline: none;
border-color: #2563eb;
}
/* Animated underline */
.input-field::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 2px;
background: #2563eb;
transition: width 0.3s ease, left 0.3s ease;
}
.input-field:focus-within::after {
width: 100%;
left: 0;
}
Validation Feedback
.input-group {
position: relative;
}
.input-group input {
padding-right: 2.5rem;
}
.input-group .validation-icon {
position: absolute;
right: 0.75rem;
top: 50%;
transform: translateY(-50%);
opacity: 0;
transition: opacity 0.2s ease;
}
.input-group input:valid:not(:placeholder-shown) .validation-icon.valid,
.input-group input:invalid:not(:placeholder-shown) .validation-icon.invalid {
opacity: 1;
}
/* Shake animation for errors */
@keyframes shake {
0%, 100% { transform: translateX(0); }
20%, 60% { transform: translateX(-5px); }
40%, 80% { transform: translateX(5px); }
}
.input-group.error input {
border-color: #dc2626;
animation: shake 0.4s ease;
}
Like/Heart Animation
.like-button {
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
}
.heart {
width: 24px;
height: 24px;
fill: transparent;
stroke: #64748b;
stroke-width: 2;
transition: all 0.3s ease;
}
.like-button:hover .heart {
stroke: #dc2626;
}
.like-button.active .heart {
fill: #dc2626;
stroke: #dc2626;
animation: heartBeat 0.3s ease;
}
@keyframes heartBeat {
0% { transform: scale(1); }
50% { transform: scale(1.3); }
100% { transform: scale(1); }
}
/* Particle burst effect */
.like-button.active::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle, #dc2626 20%, transparent 70%);
animation: particleBurst 0.5s ease forwards;
}
@keyframes particleBurst {
0% { transform: scale(0); opacity: 1; }
100% { transform: scale(2); opacity: 0; }
}
Loading Spinners
CSS Spinner
@keyframes spin {
to { transform: rotate(360deg); }
}
.spinner {
width: 24px;
height: 24px;
border: 3px solid #e2e8f0;
border-top-color: #2563eb;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
/* Size variants */
.spinner-sm { width: 16px; height: 16px; }
.spinner-md { width: 24px; height: 24px; }
.spinner-lg { width: 40px; height: 40px; }
.spinner-xl { width: 64px; height: 64px; }
Dots Loading
.dots {
display: flex;
gap: 4px;
}
.dots span {
width: 8px;
height: 8px;
background: #2563eb;
border-radius: 50%;
animation: dotPulse 1.4s ease-in-out infinite;
}
.dots span:nth-child(2) { animation-delay: 0.2s; }
.dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes dotPulse {
0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
40% { transform: scale(1); opacity: 1; }
}
Menu & Dropdown
Dropdown Animation
.dropdown {
position: relative;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
min-width: 180px;
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: all 0.2s ease;
}
.dropdown:hover .dropdown-menu,
.dropdown:focus-within .dropdown-menu {
opacity: 1;
visibility: visible;
transform: translateY(4px);
}
/* Stagger children */
.dropdown-menu > * {
opacity: 0;
transform: translateY(-5px);
transition: opacity 0.2s ease, transform 0.2s ease;
}
.dropdown:hover .dropdown-menu > *:nth-child(1) { transition-delay: 0ms; opacity: 1; transform: translateY(0); }
.dropdown:hover .dropdown-menu > *:nth-child(2) { transition-delay: 50ms; opacity: 1; transform: translateY(0); }
.dropdown:hover .dropdown-menu > *:nth-child(3) { transition-delay: 100ms; opacity: 1; transform: translateY(0); }
.dropdown:hover .dropdown-menu > *:nth-child(4) { transition-delay: 150ms; opacity: 1; transform: translateY(0); }
Card Interactions
Card Hover Effect
.card {
background: white;
border-radius: 12px;
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
}
.card-image {
transition: transform 0.3s ease;
}
.card:hover .card-image {
transform: scale(1.05);
}
Accessibility
Respect Reduced Motion
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Use in JavaScript */
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
// Disable animations
}
Focus Indicators
/* Always visible focus */
:focus-visible {
outline: 2px solid #2563eb;
outline-offset: 2px;
}
/* Custom focus ring */
.btn:focus-visible {
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.4);
}
JavaScript-Enhanced Interactions
Tooltip with Fade
.tooltip {
position: relative;
}
.tooltip::after {
content: attr(data-tooltip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%) translateY(8px);
padding: 0.5rem 0.75rem;
background: #1e293b;
color: white;
font-size: 0.875rem;
border-radius: 6px;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: all 0.2s ease;
}
.tooltip:hover::after {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(-4px);
}
Progress Step Animation
.step {
display: flex;
align-items: center;
gap: 0.5rem;
}
.step-circle {
width: 32px;
height: 32px;
border-radius: 50%;
background: #e2e8f0;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
transition: all 0.3s ease;
}
.step.active .step-circle {
background: #2563eb;
color: white;
transform: scale(1.1);
}
.step.completed .step-circle {
background: #16a34a;
color: white;
}
Implementation Best Practices
Performance Tips
/* Animate only transform and opacity */
.good {
transform: translateX(0);
opacity: 1;
transition: transform 0.3s, opacity 0.3s;
}
/* Avoid animating these */
.bad {
width: 100px; /* Causes layout */
height: 100px; /* Causes layout */
background: red; /* Causes repaint */
left: 0; /* Causes layout */
}
Timing Functions
/* Smooth but snappy */
.transition-default { transition: all 0.2s ease; }
/* More natural */
.transition-bounce { transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); }
/* Smooth entry */
.transition-smooth { transition: all 0.3s ease-out; }
Summary
Micro-interactions enhance user experience by:
- Providing feedback - Let users know actions succeeded
- Guiding attention - Draw eye to important elements
- Adding delight - Make interfaces feel alive
- Confirming state - Show current state clearly
Key implementation tips:
- Use CSS transitions for simple effects
- Reserve JavaScript for complex interactions
- Always respect
prefers-reduced-motion - Test on mobile devices
- Keep animations short (100-300ms)
- Focus on transform and opacity for performance
Comments