attention

انیمیشن‌های هدفمند برای جلب توجه کاربر، اعلان‌ها و هدایت تمرکز در رابط کاربری

این Skill مجموعه‌ای از الگوهای CSS و JavaScript برای جلب توجه کاربر ارائه می‌دهد؛ از pulse، badge و ring pulse تا spotlight، shimmer، toast و راهنمای جهت‌دار. شدت انیمیشن‌ها برای حالت‌های subtle، moderate و urgent قابل تنظیم است.

با استفاده از این الگوها می‌توانید اعلان‌ها و تغییرات مهم را واضح‌تر نمایش دهید، بدون اینکه تجربه کاربری را مختل کنید. پشتیبانی از prefers-reduced-motion، امکان dismiss و ملاحظات WCAG به ساخت رابط‌هایی دسترس‌پذیر و حرفه‌ای کمک می‌کند.

چه چیزی می‌گیرید

  • الگوهای آماده برای pulse، notification badge، ring pulse و breathing glow
  • افکت‌های هدایت تمرکز مانند spotlight، arrow hint و highlight sweep
  • انیمیشن‌های shimmer برای loading و toast برای اعلان‌های موقت
  • راهنمای انتخاب شدت انیمیشن بر اساس اهمیت رویداد
  • پیاده‌سازی جایگزین برای کاربران دارای `prefers-reduced-motion`

برای چه کاری خوب است

  • طراحی داشبوردها و اپلیکیشن‌هایی که اعلان، به‌روزرسانی یا وضعیت جدید دارند
  • ساخت onboarding و تجربه‌هایی که باید تمرکز کاربر را به یک بخش خاص هدایت کنند
  • بهبود feedback بصری برای خطا، موفقیت، loading و محتوای تازه

برای چه کاری نیست

  • انیمیشن‌های پیچیده سینمایی، موشن گرافیک چندلایه یا تدوین ویدیو
  • صفحه‌هایی که به حرکت مداوم و جلب توجه شدید نیاز ندارند یا باید کاملاً ایستا باشند

دربارهٔ این اسکیل

Attention

Animations that direct and capture user attention.

Standards Reference

StandardDocumentation
W3C DTCGdesigntokens.org
WCAG 2.1w3.org/WAI/WCAG21/Understanding
Material Designm3.material.io/foundations/interaction

Quick Reference

Uses tokens from: tokens/motion.tokens.json

Attention Intensities

LevelDurationUse
Subtle2s cyclePassive indication
Moderate1s cycleActive notification
Urgent0.5s cycleCritical alert

Selection Guide

What attention level?
│
├── SUBTLE → Passive indication
│   ├── Gentle pulse → "something here"
│   ├── Soft glow → ambient highlight
│   └── Slow breathe → available action
│
├── MODERATE → Active notification
│   ├── Badge bounce → new content
│   ├── Pulse ring → update available
│   └── Shimmer → loading/processing
│
├── URGENT → Critical alert
│   ├── Rapid pulse → requires action
│   ├── Shake → error/warning
│   └── Flash → immediate attention
│
├── GUIDED → Focus direction
│   ├── Spotlight → isolated focus
│   ├── Arrow pulse → directional hint
│   └── Highlight sweep → scan guidance
│
└── CELEBRATION → Positive feedback
    ├── Confetti → achievement
    ├── Burst → success
    └── Sparkle → reward

Patterns

Pulse Indicator

Gentle pulsing attention.

CSS:

.pulse {
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.05);
    opacity: 0.8;
  }
}

/* Urgent pulse */
.pulse-urgent {
  animation: pulse-urgent 0.5s ease-in-out infinite;
}

@keyframes pulse-urgent {
  0%, 100% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);
  }
  50% {
    transform: scale(1.02);
    box-shadow: 0 0 0 10px rgba(239, 68, 68, 0);
  }
}

Notification Badge

Bouncing badge indicator.

CSS:

.badge {
  position: absolute;
  top: -8px;
  right: -8px;
  min-width: 20px;
  height: 20px;
  background: #ef4444;
  border-radius: 10px;
  color: white;
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.badge.new {
  animation: badge-bounce 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes badge-bounce {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.3);
  }
  100% {
    transform: scale(1);
  }
}

/* Continuous attention */
.badge.attention {
  animation: badge-pulse 1s ease-in-out infinite;
}

@keyframes badge-pulse {
  0%, 100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.15);
  }
}

JavaScript (Animate on count change):

function updateBadge(badge, count) {
  const currentCount = parseInt(badge.textContent) || 0;

  if (count > currentCount) {
    badge.textContent = count;
    badge.classList.remove('new');
    void badge.offsetWidth; // Trigger reflow
    badge.classList.add('new');
  } else {
    badge.textContent = count;
  }

  badge.style.display = count > 0 ? 'flex' : 'none';
}

Ring Pulse

Expanding ring effect.

CSS:

.ring-pulse {
  position: relative;
}

.ring-pulse::before {
  content: '';
  position: absolute;
  inset: -4px;
  border: 2px solid #3b82f6;
  border-radius: inherit;
  animation: ring-expand 1.5s ease-out infinite;
}

@keyframes ring-expand {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  100% {
    transform: scale(1.5);
    opacity: 0;
  }
}

/* Multiple rings */
.ring-pulse::after {
  content: '';
  position: absolute;
  inset: -4px;
  border: 2px solid #3b82f6;
  border-radius: inherit;
  animation: ring-expand 1.5s ease-out infinite 0.5s;
}

Spotlight Focus

Highlight specific element.

CSS:

