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
5 satisfying checkbox animations

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.

More from the library

See the whole library