<form>
	<input id="a" type="radio" name="hopping" value="a" checked>
	<label for="a"><span></span>A</label>
	<input id="b" type="radio" name="hopping" value="b">
	<label for="b"><span></span>B</label>
	<input id="c" type="radio" name="hopping" value="c">
	<label for="c"><span></span>C</label>
	<div class="worm">
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
		<div class="worm__segment"></div>
	</div>
</form>
$wormDur: 0.4s
$radioDur: 0.2s
$timing1: cubic-bezier(0.45,0.05,0.55,0.95)
$timing2: cubic-bezier(0.5,0,0.5,2)

*
	border: 0
	box-sizing: border-box
	margin: 0
	padding: 0

\:root
	font-size: calc(32px + (40 - 32)*(100vw - 320px)/ (1024 - 320))

body
	background: #f1f1f1
	color: #171717
	display: flex
	font: 1em Ubuntu, sans-serif
	height: 100vh
	line-height: 1.5
	padding: 1.5em 0

form
	display: block
	margin: auto
	max-width: 10em
	position: relative

input
	position: fixed
	top: -1.5em
	left: -1.5em

label
	$shadowColor: rgba(0,0,0,0.2)
	cursor: pointer
	display: block
	font-weight: bold
	text-shadow: 0 0.1em 0.1em $shadowColor
	transition: color $radioDur $timing1
	&:not(:last-of-type)
		margin-bottom: 1.5em
	span
		box-shadow: 0 0 0 0.2em currentColor inset, 0 0.2em 0.2em $shadowColor, 0 0.3em 0.2em $shadowColor inset
		display: inline-block
		margin-right: 0.5em
		vertical-align: bottom
		width: 1.5em
		height: 1.5em
		transition: transform $radioDur $timing2, box-shadow $radioDur $timing1, color $radioDur $timing1

label span,
.worm__segment:before
	border-radius: 50%

input:checked + label,
input:checked + label span,
.worm__segment:before
	color: #2762f3

input:checked + label
	&, span
		transition-delay: $wormDur
	span
		transform: scale(1.2)

.worm
	top: 0.375em
	left: 0.375em
	&, &__segment
		position: absolute
	&__segment
		top: 0
		left: 0
		width: 0.75em
		height: 0.75em
		transition: transform $wormDur $timing1
		&:before
			animation-duration: $wormDur
			animation-timing-function: $timing1
			background: currentColor
			content: ""
			display: block
			width: 100%
			height: 100%
		&:first-child, &:last-child
			&:before
				box-shadow: 0 0 1em 0 currentColor

@for $s from 2 through 30
	$delay: $wormDur/100 * ($s - 1)
	.worm__segment:nth-child(#{$s})
		transition-delay: $delay
		&:before
			animation-delay: $delay

/* States */
@for $s from 1 through 3
	input:nth-of-type(#{$s}):checked ~ .worm .worm__segment
		@if $s > 1
			transform: translateY(3em * ($s - 1))
		&:before
			animation-name: hop#{$s}

	@keyframes hop#{$s}
		from, to
			transform: translateX(0)
		50%
			transform: translateX(-1.5em)

/* Dark mode */
@media screen and (prefers-color-scheme: dark)
	body
		background: #242424
		color: #f1f1f1
	
	input:checked + label,
	input:checked + label span, 
	.worm__segment:before
		color: #5785f6
View Compiled

External CSS

  1. https://fonts.googleapis.com/css?family=Ubuntu:400,700&amp;display=swap

External JavaScript

This Pen doesn't use any external JavaScript resources.