.spotlight-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.7);
  pointer-events: none;
  opacity: 0;
  transition: opacity 300ms;
}

.spotlight-overlay.active {
  opacity: 1;
}

.spotlight-target {
  position: relative;
  z-index: 1001;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.7);
  border-radius: 8px;
  animation: spotlight-pulse 2s ease-in-out infinite;
}

@keyframes spotlight-pulse {
  0%, 100% {
    box-shadow:
      0 0 0 9999px rgba(0, 0, 0, 0.7),
      0 0 0 4px rgba(59, 130, 246, 0.5);
  }
  50% {
    box-shadow:
      0 0 0 9999px rgba(0, 0, 0, 0.7),
      0 0 0 8px rgba(59, 130, 246, 0.3);
  }
}

JavaScript:

function spotlight(element) {
  const overlay = document.createElement('div');
  overlay.className = 'spotlight-overlay';
  document.body.appendChild(overlay);

  requestAnimationFrame(() => {
    overlay.classList.add('active');
    element.classList.add('spotlight-target');
  });

  return {
    dismiss: () => {
      overlay.classList.remove('active');
      element.classList.remove('spotlight-target');
      setTimeout(() => overlay.remove(), 300);
    }
  };
}

Shimmer Loading

Skeleton loading shimmer.

CSS:

.shimmer {
  background: linear-gradient(
    90deg,
    #f0f0f0 0%,
    #e0e0e0 50%,
    #f0f0f0 100%
  );
  background-size: 200% 100%;
  animation: shimmer 1.5s linear infinite;
}

@keyframes shimmer {
  0% {
    background-position: 200% 0;
  }
  100% {
    background-position: -200% 0;
  }
}

Directional Arrow

Pulsing directional hint.

CSS:

.arrow-hint {
  animation: arrow-bounce 1s ease-in-out infinite;
}

/* Point down */
.arrow-hint.down {
  animation-name: arrow-bounce-down;
}

@keyframes arrow-bounce-down {
  0%, 100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(8px);
  }
}

/* Point right */
.arrow-hint.right {
  animation-name: arrow-bounce-right;
}

@keyframes arrow-bounce-right {
  0%, 100% {
    transform: translateX(0);
  }
  50% {
    transform: translateX(8px);
  }
}

Highlight Sweep

Scanning highlight effect.

CSS:

.highlight-sweep {
  position: relative;
  overflow: hidden;
}

.highlight-sweep::after {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 50%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.4),
    transparent
  );
  animation: sweep 2s ease-in-out infinite;
}

@keyframes sweep {
  0% {
    left: -100%;
  }
  50%, 100% {
    left: 200%;
  }
}

Toast Notification

Attention-grabbing notification.

CSS:

.toast {
  position: fixed;
  bottom: 24px;
  right: 24px;
  padding: 16px 24px;
  background: #1f2937;
  color: white;
  border-radius: 8px;
  transform: translateY(100px);
  opacity: 0;
  transition: transform 300ms cubic-bezier(0.34, 1.56, 0.64, 1),
              opacity 300ms ease-out;
}

.toast.visible {
  transform: translateY(0);
  opacity: 1;
}

/* With progress bar */
.toast.auto-dismiss::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  height: 3px;
  background: #3b82f6;
  animation: toast-progress 5s linear forwards;
}

@keyframes toast-progress {
  from { width: 100%; }
  to { width: 0%; }
}

Breathing Glow

Ambient attention indicator.

CSS:

.breathe-glow {
  animation: breathe 3s ease-in-out infinite;
}

@keyframes breathe {
  0%, 100% {
    box-shadow: 0 0 10px rgba(59, 130, 246, 0.3);
  }
  50% {
    box-shadow: 0 0 20px rgba(59, 130, 246, 0.6);
  }
}

New Item Indicator

Highlight new content.

CSS:

.new-item {
  position: relative;
}

.new-item::before {
  content: 'NEW';
  position: absolute;
  top: -8px;
  right: -8px;
  padding: 2px 6px;
  background: #10b981;
  color: white;
  font-size: 10px;
  font-weight: bold;
  border-radius: 4px;
  animation: new-bounce 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.new-item.fade-out::before {
  animation: fade-out 300ms ease-out forwards;
}

@keyframes new-bounce {
  0% { transform: scale(0) rotate(-12deg); }
  100% { transform: scale(1) rotate(-12deg); }
}

Reduced Motion

For prefers-reduced-motion: reduce:

Full MotionReduced Alternative
PulsingStatic highlight color
Bouncing badgeInstant appearance
Ring pulseStatic border
ShimmerSolid gray
Arrow bounceStatic arrow

Implementation:

@media (prefers-reduced-motion: reduce) {
  .pulse,
  .badge.attention,
  .ring-pulse::before,
  .shimmer,
  .arrow-hint,
  .breathe-glow {
    animation: none;
  }

  .ring-pulse::before {
    opacity: 1;
    transform: scale(1);
    border-color: #3b82f6;
  }

  .shimmer {
    background: #e0e0e0;
  }
}

Accessibility Considerations

  • Don't auto-play indefinitely pulsing animations
  • Provide dismiss for persistent attention elements
  • Limit frequency of attention-grabbing effects
  • Color isn't enough - combine with shape/motion
  • Respect focus - don't animate focused elements excessively

Cross-References

  • See accessibility/ for WCAG requirements
  • See glow-shadow/ for glow effects
  • See looping/ for continuous animations
  • See particles/ for celebration effects

References

  • references/attention-patterns.md — UX attention patterns

دانلود ZIP