25 CSS Spinners 13 / 25
Clock Tick Sweep Spinner
A precise gold second-hand sweeps in discrete tick steps around a minimal dark clock face with 12 hour markers, using CSS steps() timing to simulate mechanical tick motion.
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="sp-13">
<div class="sp-13__clock">
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__hand"></div>
<div class="sp-13__center"></div>
</div>
</div> <div class="sp-13">
<div class="sp-13__clock">
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__tick"></div>
<div class="sp-13__hand"></div>
<div class="sp-13__center"></div>
</div>
</div>.sp-13,.sp-13 *,.sp-13 *::before,.sp-13 *::after{box-sizing:border-box;margin:0;padding:0}
.sp-13{
--bg:#0d0d0d;
--face:#111;
--ring:#e8e0d0;
--hand:#f5c518;
--tick:rgba(232,224,208,0.4);
display:flex;
align-items:center;
justify-content:center;
min-height:100vh;
background:var(--bg);
}
.sp-13__clock{
position:relative;
width:80px;
height:80px;
background:var(--face);
border-radius:50%;
border:2px solid var(--ring);
box-shadow:0 0 0 1px rgba(232,224,208,0.1),inset 0 0 16px rgba(0,0,0,0.5);
}
.sp-13__tick{
position:absolute;
top:50%;
left:50%;
width:2px;
height:6px;
margin-left:-1px;
background:var(--tick);
transform-origin:bottom center;
border-radius:1px;
}
.sp-13__tick:nth-child(1){transform:translateY(-100%) translateY(-27px) rotate(0deg)}
.sp-13__tick:nth-child(2){transform:translateY(-100%) translateY(-27px) rotate(30deg)}
.sp-13__tick:nth-child(3){transform:translateY(-100%) translateY(-27px) rotate(60deg)}
.sp-13__tick:nth-child(4){transform:translateY(-100%) translateY(-27px) rotate(90deg)}
.sp-13__tick:nth-child(5){transform:translateY(-100%) translateY(-27px) rotate(120deg)}
.sp-13__tick:nth-child(6){transform:translateY(-100%) translateY(-27px) rotate(150deg)}
.sp-13__tick:nth-child(7){transform:translateY(-100%) translateY(-27px) rotate(180deg)}
.sp-13__tick:nth-child(8){transform:translateY(-100%) translateY(-27px) rotate(210deg)}
.sp-13__tick:nth-child(9){transform:translateY(-100%) translateY(-27px) rotate(240deg)}
.sp-13__tick:nth-child(10){transform:translateY(-100%) translateY(-27px) rotate(270deg)}
.sp-13__tick:nth-child(11){transform:translateY(-100%) translateY(-27px) rotate(300deg)}
.sp-13__tick:nth-child(12){transform:translateY(-100%) translateY(-27px) rotate(330deg)}
.sp-13__hand{
position:absolute;
bottom:50%;
left:50%;
width:2px;
height:26px;
margin-left:-1px;
background:var(--hand);
border-radius:2px 2px 0 0;
transform-origin:bottom center;
box-shadow:0 0 4px var(--hand);
animation:sp-13-tick 1s steps(60,end) infinite;
}
.sp-13__center{
position:absolute;
top:50%;
left:50%;
width:6px;
height:6px;
border-radius:50%;
background:var(--hand);
transform:translate(-50%,-50%);
box-shadow:0 0 4px var(--hand);
}
@keyframes sp-13-tick{
to{transform:rotate(360deg)}
}
@media (prefers-reduced-motion: reduce){
.sp-13__hand{animation:none}
} .sp-13,.sp-13 *,.sp-13 *::before,.sp-13 *::after{box-sizing:border-box;margin:0;padding:0}
.sp-13{
--bg:#0d0d0d;
--face:#111;
--ring:#e8e0d0;
--hand:#f5c518;
--tick:rgba(232,224,208,0.4);
display:flex;
align-items:center;
justify-content:center;
min-height:100vh;
background:var(--bg);
}
.sp-13__clock{
position:relative;
width:80px;
height:80px;
background:var(--face);
border-radius:50%;
border:2px solid var(--ring);
box-shadow:0 0 0 1px rgba(232,224,208,0.1),inset 0 0 16px rgba(0,0,0,0.5);
}
.sp-13__tick{
position:absolute;
top:50%;
left:50%;
width:2px;
height:6px;
margin-left:-1px;
background:var(--tick);
transform-origin:bottom center;
border-radius:1px;
}
.sp-13__tick:nth-child(1){transform:translateY(-100%) translateY(-27px) rotate(0deg)}
.sp-13__tick:nth-child(2){transform:translateY(-100%) translateY(-27px) rotate(30deg)}
.sp-13__tick:nth-child(3){transform:translateY(-100%) translateY(-27px) rotate(60deg)}
.sp-13__tick:nth-child(4){transform:translateY(-100%) translateY(-27px) rotate(90deg)}
.sp-13__tick:nth-child(5){transform:translateY(-100%) translateY(-27px) rotate(120deg)}
.sp-13__tick:nth-child(6){transform:translateY(-100%) translateY(-27px) rotate(150deg)}
.sp-13__tick:nth-child(7){transform:translateY(-100%) translateY(-27px) rotate(180deg)}
.sp-13__tick:nth-child(8){transform:translateY(-100%) translateY(-27px) rotate(210deg)}
.sp-13__tick:nth-child(9){transform:translateY(-100%) translateY(-27px) rotate(240deg)}
.sp-13__tick:nth-child(10){transform:translateY(-100%) translateY(-27px) rotate(270deg)}
.sp-13__tick:nth-child(11){transform:translateY(-100%) translateY(-27px) rotate(300deg)}
.sp-13__tick:nth-child(12){transform:translateY(-100%) translateY(-27px) rotate(330deg)}
.sp-13__hand{
position:absolute;
bottom:50%;
left:50%;
width:2px;
height:26px;
margin-left:-1px;
background:var(--hand);
border-radius:2px 2px 0 0;
transform-origin:bottom center;
box-shadow:0 0 4px var(--hand);
animation:sp-13-tick 1s steps(60,end) infinite;
}
.sp-13__center{
position:absolute;
top:50%;
left:50%;
width:6px;
height:6px;
border-radius:50%;
background:var(--hand);
transform:translate(-50%,-50%);
box-shadow:0 0 4px var(--hand);
}
@keyframes sp-13-tick{
to{transform:rotate(360deg)}
}
@media (prefers-reduced-motion: reduce){
.sp-13__hand{animation:none}
}How this works
The clock face is a circle styled with a dark background and a subtle inset shadow for depth, with 12 small tick marks positioned using the same radial offset technique as clock hands — each tick is a thin rectangle placed at top:50%;left:50%, rotated by N×30deg, then translated outward. The 12 individual nth-child rules pre-rotate each tick to its hour position.
The hand element uses bottom:50% (so it grows upward from the pivot) with transform-origin:bottom center. Its animation uses steps(60, end) timing — this makes the hand snap to each of 60 discrete positions per revolution rather than smoothly easing, exactly replicating the tick-tick motion of a mechanical clock second hand.
Customize
- Change the hand colour via
--hand— a red (#e53935) hand on a white face creates a classic kitchen-timer look. - Add a minute hand by duplicating the hand element with a longer duration (
60s) and a different colour. - Replace
steps(60,end)withlinearfor a smooth sweep hand (quartz vs mechanical analogy). - Increase tick mark height from
6pxto10pxand add a larger square every 3rd tick usingnth-child(3n)for a more detailed clock face. - Wrap in a larger outer bezel div with a metallic
background:conic-gradient(...)for a watch-case appearance.
Watch out for
steps(60,end)produces 60 positions per full rotation — for a spinner rather than a clock,steps(8,end)orsteps(12,end)gives a crisper mechanical feel without requiring a full second-hand metaphor.- The tick marks use a specific translate distance tuned to the 80px wrapper — resizing the clock requires recalculating the
translateY(-27px)offset to keep ticks inside the ring. - Using
bottom:50%instead oftop:50%for the hand positioning means the pivot point is at the visual centre — changing totop:50%and adjusting height will invert the hand direction.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 60+ | 12+ | 60+ | 60+ |
steps() timing function well-supported; no modern-only APIs needed.