animation-iteration-count & animation-direction
animation-iteration-count controls how many times an animation repeats (1, 2, infinite, 0.5). animation-direction controls playback direction (normal, reverse, alternate, alternate-reverse). Together they create looping animations, back-and-forth effects, and ping-pong patterns. Essential for loading spinners, bouncing elements, and continuous animations.
Animation iteration count
Value | Behavior | Use Case |
|---|---|---|
1 (default) | Plays once | One-time animations, entrances |
2, 3, ..., N | Plays N times | Fixed repetition count |
infinite | Loops forever | Spinners, loaders, continuous |
0.5, 1.5, etc | Decimal counts | Partial animation (e.g., 1.5 = 1.5x plays) |
/* Play once (default) */
.one-time {
animation: slide-in 0.5s ease;
animation-iteration-count: 1;
/* Animates once on load */
}
/* Play multiple times */
.repeat-three {
animation: blink 0.5s ease;
animation-iteration-count: 3;
/* Blinks 3 times */
}
/* Play indefinitely */
.infinite {
animation: spin 2s linear;
animation-iteration-count: infinite;
/* Continuous spinning */
}
/* Partial iteration */
.partial {
animation: move 1s ease;
animation-iteration-count: 1.5;
/* Plays 1.5 times (stops halfway through second iteration) */
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
@keyframes move {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}
/* Common patterns */
/* Loading spinner */
.loader {
animation: spin 1s linear infinite;
/* Spins continuously */
}
/* Pulse effect */
.pulse {
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* Flash warning */
.warning-flash {
animation: flash 0.3s ease;
animation-iteration-count: 3;
/* Flashes 3 times then stops */
}
@keyframes flash {
0%, 50%, 100% { opacity: 1; }
25%, 75% { opacity: 0.3; }
}Animation direction
Value | Behavior | Effect |
|---|---|---|
normal (default) | Forward each time | Plays 0% → 100%, then 0% → 100% |
reverse | Backward each time | Plays 100% → 0%, then 100% → 0% |
alternate | Back and forth | Plays 0% → 100% → 0% → 100%... |
alternate-reverse | Backward back-forth | Plays 100% → 0% → 100% → 0%... |
/* Normal: forward each iteration */
.normal-direction {
animation: slide 1s ease;
animation-iteration-count: 2;
animation-direction: normal;
/* Slides right, slides right again */
}
/* Reverse: backward each iteration */
.reverse-direction {
animation: slide 1s ease;
animation-iteration-count: 2;
animation-direction: reverse;
/* Slides left, slides left again */
}
/* Alternate: forward then backward */
.alternate-direction {
animation: slide 1s ease;
animation-iteration-count: 2;
animation-direction: alternate;
/* Slides right, then slides left back to start */
}
/* Alternate-reverse: backward then forward */
.alternate-reverse-direction {
animation: slide 1s ease;
animation-iteration-count: 2;
animation-direction: alternate-reverse;
/* Slides left, then slides right back to start */
}
@keyframes slide {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}
/* Common patterns */
/* Bouncing ball (alternate direction) */
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-100px); }
}
.bouncing-ball {
animation: bounce 0.8s ease infinite;
animation-direction: alternate;
/* Up and down, up and down... */
}
/* Swinging pendulum (alternate) */
@keyframes swing {
from { transform: rotate(-30deg); }
to { transform: rotate(30deg); }
}
.pendulum {
animation: swing 1.5s ease-in-out infinite;
animation-direction: alternate;
/* Swings left then right, back and forth */
transform-origin: top center;
}
/* Back-and-forth scroll hint */
@keyframes scroll-hint {
from { transform: translateY(0); }
to { transform: translateY(10px); }
}
.scroll-indicator {
animation: scroll-hint 1s ease-in-out 2;
animation-direction: alternate;
/* Down then up, twice */
}Combining count and direction
/* Combine for complex effects */
/* Bouncy entrance: forward for emphasis */
@keyframes bouncy-entrance {
0% { opacity: 0; transform: scale(0); }
70% { transform: scale(1.1); opacity: 1; }
100% { transform: scale(1); }
}
.bounce-in {
animation: bouncy-entrance 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
animation-iteration-count: 1;
/* Bounces in once */
}
/* Heartbeat: infinite alternate */
@keyframes heartbeat {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.2); }
}
.heart {
animation: heartbeat 0.6s ease-in-out;
animation-iteration-count: infinite;
animation-direction: alternate;
/* Constantly pulsing */
}
/* Loading dots: infinite normal */
.dot-1 { animation: fade 1s ease infinite; animation-delay: 0s; }
.dot-2 { animation: fade 1s ease infinite; animation-delay: 0.2s; }
.dot-3 { animation: fade 1s ease infinite; animation-delay: 0.4s; }
@keyframes fade {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
/* Wave effect: infinite normal with delays */
.wave-item {
animation: wave 1s ease-in-out;
animation-iteration-count: infinite;
/* Continuous waving */
}
@keyframes wave {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.wave-item:nth-child(1) { animation-delay: 0s; }
.wave-item:nth-child(2) { animation-delay: 0.1s; }
.wave-item:nth-child(3) { animation-delay: 0.2s; }
/* Flip card: one iteration, then could be repeatable */
@keyframes card-flip {
from { transform: rotateY(0deg); }
to { transform: rotateY(180deg); }
}
.flip-card {
animation: card-flip 0.6s ease-in-out;
animation-iteration-count: 1;
/* Flips once on interaction */
}
/* Repeating flip with reverse (back and forth) */
.repeating-flip {
animation: card-flip 0.6s ease-in-out;
animation-iteration-count: infinite;
animation-direction: alternate;
/* Flips back and forth forever */
}Practical examples
/* Loading spinner */
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear;
animation-iteration-count: infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Pulsing notification badge */
.notification-pulse {
width: 20px;
height: 20px;
background: #e74c3c;
border-radius: 50%;
animation: pulse 2s ease-in-out;
animation-iteration-count: infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.8; }
}
/* Animated underline for active nav */
.nav-link {
position: relative;
}
.nav-link.active::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, #3498db 0%, #2ecc71 100%);
animation: slide-underline 1.5s ease-in-out;
animation-iteration-count: 1;
}
@keyframes slide-underline {
from { width: 0; }
to { width: 100%; }
}
/* Bounce animation on click */
.click-bounce {
animation: bounce 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
animation-iteration-count: 1;
}
@keyframes bounce {
0% { transform: scale(1); }
25% { transform: scale(1.2); }
50% { transform: scale(0.95); }
75% { transform: scale(1.1); }
100% { transform: scale(1); }
}
/* Infinite scroll prompt (then stops) */
.scroll-prompt {
opacity: 0;
animation: float-up 2s ease-in-out;
animation-iteration-count: 3;
/* Floats up 3 times then stops */
}
@keyframes float-up {
from { opacity: 0; transform: translateY(0); }
to { opacity: 0; transform: translateY(-30px); }
}
/* Attention grabber: shakes back and forth */
.shake {
animation: shake 0.5s ease-in-out;
animation-iteration-count: infinite;
animation-direction: alternate;
}
@keyframes shake {
from { transform: translateX(-5px); }
to { transform: translateX(5px); }
}Performance and accessibility
/* Infinite animations can be processor-intensive */
.heavy-animation {
animation: complex 5s ease;
animation-iteration-count: infinite;
/* Continuous animation = constant CPU usage */
}
/* Use will-change for optimized infinite animations */
.optimized-infinite {
will-change: transform;
animation: spin 2s linear infinite;
/* GPU accelerated */
}
/* Respect prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}
/* Alternative: allow user to pause animations */
.pausable-animation {
animation: spin 2s linear infinite;
animation-play-state: running;
}
.pausable-animation:hover,
.pausable-animation:focus {
animation-play-state: paused;
/* User can pause on hover/focus */
}
/* Avoid infinite animations that distract */
.avoid-infinite {
animation: flash 0.2s ease;
animation-iteration-count: infinite;
/* Distracting flickering */
}
/* Better: use for purposeful indicators only */
.thoughtful-infinite {
animation: gentle-pulse 3s ease-in-out;
animation-iteration-count: infinite;
/* Subtle, not distracting */
}