Skip to main content
CSS Tip

Bouncy radio buttons using modern CSS

A fun CSS-only experimentation using modern features to create bouncy radio buttons. Powered by anchor positioning, @property, :has() selector, and more!

@property --_x {
syntax: "<number>";
inherits: false;
initial-value: 0;
}
.container {
--s: 1em; /* control the size */
--c: #009688; /* the active color */

display: grid;
position: relative;
}
.container:before {
content: "";
position: absolute;
position-anchor: --checkbox;
height: calc(var(--s)/2);
aspect-ratio: 1;
--_y: clamp(-1,var(--_x)*9999,1); /* until better support for sign() */
left: max(-80px,var(--s)/4 - var(--_x)*var(--_y)*1px);
top: calc(anchor(top) + var(--s)/4);
background: var(--c);
border-radius: 50%;
transition: top .4s linear,--_x cubic-bezier(.1,3000,.7,3000) .4s;
}
input {
height: var(--s);
aspect-ratio: 1;
border: calc(var(--s)/8) solid #939393;
border-radius: 50%;
outline-offset: calc(var(--s)/10);
appearance: none;
transition: .3s .1s;
}
input:checked {
border-color: var(--c);
anchor-name: --checkbox;
}
/* The hacky part ... */
.container:has(label:nth-child(1) input:checked):before {--_x:.01}
.container:has(label:nth-child(2) input:checked):before {--_x:.02}
.container:has(label:nth-child(3) input:checked):before {--_x:.03}
/* .container:has(label:nth-child(N) input:checked):before {--_x:.0N}*/

Click for a cool effect!

See the Pen Bouncy input radio! by Temani Afif (@t_afif) on CodePen.

And the horizontal version as well

See the Pen Bouncy input radio II by Temani Afif (@t_afif) on CodePen.

A related article to understand the logic behind the hacky part and the strange cubic-bezier(): Advanced CSS Animation Using cubic-bezier()


More CSS Tips