Skip to main content
โšก Calmops

Micro-interactions: Adding Delight to User Interfaces

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; }
}
.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