30 CSS Keyframe Animations 20 / 30
CSS Path Drawing Animation
SVG stroke-drawing animations: signature handwriting, success checkmark, map route, logo outline, circuit trace and infinite symbol — stroke-dashoffset keyframes only.
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<div class="kf-20">
<h2>CSS Path Drawing Animations</h2>
<div class="grid">
<!-- Signature -->
<div class="card">
<svg class="sig-svg" width="200" height="100" viewBox="0 0 200 100">
<path d="M20,70 C30,20 50,20 60,50 C70,80 80,80 90,50 C100,20 110,30 120,60 C130,80 140,70 160,40 C170,25 180,40 185,60"/>
</svg>
<label>Signature Draw</label>
</div>
<!-- Check mark -->
<div class="card">
<svg class="check-svg" width="100" height="100" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40"/>
<polyline points="28,52 42,66 72,36"/>
</svg>
<label>Success Check</label>
</div>
<!-- Map route -->
<div class="card">
<div class="route-wrap">
<svg class="route-svg" width="200" height="130" viewBox="0 0 200 130">
<polyline points="20,110 20,60 80,60 80,30 160,30 160,80 140,80"/>
<circle class="dot-start" cx="20" cy="110" r="6" fill="#ff6b35"/>
<circle class="dot-end" cx="140" cy="80" r="6" fill="#ff6b35"/>
</svg>
</div>
<label>Map Route</label>
</div>
<!-- Logo outline -->
<div class="card">
<svg class="logo-svg" width="120" height="120" viewBox="0 0 120 120">
<path d="M60,10 L110,95 L10,95 Z"/>
<path d="M60,30 L90,80 L30,80 Z"/>
<path d="M60,50 L75,75 L45,75 Z"/>
</svg>
<label>Logo Outline</label>
</div>
<!-- Circuit board -->
<div class="card">
<svg class="circuit-svg" width="200" height="120" viewBox="0 0 200 120">
<line class="trace" x1="10" y1="60" x2="190" y2="60"/>
<line class="trace" x1="60" y1="10" x2="60" y2="110"/>
<line class="trace" x1="130" y1="10" x2="130" y2="110"/>
<path class="trace" d="M60,60 Q95,20 130,60"/>
<circle class="node" cx="60" cy="60" r="5"/>
<circle class="node" cx="130" cy="60" r="5"/>
<circle class="node" cx="10" cy="60" r="4"/>
<circle class="node" cx="190" cy="60" r="4"/>
</svg>
<label>Circuit Trace</label>
</div>
<!-- Infinity -->
<div class="card">
<svg class="inf-svg" width="200" height="100" viewBox="0 0 200 100">
<defs>
<linearGradient id="kf20grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#7c6af7"/>
<stop offset="50%" stop-color="#e91e8c"/>
<stop offset="100%" stop-color="#00d4ff"/>
</linearGradient>
</defs>
<path d="M50,50 C50,25 75,10 100,50 C125,90 150,75 150,50 C150,25 125,10 100,50 C75,90 50,75 50,50 Z"/>
</svg>
<label>Infinity Stroke</label>
</div>
</div>
</div> <div class="kf-20">
<h2>CSS Path Drawing Animations</h2>
<div class="grid">
<!-- Signature -->
<div class="card">
<svg class="sig-svg" width="200" height="100" viewBox="0 0 200 100">
<path d="M20,70 C30,20 50,20 60,50 C70,80 80,80 90,50 C100,20 110,30 120,60 C130,80 140,70 160,40 C170,25 180,40 185,60"/>
</svg>
<label>Signature Draw</label>
</div>
<!-- Check mark -->
<div class="card">
<svg class="check-svg" width="100" height="100" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40"/>
<polyline points="28,52 42,66 72,36"/>
</svg>
<label>Success Check</label>
</div>
<!-- Map route -->
<div class="card">
<div class="route-wrap">
<svg class="route-svg" width="200" height="130" viewBox="0 0 200 130">
<polyline points="20,110 20,60 80,60 80,30 160,30 160,80 140,80"/>
<circle class="dot-start" cx="20" cy="110" r="6" fill="#ff6b35"/>
<circle class="dot-end" cx="140" cy="80" r="6" fill="#ff6b35"/>
</svg>
</div>
<label>Map Route</label>
</div>
<!-- Logo outline -->
<div class="card">
<svg class="logo-svg" width="120" height="120" viewBox="0 0 120 120">
<path d="M60,10 L110,95 L10,95 Z"/>
<path d="M60,30 L90,80 L30,80 Z"/>
<path d="M60,50 L75,75 L45,75 Z"/>
</svg>
<label>Logo Outline</label>
</div>
<!-- Circuit board -->
<div class="card">
<svg class="circuit-svg" width="200" height="120" viewBox="0 0 200 120">
<line class="trace" x1="10" y1="60" x2="190" y2="60"/>
<line class="trace" x1="60" y1="10" x2="60" y2="110"/>
<line class="trace" x1="130" y1="10" x2="130" y2="110"/>
<path class="trace" d="M60,60 Q95,20 130,60"/>
<circle class="node" cx="60" cy="60" r="5"/>
<circle class="node" cx="130" cy="60" r="5"/>
<circle class="node" cx="10" cy="60" r="4"/>
<circle class="node" cx="190" cy="60" r="4"/>
</svg>
<label>Circuit Trace</label>
</div>
<!-- Infinity -->
<div class="card">
<svg class="inf-svg" width="200" height="100" viewBox="0 0 200 100">
<defs>
<linearGradient id="kf20grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#7c6af7"/>
<stop offset="50%" stop-color="#e91e8c"/>
<stop offset="100%" stop-color="#00d4ff"/>
</linearGradient>
</defs>
<path d="M50,50 C50,25 75,10 100,50 C125,90 150,75 150,50 C150,25 125,10 100,50 C75,90 50,75 50,50 Z"/>
</svg>
<label>Infinity Stroke</label>
</div>
</div>
</div>.kf-20 *, .kf-20 *::before, .kf-20 *::after { box-sizing: border-box; margin: 0; padding: 0; }
.kf-20 {
font-family: 'Segoe UI', sans-serif;
background: #0d0d1a;
color: #fff;
padding: 40px 20px;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
gap: 48px;
}
.kf-20 h2 {
font-size: 1.1rem;
color: #888;
letter-spacing: 2px;
text-transform: uppercase;
text-align: center;
}
.kf-20 .grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 32px;
width: 100%;
max-width: 900px;
}
.kf-20 .card {
background: #13132a;
border: 1px solid #2a2a4a;
border-radius: 16px;
padding: 32px 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.kf-20 .card label {
font-size: 0.8rem;
color: #666;
letter-spacing: 1px;
text-transform: uppercase;
}
/* 1: Signature handwriting */
.kf-20 .sig-svg path {
fill: none;
stroke: #7c6af7;
stroke-width: 3;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 600;
stroke-dashoffset: 600;
animation: kf-20-draw 2.5s ease forwards infinite;
}
@keyframes kf-20-draw {
0% { stroke-dashoffset: 600; opacity: 1; }
70% { stroke-dashoffset: 0; opacity: 1; }
85% { stroke-dashoffset: 0; opacity: 1; }
100% { stroke-dashoffset: -600; opacity: 0; }
}
/* 2: Check mark */
.kf-20 .check-svg circle {
fill: none;
stroke: #00d46a;
stroke-width: 4;
stroke-dasharray: 251;
stroke-dashoffset: 251;
animation: kf-20-circle 0.6s ease forwards infinite;
animation-delay: 0.2s;
transform-origin: center;
transform: rotate(-90deg);
}
.kf-20 .check-svg polyline {
fill: none;
stroke: #00d46a;
stroke-width: 4;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 80;
stroke-dashoffset: 80;
animation: kf-20-check 0.4s ease forwards infinite;
animation-delay: 0.8s;
}
@keyframes kf-20-circle {
0% { stroke-dashoffset: 251; }
70% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: 0; }
}
@keyframes kf-20-check {
0% { stroke-dashoffset: 80; }
100% { stroke-dashoffset: 0; }
}
.kf-20 .check-svg {
animation: kf-20-checkloop 2.5s ease infinite;
}
@keyframes kf-20-checkloop {
0%, 30% { opacity: 0; }
35%, 80% { opacity: 1; }
90%, 100% { opacity: 0; }
}
/* 3: Map route */
.kf-20 .route-svg polyline {
fill: none;
stroke: #ff6b35;
stroke-width: 3;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 400;
stroke-dashoffset: 400;
animation: kf-20-route 3s ease forwards infinite;
}
.kf-20 .route-svg .dot-start,
.kf-20 .route-svg .dot-end {
animation: kf-20-dotpop 0.3s ease forwards;
transform-origin: center;
}
.kf-20 .route-svg .dot-start { animation-delay: 0s; }
.kf-20 .route-svg .dot-end { animation-delay: 2.8s; }
@keyframes kf-20-route {
0% { stroke-dashoffset: 400; }
80% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: 0; }
}
@keyframes kf-20-dotpop {
0% { r: 0; }
70% { r: 8; }
100% { r: 6; }
}
.kf-20 .route-wrap { animation: kf-20-routeloop 4s ease infinite; }
@keyframes kf-20-routeloop {
0% { opacity: 0; }
5%, 85% { opacity: 1; }
95%, 100% { opacity: 0; }
}
/* 4: Logo outline */
.kf-20 .logo-svg path {
fill: none;
stroke: #e91e8c;
stroke-width: 2.5;
stroke-linecap: round;
stroke-dasharray: 800;
stroke-dashoffset: 800;
animation: kf-20-logo 3s ease forwards infinite;
}
.kf-20 .logo-svg path:nth-child(2) { animation-delay: 1s; stroke: #7c6af7; }
.kf-20 .logo-svg path:nth-child(3) { animation-delay: 2s; stroke: #00d4ff; }
@keyframes kf-20-logo {
0% { stroke-dashoffset: 800; }
60% { stroke-dashoffset: 0; }
80% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -800; }
}
/* 5: Circuit board */
.kf-20 .circuit-svg line,
.kf-20 .circuit-svg path {
fill: none;
stroke: #00d4ff;
stroke-width: 2;
stroke-linecap: square;
}
.kf-20 .circuit-svg .trace {
stroke-dasharray: 300;
stroke-dashoffset: 300;
animation: kf-20-circuit 2s linear infinite;
}
.kf-20 .circuit-svg .trace:nth-child(2) { animation-delay: 0.3s; stroke: #7c6af7; }
.kf-20 .circuit-svg .trace:nth-child(3) { animation-delay: 0.6s; stroke: #00d46a; }
.kf-20 .circuit-svg .trace:nth-child(4) { animation-delay: 0.9s; stroke: #ff6b35; }
@keyframes kf-20-circuit {
0% { stroke-dashoffset: 300; }
100% { stroke-dashoffset: -300; }
}
.kf-20 .circuit-svg .node {
fill: #00d4ff;
animation: kf-20-nodeflash 2s ease infinite;
}
@keyframes kf-20-nodeflash {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
/* 6: Infinity symbol */
.kf-20 .inf-svg path {
fill: none;
stroke: url(#kf20grad);
stroke-width: 6;
stroke-linecap: round;
stroke-dasharray: 500;
stroke-dashoffset: 0;
animation: kf-20-inf 3s linear infinite;
}
@keyframes kf-20-inf {
0% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -500; }
}
@media (prefers-reduced-motion: reduce) {
.kf-20 .sig-svg path,
.kf-20 .check-svg circle,
.kf-20 .check-svg polyline,
.kf-20 .check-svg,
.kf-20 .route-svg polyline,
.kf-20 .route-wrap,
.kf-20 .logo-svg path,
.kf-20 .circuit-svg .trace,
.kf-20 .circuit-svg .node,
.kf-20 .inf-svg path {
animation: none;
stroke-dashoffset: 0;
opacity: 1;
}
} .kf-20 *, .kf-20 *::before, .kf-20 *::after { box-sizing: border-box; margin: 0; padding: 0; }
.kf-20 {
font-family: 'Segoe UI', sans-serif;
background: #0d0d1a;
color: #fff;
padding: 40px 20px;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
gap: 48px;
}
.kf-20 h2 {
font-size: 1.1rem;
color: #888;
letter-spacing: 2px;
text-transform: uppercase;
text-align: center;
}
.kf-20 .grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 32px;
width: 100%;
max-width: 900px;
}
.kf-20 .card {
background: #13132a;
border: 1px solid #2a2a4a;
border-radius: 16px;
padding: 32px 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.kf-20 .card label {
font-size: 0.8rem;
color: #666;
letter-spacing: 1px;
text-transform: uppercase;
}
/* 1: Signature handwriting */
.kf-20 .sig-svg path {
fill: none;
stroke: #7c6af7;
stroke-width: 3;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 600;
stroke-dashoffset: 600;
animation: kf-20-draw 2.5s ease forwards infinite;
}
@keyframes kf-20-draw {
0% { stroke-dashoffset: 600; opacity: 1; }
70% { stroke-dashoffset: 0; opacity: 1; }
85% { stroke-dashoffset: 0; opacity: 1; }
100% { stroke-dashoffset: -600; opacity: 0; }
}
/* 2: Check mark */
.kf-20 .check-svg circle {
fill: none;
stroke: #00d46a;
stroke-width: 4;
stroke-dasharray: 251;
stroke-dashoffset: 251;
animation: kf-20-circle 0.6s ease forwards infinite;
animation-delay: 0.2s;
transform-origin: center;
transform: rotate(-90deg);
}
.kf-20 .check-svg polyline {
fill: none;
stroke: #00d46a;
stroke-width: 4;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 80;
stroke-dashoffset: 80;
animation: kf-20-check 0.4s ease forwards infinite;
animation-delay: 0.8s;
}
@keyframes kf-20-circle {
0% { stroke-dashoffset: 251; }
70% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: 0; }
}
@keyframes kf-20-check {
0% { stroke-dashoffset: 80; }
100% { stroke-dashoffset: 0; }
}
.kf-20 .check-svg {
animation: kf-20-checkloop 2.5s ease infinite;
}
@keyframes kf-20-checkloop {
0%, 30% { opacity: 0; }
35%, 80% { opacity: 1; }
90%, 100% { opacity: 0; }
}
/* 3: Map route */
.kf-20 .route-svg polyline {
fill: none;
stroke: #ff6b35;
stroke-width: 3;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 400;
stroke-dashoffset: 400;
animation: kf-20-route 3s ease forwards infinite;
}
.kf-20 .route-svg .dot-start,
.kf-20 .route-svg .dot-end {
animation: kf-20-dotpop 0.3s ease forwards;
transform-origin: center;
}
.kf-20 .route-svg .dot-start { animation-delay: 0s; }
.kf-20 .route-svg .dot-end { animation-delay: 2.8s; }
@keyframes kf-20-route {
0% { stroke-dashoffset: 400; }
80% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: 0; }
}
@keyframes kf-20-dotpop {
0% { r: 0; }
70% { r: 8; }
100% { r: 6; }
}
.kf-20 .route-wrap { animation: kf-20-routeloop 4s ease infinite; }
@keyframes kf-20-routeloop {
0% { opacity: 0; }
5%, 85% { opacity: 1; }
95%, 100% { opacity: 0; }
}
/* 4: Logo outline */
.kf-20 .logo-svg path {
fill: none;
stroke: #e91e8c;
stroke-width: 2.5;
stroke-linecap: round;
stroke-dasharray: 800;
stroke-dashoffset: 800;
animation: kf-20-logo 3s ease forwards infinite;
}
.kf-20 .logo-svg path:nth-child(2) { animation-delay: 1s; stroke: #7c6af7; }
.kf-20 .logo-svg path:nth-child(3) { animation-delay: 2s; stroke: #00d4ff; }
@keyframes kf-20-logo {
0% { stroke-dashoffset: 800; }
60% { stroke-dashoffset: 0; }
80% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -800; }
}
/* 5: Circuit board */
.kf-20 .circuit-svg line,
.kf-20 .circuit-svg path {
fill: none;
stroke: #00d4ff;
stroke-width: 2;
stroke-linecap: square;
}
.kf-20 .circuit-svg .trace {
stroke-dasharray: 300;
stroke-dashoffset: 300;
animation: kf-20-circuit 2s linear infinite;
}
.kf-20 .circuit-svg .trace:nth-child(2) { animation-delay: 0.3s; stroke: #7c6af7; }
.kf-20 .circuit-svg .trace:nth-child(3) { animation-delay: 0.6s; stroke: #00d46a; }
.kf-20 .circuit-svg .trace:nth-child(4) { animation-delay: 0.9s; stroke: #ff6b35; }
@keyframes kf-20-circuit {
0% { stroke-dashoffset: 300; }
100% { stroke-dashoffset: -300; }
}
.kf-20 .circuit-svg .node {
fill: #00d4ff;
animation: kf-20-nodeflash 2s ease infinite;
}
@keyframes kf-20-nodeflash {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
/* 6: Infinity symbol */
.kf-20 .inf-svg path {
fill: none;
stroke: url(#kf20grad);
stroke-width: 6;
stroke-linecap: round;
stroke-dasharray: 500;
stroke-dashoffset: 0;
animation: kf-20-inf 3s linear infinite;
}
@keyframes kf-20-inf {
0% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -500; }
}
@media (prefers-reduced-motion: reduce) {
.kf-20 .sig-svg path,
.kf-20 .check-svg circle,
.kf-20 .check-svg polyline,
.kf-20 .check-svg,
.kf-20 .route-svg polyline,
.kf-20 .route-wrap,
.kf-20 .logo-svg path,
.kf-20 .circuit-svg .trace,
.kf-20 .circuit-svg .node,
.kf-20 .inf-svg path {
animation: none;
stroke-dashoffset: 0;
opacity: 1;
}
}How this works
Every SVG stroke uses the classic stroke-dasharray + stroke-dashoffset draw trick: set the dash array equal to or larger than the path length, set the offset to the same value (path appears hidden), then animate the offset to 0 to reveal the stroke as if a pen is drawing it. The signature uses stroke-dasharray: 600; stroke-dashoffset: 600 → 0 over 2.5s.
The success checkmark sequences two paths: the circle draws first (0.2s delay, 0.6s duration), then the checkmark polyline draws (0.8s delay, 0.4s). The map route does the same but pairs it with two circle markers that pop in via an r attribute animation. The infinity uses stroke-dashoffset: 0 → -500 for a continuous-flow animation, and the circuit traces 0 → -300 in a linear loop so the segments appear to flow.
Customize
- Change stroke colours by editing each path's
strokeproperty —#7c6af7,#00d46a,#ff6b35,#e91e8c,#00d4ff. - Slow the signature by changing
2.5sto4son thekf-20-drawrule for a more deliberate handwriting feel. - Adjust stroke width via
stroke-width: 3— drop to2for fine-line aesthetics or5for bold strokes. - Sequence custom paths by setting
animation-delayvalues that chain end-to-start. - Tune the dasharray for new paths — measure path length with
path.getTotalLength()in dev tools first.
Watch out for
stroke-dasharrayvalues must exceed the actual path length, or the stroke loops mid-draw and you see a visible gap.- SVG
rattribute animation inkf-20-dotpopuses CSS — Safari 13.0 and below ignored CSS attribute animation; safe from 13.1 onward. - Gradient strokes (the infinity) need an SVG
<defs>linear or conic gradient — without it thestroke: url(#...)reference falls back to black.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 55+ | 13.1+ | 54+ | 55+ |
SVG attribute animation via CSS needs Safari 13.1+; older Safari ignores the dot-pop effect.