Back to Components
Animated Hourglass Preloader Using Pure SVG & CSS
Component

Animated Hourglass Preloader Using Pure SVG & CSS

CodewithLord
December 20, 2025

A visually rich hourglass preloader built using pure SVG and CSS animations. Features sand flow simulation, glass flipping motion, circular progress strokes, glare effects, and theme-aware color handling.

Description


This project showcases an animated hourglass preloader created entirely with SVG and CSS, without any JavaScript.
The animation simulates realistic sand flow, glass flipping, glare highlights, and circular motion strokes—making it ideal for loading screens, splash screens, and creative UI transitions.

Key highlights:


  • Pure SVG-based vector animation
  • CSS-driven timing and easing
  • Sand grain, mound, and drop simulation
  • Rotating circular motion indicators
  • Automatic dark/light theme adaptation
  • Scalable and resolution-independent design

The animation loops seamlessly and is optimized for modern browsers.



HTML

1<!DOCTYPE html> 2<html lang="en" > 3<head> 4 <meta charset="UTF-8"> 5 <title>Hourglass Preloader | @coding.stella</title> 6 <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"><link rel="stylesheet" href="./style.css"> 7 8</head> 9<body> 10 <svg 11 class="hourglass" 12 viewBox="0 0 56 56" 13 width="56px" 14 height="56px" 15 role="img" 16 aria-label="Hourglass being flipped clockwise and circled by three white curves fading in and out" 17 > 18 <clipPath id="sand-mound-top"> 19 <path 20 class="hourglass__sand-mound-top" 21 d="M 14.613 13.087 C 15.814 12.059 19.3 8.039 20.3 6.539 C 21.5 4.789 21.5 2.039 21.5 2.039 L 3 2.039 C 3 2.039 3 4.789 4.2 6.539 C 5.2 8.039 8.686 12.059 9.887 13.087 C 11 14.039 12.25 14.039 12.25 14.039 C 12.25 14.039 13.5 14.039 14.613 13.087 Z" 22 /> 23 </clipPath> 24 <clipPath id="sand-mound-bottom"> 25 <path 26 class="hourglass__sand-mound-bottom" 27 d="M 14.613 20.452 C 15.814 21.48 19.3 25.5 20.3 27 C 21.5 28.75 21.5 31.5 21.5 31.5 L 3 31.5 C 3 31.5 3 28.75 4.2 27 C 5.2 25.5 8.686 21.48 9.887 20.452 C 11 19.5 12.25 19.5 12.25 19.5 C 12.25 19.5 13.5 19.5 14.613 20.452 Z" 28 /> 29 </clipPath> 30 <g transform="translate(2,2)"> 31 <g 32 fill="none" 33 stroke="hsl(0,0%,100%)" 34 stroke-dasharray="153.94 153.94" 35 stroke-dashoffset="153.94" 36 stroke-linecap="round" 37 transform="rotate(-90,26,26)" 38 > 39 <circle 40 class="hourglass__motion-thick" 41 stroke-width="2.5" 42 cx="26" 43 cy="26" 44 r="24.5" 45 transform="rotate(0,26,26)" 46 /> 47 <circle 48 class="hourglass__motion-medium" 49 stroke-width="1.75" 50 cx="26" 51 cy="26" 52 r="24.5" 53 transform="rotate(90,26,26)" 54 /> 55 <circle 56 class="hourglass__motion-thin" 57 stroke-width="1" 58 cx="26" 59 cy="26" 60 r="24.5" 61 transform="rotate(180,26,26)" 62 /> 63 </g> 64 <g class="hourglass__model" transform="translate(13.75,9.25)"> 65 <!-- glass --> 66 <path 67 fill="hsl(var(--hue),90%,85%)" 68 d="M 1.5 2 L 23 2 C 23 2 22.5 8.5 19 12 C 16 15.5 13.5 13.5 13.5 16.75 C 13.5 20 16 18 19 21.5 C 22.5 25 23 31.5 23 31.5 L 1.5 31.5 C 1.5 31.5 2 25 5.5 21.5 C 8.5 18 11 20 11 16.75 C 11 13.5 8.5 15.5 5.5 12 C 2 8.5 1.5 2 1.5 2 Z" 69 /> 70 <!-- sand --> 71 <g stroke="hsl(35,90%,90%)" stroke-linecap="round"> 72 <line 73 class="hourglass__sand-grain-left" 74 stroke-width="1" 75 stroke-dasharray="0.25 33.75" 76 x1="12" 77 y1="15.75" 78 x2="12" 79 y2="20.75" 80 /> 81 <line 82 class="hourglass__sand-grain-right" 83 stroke-width="1" 84 stroke-dasharray="0.25 33.75" 85 x1="12.5" 86 y1="16.75" 87 x2="12.5" 88 y2="21.75" 89 /> 90 <line 91 class="hourglass__sand-drop" 92 stroke-width="1" 93 stroke-dasharray="0.5 107.5" 94 x1="12.25" 95 y1="18" 96 x2="12.25" 97 y2="31.5" 98 /> 99 <line 100 class="hourglass__sand-fill" 101 stroke-width="1.5" 102 stroke-dasharray="54 54" 103 x1="12.25" 104 y1="14.75" 105 x2="12.25" 106 y2="31.5" 107 /> 108 <line 109 class="hourglass__sand-line-left" 110 stroke="hsl(35,90%,83%)" 111 stroke-width="1" 112 stroke-dasharray="1 107" 113 x1="12" 114 y1="16" 115 x2="12" 116 y2="31.5" 117 /> 118 <line 119 class="hourglass__sand-line-right" 120 stroke="hsl(35,90%,83%)" 121 stroke-width="1" 122 stroke-dasharray="12 96" 123 x1="12.5" 124 y1="16" 125 x2="12.5" 126 y2="31.5" 127 /> 128 <!-- mounds --> 129 <g fill="hsl(35,90%,90%)" stroke-width="0"> 130 <path 131 clip-path="url(#sand-mound-top)" 132 d="M 12.25 15 L 15.392 13.486 C 21.737 11.168 22.5 2 22.5 2 L 2 2.013 C 2 2.013 2.753 11.046 9.009 13.438 L 12.25 15 Z" 133 /> 134 <path 135 clip-path="url(#sand-mound-bottom)" 136 d="M 12.25 18.5 L 15.392 20.014 C 21.737 22.332 22.5 31.5 22.5 31.5 L 2 31.487 C 2 31.487 2.753 22.454 9.009 20.062 Z" 137 /> 138 </g> 139 </g> 140 <!-- glass glare --> 141 <g fill="none" opacity="0.7" stroke-linecap="round" stroke-width="2"> 142 <path 143 class="hourglass__glare-top" 144 stroke="hsl(0,0%,100%)" 145 d="M 19.437 3.421 C 19.437 3.421 19.671 6.454 17.914 8.846 C 16.157 11.238 14.5 11.5 14.5 11.5" 146 /> 147 <path 148 class="hourglass__glare-bottom" 149 stroke="hsla(0,0%,100%,0)" 150 d="M 19.437 3.421 C 19.437 3.421 19.671 6.454 17.914 8.846 C 16.157 11.238 14.5 11.5 14.5 11.5" 151 transform="rotate(180,12.25,16.75)" 152 /> 153 </g> 154 <!-- caps --> 155 <rect fill="hsl(var(--hue),90%,50%)" width="24.5" height="2" /> 156 <rect 157 fill="hsl(var(--hue),90%,57.5%)" 158 rx="0.5" 159 ry="0.5" 160 x="2.5" 161 y="0.5" 162 width="19.5" 163 height="1" 164 /> 165 <rect fill="hsl(var(--hue),90%,50%)" y="31.5" width="24.5" height="2" /> 166 <rect 167 fill="hsl(var(--hue),90%,57.5%)" 168 rx="0.5" 169 ry="0.5" 170 x="2.5" 171 y="32" 172 width="19.5" 173 height="1" 174 /> 175 </g> 176 </g> 177 </svg> 178 179</body> 180</html>

🎨 CSS Code


1* { 2 border: 0; 3 box-sizing: border-box; 4 margin: 0; 5 padding: 0; 6} 7 8:root { 9 --hue: 223; 10 --bg: hsl(var(--hue), 90%, 70%); 11 --fg: hsl(var(--hue), 90%, 10%); 12 --primary: hsl(var(--hue), 90%, 50%); 13 --trans-dur: 0.3s; 14 font-size: clamp(1rem, 0.95rem + 0.25vw, 1.25rem); 15} 16 17body { 18 background-color: var(--bg); 19 color: var(--fg); 20 display: flex; 21 font: 1em/1.5 sans-serif; 22 height: 100vh; 23 transition: 24 background-color var(--trans-dur), 25 color var(--trans-dur); 26} 27 28.hourglass { 29 --dur: 2s; 30 display: block; 31 margin: auto; 32 width: 14em; 33 height: auto; 34} 35.hourglass__glare-top, 36.hourglass__glare-bottom, 37.hourglass__model, 38.hourglass__motion-thick, 39.hourglass__motion-medium, 40.hourglass__motion-thin, 41.hourglass__sand-drop, 42.hourglass__sand-fill, 43.hourglass__sand-grain-left, 44.hourglass__sand-grain-right, 45.hourglass__sand-line-left, 46.hourglass__sand-line-right, 47.hourglass__sand-mound-top, 48.hourglass__sand-mound-bottom { 49 animation-duration: var(--dur); 50 animation-timing-function: cubic-bezier(0.83, 0, 0.17, 1); 51 animation-iteration-count: infinite; 52} 53.hourglass__glare-top { 54 animation-name: glare-top; 55} 56.hourglass__glare-bottom { 57 animation-name: glare-bottom; 58} 59.hourglass__model { 60 animation-name: hourglass-flip; 61 transform-origin: 12.25px 16.75px; 62} 63.hourglass__motion-thick, 64.hourglass__motion-medium, 65.hourglass__motion-thin { 66 transform-origin: 26px 26px; 67} 68.hourglass__motion-thick { 69 animation-name: motion-thick; 70} 71.hourglass__motion-medium { 72 animation-name: motion-medium; 73} 74.hourglass__motion-thin { 75 animation-name: motion-thin; 76} 77.hourglass__sand-drop { 78 animation-name: sand-drop; 79} 80.hourglass__sand-fill { 81 animation-name: sand-fill; 82} 83.hourglass__sand-grain-left { 84 animation-name: sand-grain-left; 85} 86.hourglass__sand-grain-right { 87 animation-name: sand-grain-right; 88} 89.hourglass__sand-line-left { 90 animation-name: sand-line-left; 91} 92.hourglass__sand-line-right { 93 animation-name: sand-line-right; 94} 95.hourglass__sand-mound-top { 96 animation-name: sand-mound-top; 97} 98.hourglass__sand-mound-bottom { 99 animation-name: sand-mound-bottom; 100 transform-origin: 12.25px 31.5px; 101} 102 103/* Dark theme */ 104@media (prefers-color-scheme: dark) { 105 :root { 106 --bg: hsl(var(--hue), 90%, 10%); 107 --fg: hsl(var(--hue), 90%, 90%); 108 } 109} 110/* Animation */ 111@keyframes hourglass-flip { 112 from { 113 transform: translate(13.75px, 9.25px) rotate(-180deg); 114 } 115 24%, 116 to { 117 transform: translate(13.75px, 9.25px) rotate(0); 118 } 119} 120@keyframes glare-top { 121 from { 122 stroke: rgba(255, 255, 255, 0); 123 } 124 24%, 125 to { 126 stroke: white; 127 } 128} 129@keyframes glare-bottom { 130 from { 131 stroke: white; 132 } 133 24%, 134 to { 135 stroke: rgba(255, 255, 255, 0); 136 } 137} 138@keyframes motion-thick { 139 from { 140 animation-timing-function: cubic-bezier(0.33, 0, 0.67, 0); 141 stroke: rgba(255, 255, 255, 0); 142 stroke-dashoffset: 153.94; 143 transform: rotate(0.67turn); 144 } 145 20% { 146 animation-timing-function: cubic-bezier(0.33, 1, 0.67, 1); 147 stroke: white; 148 stroke-dashoffset: 141.11; 149 transform: rotate(1turn); 150 } 151 40%, 152 to { 153 stroke: rgba(255, 255, 255, 0); 154 stroke-dashoffset: 153.94; 155 transform: rotate(1.33turn); 156 } 157} 158@keyframes motion-medium { 159 from, 160 8% { 161 animation-timing-function: cubic-bezier(0.33, 0, 0.67, 0); 162 stroke: rgba(255, 255, 255, 0); 163 stroke-dashoffset: 153.94; 164 transform: rotate(0.5turn); 165 } 166 20% { 167 animation-timing-function: cubic-bezier(0.33, 1, 0.67, 1); 168 stroke: white; 169 stroke-dashoffset: 147.53; 170 transform: rotate(0.83turn); 171 } 172 32%, 173 to { 174 stroke: rgba(255, 255, 255, 0); 175 stroke-dashoffset: 153.94; 176 transform: rotate(1.17turn); 177 } 178} 179@keyframes motion-thin { 180 from, 181 4% { 182 animation-timing-function: cubic-bezier(0.33, 0, 0.67, 0); 183 stroke: rgba(255, 255, 255, 0); 184 stroke-dashoffset: 153.94; 185 transform: rotate(0.33turn); 186 } 187 24% { 188 animation-timing-function: cubic-bezier(0.33, 1, 0.67, 1); 189 stroke: white; 190 stroke-dashoffset: 134.7; 191 transform: rotate(0.67turn); 192 } 193 44%, 194 to { 195 stroke: rgba(255, 255, 255, 0); 196 stroke-dashoffset: 153.94; 197 transform: rotate(1turn); 198 } 199} 200@keyframes sand-drop { 201 from, 202 10% { 203 animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); 204 stroke-dashoffset: 1; 205 } 206 70%, 207 to { 208 stroke-dashoffset: -107; 209 } 210} 211@keyframes sand-fill { 212 from, 213 10% { 214 animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); 215 stroke-dashoffset: 55; 216 } 217 70%, 218 to { 219 stroke-dashoffset: -54; 220 } 221} 222@keyframes sand-grain-left { 223 from, 224 10% { 225 animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); 226 stroke-dashoffset: 29; 227 } 228 70%, 229 to { 230 stroke-dashoffset: -22; 231 } 232} 233@keyframes sand-grain-right { 234 from, 235 10% { 236 animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); 237 stroke-dashoffset: 27; 238 } 239 70%, 240 to { 241 stroke-dashoffset: -24; 242 } 243} 244@keyframes sand-line-left { 245 from, 246 10% { 247 animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); 248 stroke-dashoffset: 53; 249 } 250 70%, 251 to { 252 stroke-dashoffset: -55; 253 } 254} 255@keyframes sand-line-right { 256 from, 257 10% { 258 animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); 259 stroke-dashoffset: 14; 260 } 261 70%, 262 to { 263 stroke-dashoffset: -24.5; 264 } 265} 266@keyframes sand-mound-top { 267 from, 268 10% { 269 animation-timing-function: linear; 270 transform: translate(0, 0); 271 } 272 15% { 273 animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); 274 transform: translate(0, 1.5px); 275 } 276 51%, 277 to { 278 transform: translate(0, 13px); 279 } 280} 281@keyframes sand-mound-bottom { 282 from, 283 31% { 284 animation-timing-function: cubic-bezier(0.61, 1, 0.88, 1); 285 transform: scale(1, 0); 286 } 287 56%, 288 to { 289 transform: scale(1, 1); 290 } 291}

How It Works


  1. SVG as the Animation Engine

The entire loader is vector-based

Shapes, sand, and strokes are individual SVG elements

Clip paths simulate sand mounds

  1. CSS-Only Animation Logic

stroke-dasharray and stroke-dashoffset animate sand flow

Rotation simulates flipping of the hourglass

Cubic-bezier easing creates natural motion

  1. Sand Simulation

Falling sand grains use dashed strokes

Sand fill lines move downward

Top mound shrinks while bottom mound grows

  1. Motion Feedback

Three rotating circular strokes act as progress indicators

Different stroke widths create depth

Key Features


⏳ Pure SVG + CSS (no JavaScript)

🎨 Theme-aware color variables

🔁 Seamless infinite loop

📐 Fully scalable & responsive

🌙 Dark mode support

⚡ Lightweight and performant

Use Cases

Website preloaders

App splash screens

Portfolio micro-interactions

Creative UI loaders

Modern SaaS loading states

Love this component?

Explore more components and build amazing UIs.

View All Components