5 satisfying checkbox animations
Five ways to tick a to do, each on a real checkbox with a label. Tick them to try.
UI loop
5 satisfying ticks
Sundry Millsundrymill.com/library
Everything in the preview is live. Try it.
The code, one by one
Shared: the base every checkbox uses
.ck {
position: relative;
display: flex;
align-items: center;
gap: 12px;
min-height: 44px;
font-weight: 600;
cursor: pointer;
}
.ck input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.ck-box { flex: none; display: grid; place-items: center; width: 26px; height: 26px; }
.ck-box svg { width: 18px; height: 18px; fill: none; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.ck input:focus-visible + .ck-box { outline: 3px solid #E8631A; outline-offset: 3px; }
Draw
The tick draws itself in one stroke.
HTML
<label class="ck ck-draw"><input type="checkbox" checked><span class="ck-box"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>Water the plants</label>
CSS
.ck-draw .ck-box {
border: 2px solid #17202C;
border-radius: 8px;
transition: background-color .2s;
}
.ck-draw svg {
stroke: #FDF9F1;
stroke-dasharray: 20;
stroke-dashoffset: 20;
transition: stroke-dashoffset .35s cubic-bezier(.65, 0, .35, 1) .05s;
}
.ck-draw input:checked + .ck-box { background: #17202C; }
.ck-draw input:checked + .ck-box svg { stroke-dashoffset: 0; }
Pop
A little bounce, so the tick feels earned.
HTML
<label class="ck ck-pop"><input type="checkbox"><span class="ck-box"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>Reply to Sam</label>
CSS
.ck-pop .ck-box {
border: 2px solid #17202C;
border-radius: 50%;
transition: background-color .2s, border-color .2s;
}
.ck-pop svg { stroke: #17202C; opacity: 0; transform: scale(.4); transition: opacity .2s, transform .35s cubic-bezier(.34, 1.56, .64, 1); }
.ck-pop input:checked + .ck-box {
background: #FF7A2F;
border-color: #FF7A2F;
animation: ck-pop .4s cubic-bezier(.34, 1.56, .64, 1);
}
.ck-pop input:checked + .ck-box svg { opacity: 1; transform: none; }
@keyframes ck-pop { 0% { transform: scale(.8); } 60% { transform: scale(1.15); } 100% { transform: scale(1); } }
Fill
Colour floods in from the edges, then the tick appears.
HTML
<label class="ck ck-fill"><input type="checkbox" checked><span class="ck-box"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>Book the dentist</label>
CSS
.ck-fill .ck-box {
border: 2px solid #2F55D4;
border-radius: 7px;
box-shadow: inset 0 0 0 0 #2F55D4;
transition: box-shadow .3s cubic-bezier(.65, 0, .35, 1);
}
.ck-fill svg { stroke: #FFFFFF; opacity: 0; transition: opacity .2s .15s; }
.ck-fill input:checked + .ck-box { box-shadow: inset 0 0 0 14px #2F55D4; }
.ck-fill input:checked + .ck-box svg { opacity: 1; }
Strike
A to do list classic: the task gets crossed out.
HTML
<label class="ck ck-strike"><input type="checkbox" checked><span class="ck-box"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span><span class="ck-text">Buy oat milk</span></label>
CSS
.ck-strike .ck-box { border: 2px solid #17202C; border-radius: 6px; }
.ck-strike svg { stroke: #17202C; opacity: 0; transition: opacity .2s; }
.ck-strike .ck-text {
background: linear-gradient(#17202C, #17202C) no-repeat 0 55% / 0 2px;
transition: background-size .35s cubic-bezier(.65, 0, .35, 1), color .35s;
}
.ck-strike input:checked + .ck-box svg { opacity: 1; }
.ck-strike input:checked ~ .ck-text { background-size: 100% 2px; color: #6B7280; }
Burst
A round check that sends out a ring when you tick it.
HTML
<label class="ck ck-burst"><input type="checkbox"><span class="ck-box"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>Go for a walk</label>
CSS
.ck-burst .ck-box {
position: relative;
width: 28px;
height: 28px;
border: 2px solid #1E8A50;
border-radius: 50%;
transition: background-color .25s;
}
.ck-burst .ck-box::after {
content: "";
position: absolute;
inset: -2px;
border-radius: 50%;
border: 2px solid #1E8A50;
opacity: 0;
}
.ck-burst svg { stroke: #FFFFFF; transform: scale(0); transition: transform .3s cubic-bezier(.34, 1.56, .64, 1); }
.ck-burst input:checked + .ck-box { background: #1E8A50; }
.ck-burst input:checked + .ck-box svg { transform: none; }
.ck-burst input:checked + .ck-box::after { animation: ck-burst .5s ease-out; }
@keyframes ck-burst { from { transform: scale(1); opacity: .8; } to { transform: scale(1.9); opacity: 0; } }
How to use it
- The real checkbox is hidden but still there, so clicking the label, pressing Space and screen readers all work.
- Each style needs the shared base CSS plus its own block.
- Strike suits to do lists, Draw and Fill suit forms, Pop and Burst suit habit trackers where ticking should feel like a small win.
Showing an interaction like this to a client or in a tutorial? Framok records your screen and zooms in on every click for you.
Free to use in personal and commercial work. No credit needed, though a link back is always nice.