Let’s create an Hourglass Preloader using HTML and CSS. This fun and eye-catching animation mimics an hourglass flipping and filling with sand – perfect for keeping users entertained while your content loads.
We’ll use:
- HTML to set up the hourglass structure.
- CSS to design and animate the flipping and sand flow effect.
This project is simple to build and great for learning how to use CSS animations to create engaging loaders. Whether you’re a beginner or just exploring creative ideas, this hourglass preloader is a cool way to level up your front-end skills! ⏳✨
HTML :
This HTML code creates an animated SVG hourglass preloader with falling sand and rotating circular strokes. It uses clip paths, HSL colors, and CSS variables for a customizable, smooth-loading effect. The design includes realistic glass reflections, motion effects, and accessibility labels, making it ideal for visually appealing loaders.
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>Hourglass Preloader | @coding.stella</title> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"><link rel="stylesheet" href="./style.css"> </head> <body> <svg class="hourglass" viewBox="0 0 56 56" width="56px" height="56px" role="img" aria-label="Hourglass being flipped clockwise and circled by three white curves fading in and out" > <clipPath id="sand-mound-top"> <path class="hourglass__sand-mound-top" 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" /> </clipPath> <clipPath id="sand-mound-bottom"> <path class="hourglass__sand-mound-bottom" 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" /> </clipPath> <g transform="translate(2,2)"> <g fill="none" stroke="hsl(0,0%,100%)" stroke-dasharray="153.94 153.94" stroke-dashoffset="153.94" stroke-linecap="round" transform="rotate(-90,26,26)" > <circle class="hourglass__motion-thick" stroke-width="2.5" cx="26" cy="26" r="24.5" transform="rotate(0,26,26)" /> <circle class="hourglass__motion-medium" stroke-width="1.75" cx="26" cy="26" r="24.5" transform="rotate(90,26,26)" /> <circle class="hourglass__motion-thin" stroke-width="1" cx="26" cy="26" r="24.5" transform="rotate(180,26,26)" /> </g> <g class="hourglass__model" transform="translate(13.75,9.25)"> <!-- glass --> <path fill="hsl(var(--hue),90%,85%)" 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" /> <!-- sand --> <g stroke="hsl(35,90%,90%)" stroke-linecap="round"> <line class="hourglass__sand-grain-left" stroke-width="1" stroke-dasharray="0.25 33.75" x1="12" y1="15.75" x2="12" y2="20.75" /> <line class="hourglass__sand-grain-right" stroke-width="1" stroke-dasharray="0.25 33.75" x1="12.5" y1="16.75" x2="12.5" y2="21.75" /> <line class="hourglass__sand-drop" stroke-width="1" stroke-dasharray="0.5 107.5" x1="12.25" y1="18" x2="12.25" y2="31.5" /> <line class="hourglass__sand-fill" stroke-width="1.5" stroke-dasharray="54 54" x1="12.25" y1="14.75" x2="12.25" y2="31.5" /> <line class="hourglass__sand-line-left" stroke="hsl(35,90%,83%)" stroke-width="1" stroke-dasharray="1 107" x1="12" y1="16" x2="12" y2="31.5" /> <line class="hourglass__sand-line-right" stroke="hsl(35,90%,83%)" stroke-width="1" stroke-dasharray="12 96" x1="12.5" y1="16" x2="12.5" y2="31.5" /> <!-- mounds --> <g fill="hsl(35,90%,90%)" stroke-width="0"> <path clip-path="url(#sand-mound-top)" 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" /> <path clip-path="url(#sand-mound-bottom)" 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" /> </g> </g> <!-- glass glare --> <g fill="none" opacity="0.7" stroke-linecap="round" stroke-width="2"> <path class="hourglass__glare-top" stroke="hsl(0,0%,100%)" 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" /> <path class="hourglass__glare-bottom" stroke="hsla(0,0%,100%,0)" 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" transform="rotate(180,12.25,16.75)" /> </g> <!-- caps --> <rect fill="hsl(var(--hue),90%,50%)" width="24.5" height="2" /> <rect fill="hsl(var(--hue),90%,57.5%)" rx="0.5" ry="0.5" x="2.5" y="0.5" width="19.5" height="1" /> <rect fill="hsl(var(--hue),90%,50%)" y="31.5" width="24.5" height="2" /> <rect fill="hsl(var(--hue),90%,57.5%)" rx="0.5" ry="0.5" x="2.5" y="32" width="19.5" height="1" /> </g> </g> </svg> </body> </html>
CSS :
This CSS code styles and animates an hourglass graphic using custom properties (--vars
) for consistent theming, smooth transitions, and responsive sizing. It resets default browser styles, defines a color scheme that adapts to light/dark mode, and uses keyframe animations to simulate sand falling, glass flipping, and moving glare. Each part of the hourglass (sand, glare, motion lines) has a continuous animation with unique timing for a realistic flowing effect. The layout is centered using Flexbox, and the entire visual adjusts smoothly across devices.
* { border: 0; box-sizing: border-box; margin: 0; padding: 0; } :root { --hue: 223; --bg: hsl(var(--hue), 90%, 70%); --fg: hsl(var(--hue), 90%, 10%); --primary: hsl(var(--hue), 90%, 50%); --trans-dur: 0.3s; font-size: clamp(1rem, 0.95rem + 0.25vw, 1.25rem); } body { background-color: var(--bg); color: var(--fg); display: flex; font: 1em/1.5 sans-serif; height: 100vh; transition: background-color var(--trans-dur), color var(--trans-dur); } .hourglass { --dur: 2s; display: block; margin: auto; width: 14em; height: auto; } .hourglass__glare-top, .hourglass__glare-bottom, .hourglass__model, .hourglass__motion-thick, .hourglass__motion-medium, .hourglass__motion-thin, .hourglass__sand-drop, .hourglass__sand-fill, .hourglass__sand-grain-left, .hourglass__sand-grain-right, .hourglass__sand-line-left, .hourglass__sand-line-right, .hourglass__sand-mound-top, .hourglass__sand-mound-bottom { animation-duration: var(--dur); animation-timing-function: cubic-bezier(0.83, 0, 0.17, 1); animation-iteration-count: infinite; } .hourglass__glare-top { animation-name: glare-top; } .hourglass__glare-bottom { animation-name: glare-bottom; } .hourglass__model { animation-name: hourglass-flip; transform-origin: 12.25px 16.75px; } .hourglass__motion-thick, .hourglass__motion-medium, .hourglass__motion-thin { transform-origin: 26px 26px; } .hourglass__motion-thick { animation-name: motion-thick; } .hourglass__motion-medium { animation-name: motion-medium; } .hourglass__motion-thin { animation-name: motion-thin; } .hourglass__sand-drop { animation-name: sand-drop; } .hourglass__sand-fill { animation-name: sand-fill; } .hourglass__sand-grain-left { animation-name: sand-grain-left; } .hourglass__sand-grain-right { animation-name: sand-grain-right; } .hourglass__sand-line-left { animation-name: sand-line-left; } .hourglass__sand-line-right { animation-name: sand-line-right; } .hourglass__sand-mound-top { animation-name: sand-mound-top; } .hourglass__sand-mound-bottom { animation-name: sand-mound-bottom; transform-origin: 12.25px 31.5px; } /* Dark theme */ @media (prefers-color-scheme: dark) { :root { --bg: hsl(var(--hue), 90%, 10%); --fg: hsl(var(--hue), 90%, 90%); } } /* Animation */ @keyframes hourglass-flip { from { transform: translate(13.75px, 9.25px) rotate(-180deg); } 24%, to { transform: translate(13.75px, 9.25px) rotate(0); } } @keyframes glare-top { from { stroke: rgba(255, 255, 255, 0); } 24%, to { stroke: white; } } @keyframes glare-bottom { from { stroke: white; } 24%, to { stroke: rgba(255, 255, 255, 0); } } @keyframes motion-thick { from { animation-timing-function: cubic-bezier(0.33, 0, 0.67, 0); stroke: rgba(255, 255, 255, 0); stroke-dashoffset: 153.94; transform: rotate(0.67turn); } 20% { animation-timing-function: cubic-bezier(0.33, 1, 0.67, 1); stroke: white; stroke-dashoffset: 141.11; transform: rotate(1turn); } 40%, to { stroke: rgba(255, 255, 255, 0); stroke-dashoffset: 153.94; transform: rotate(1.33turn); } } @keyframes motion-medium { from, 8% { animation-timing-function: cubic-bezier(0.33, 0, 0.67, 0); stroke: rgba(255, 255, 255, 0); stroke-dashoffset: 153.94; transform: rotate(0.5turn); } 20% { animation-timing-function: cubic-bezier(0.33, 1, 0.67, 1); stroke: white; stroke-dashoffset: 147.53; transform: rotate(0.83turn); } 32%, to { stroke: rgba(255, 255, 255, 0); stroke-dashoffset: 153.94; transform: rotate(1.17turn); } } @keyframes motion-thin { from, 4% { animation-timing-function: cubic-bezier(0.33, 0, 0.67, 0); stroke: rgba(255, 255, 255, 0); stroke-dashoffset: 153.94; transform: rotate(0.33turn); } 24% { animation-timing-function: cubic-bezier(0.33, 1, 0.67, 1); stroke: white; stroke-dashoffset: 134.7; transform: rotate(0.67turn); } 44%, to { stroke: rgba(255, 255, 255, 0); stroke-dashoffset: 153.94; transform: rotate(1turn); } } @keyframes sand-drop { from, 10% { animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); stroke-dashoffset: 1; } 70%, to { stroke-dashoffset: -107; } } @keyframes sand-fill { from, 10% { animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); stroke-dashoffset: 55; } 70%, to { stroke-dashoffset: -54; } } @keyframes sand-grain-left { from, 10% { animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); stroke-dashoffset: 29; } 70%, to { stroke-dashoffset: -22; } } @keyframes sand-grain-right { from, 10% { animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); stroke-dashoffset: 27; } 70%, to { stroke-dashoffset: -24; } } @keyframes sand-line-left { from, 10% { animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); stroke-dashoffset: 53; } 70%, to { stroke-dashoffset: -55; } } @keyframes sand-line-right { from, 10% { animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); stroke-dashoffset: 14; } 70%, to { stroke-dashoffset: -24.5; } } @keyframes sand-mound-top { from, 10% { animation-timing-function: linear; transform: translate(0, 0); } 15% { animation-timing-function: cubic-bezier(0.12, 0, 0.39, 0); transform: translate(0, 1.5px); } 51%, to { transform: translate(0, 13px); } } @keyframes sand-mound-bottom { from, 31% { animation-timing-function: cubic-bezier(0.61, 1, 0.88, 1); transform: scale(1, 0); } 56%, to { transform: scale(1, 1); } }
In conclusion, the Hourglass Preloader using HTML and CSS adds a creative and stylish touch to loading screens. It’s a simple project that showcases how CSS animations can bring static designs to life. Keep experimenting and make your websites more fun and interactive!
If your project has problems, don’t worry. Just click to download the source code and face your coding challenges with excitement. Have fun coding!