<div class="grid">
<label class="radio">
<input type="radio" name="r" value="1" checked />
<svg viewBox="0 0 24 24" filter="url(#goo-light)">
<circle class="top" cx="12" cy="-12" r="8" />
<circle class="dot" cx="12" cy="12" r="5" />
<circle class="drop" cx="12" cy="12" r="2" />
</svg>
</label>
<label class="radio">
<input type="radio" name="r" value="2" />
<svg viewBox="0 0 24 24" filter="url(#goo-light)">
<circle class="top" cx="12" cy="-12" r="8" />
<circle class="dot" cx="12" cy="12" r="5" />
<circle class="drop" cx="12" cy="12" r="2" />
</svg>
</label>
<label class="switch">
<input type="checkbox" checked />
<svg viewBox="0 0 38 24" filter="url(#goo)">
<circle class="default" cx="12" cy="12" r="8" />
<circle class="dot" cx="26" cy="12" r="8" />
<circle class="drop" cx="25" cy="-1" r="2" />
</svg>
</label>
<label class="switch">
<input type="checkbox" />
<svg viewBox="0 0 38 24" filter="url(#goo)">
<circle class="default" cx="12" cy="12" r="8" />
<circle class="dot" cx="26" cy="12" r="8" />
</svg>
</label>
<label class="checkbox">
<input type="checkbox" checked />
<svg viewBox="0 0 24 24" filter="url(#goo-light)">
<path class="tick" d="M4.5 10L10.5 16L24.5 1" />
<circle class="dot" cx="10.5" cy="15.5" r="1.5" />
</svg>
</label>
<label class="checkbox">
<input type="checkbox" />
<svg viewBox="0 0 24 24" filter="url(#goo-light)">
<path class="tick" d="M4.5 10L10.5 16L24.5 1" />
<circle class="dot" cx="10.5" cy="15.5" r="1.5" />
<circle class="drop" cx="25" cy="-1" r="2" />
</svg>
</label>
<div class="last">
<button class="btn">
<span>Submit</span>
<svg preserveAspectRatio="none" viewBox="0 0 132 45">
<g clip-path="url(#clip)" filter="url(#goo-big)">
<circle class="top-left" cx="49.5" cy="-0.5" r="26.5" />
<circle class="middle-bottom" cx="70.5" cy="40.5" r="26.5" />
<circle class="top-right" cx="104" cy="6.5" r="27" />
<circle class="right-bottom" cx="123.5" cy="36.5" r="26.5" />
<circle class="left-bottom" cx="16.5" cy="28" r="30" />
</g>
<defs>
<clipPath id="clip">
<rect width="132" height="45" rx="7" />
</clipPath>
</defs>
</svg>
</button>
</div>
</div>
<svg width="0" height="0">
<defs>
<filter id="goo" x="-50%" width="200%" y="-50%" height="200%" color-interpolation-filters="sRGB">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur" />
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 21 -7" result="cm" />
</filter>
<filter id="goo-light" x="-50%" width="200%" y="-50%" height="200%" color-interpolation-filters="sRGB">
<feGaussianBlur in="SourceGraphic" stdDeviation="1.25" result="blur" />
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 21 -7" result="cm" />
</filter>
<filter id="goo-big" x="-50%" width="200%" y="-50%" height="200%" color-interpolation-filters="sRGB">
<feGaussianBlur in="SourceGraphic" stdDeviation="7" result="blur" />
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 21 -7" result="cm" />
</filter>
</defs>
</svg>
<!-- dribbble - twitter -->
<a class="dribbble" href="https://dribbble.com/ai" target="_blank"><img src="https://cdn.dribbble.com/assets/dribbble-ball-mark-2bd45f09c2fb58dbbfb44766d5d1d07c5a12972d602ef8b32204d28fa3dda554.svg" alt=""></a>
<a class="twitter" target="_top" href="https://twitter.com/aaroniker_me"><svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" viewBox="0 0 72 72"><path d="M67.812 16.141a26.246 26.246 0 0 1-7.519 2.06 13.134 13.134 0 0 0 5.756-7.244 26.127 26.127 0 0 1-8.313 3.176A13.075 13.075 0 0 0 48.182 10c-7.229 0-13.092 5.861-13.092 13.093 0 1.026.118 2.021.338 2.981-10.885-.548-20.528-5.757-26.987-13.679a13.048 13.048 0 0 0-1.771 6.581c0 4.542 2.312 8.551 5.824 10.898a13.048 13.048 0 0 1-5.93-1.638c-.002.055-.002.11-.002.162 0 6.345 4.513 11.638 10.504 12.84a13.177 13.177 0 0 1-3.449.457c-.846 0-1.667-.078-2.465-.231 1.667 5.2 6.499 8.986 12.23 9.09a26.276 26.276 0 0 1-16.26 5.606A26.21 26.21 0 0 1 4 55.976a37.036 37.036 0 0 0 20.067 5.882c24.083 0 37.251-19.949 37.251-37.249 0-.566-.014-1.134-.039-1.694a26.597 26.597 0 0 0 6.533-6.774z"></path></svg></a>
:root {
--c-active: #275EFE;
--c-active-inner: #FFFFFF;
--c-default: #D2D6E9;
--c-default-dark: #C7CBDF;
--c-black: #1B1B22;
}
.radio,
.checkbox,
.switch {
display: table;
border-radius: var(--border-radius, 12px) var(--border-radius-corner, 12px) var(--border-radius, 12px) var(--border-radius, 12px);
position: relative;
input {
appearance: none;
outline: none;
border: none;
background: var(--input-background, none);
display: block;
cursor: pointer;
margin: 0;
padding: 0;
border-radius: inherit;
width: var(--input-width, 24px);
height: var(--input-height, 24px);
}
svg {
display: block;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
pointer-events: none;
fill: var(--c-active-inner);
transform: scale(1.01) translateZ(0);
}
}
.radio,
.checkbox {
input {
--border-color: var(--c-default);
--border-width: 2px;
box-shadow: inset 0 0 0 var(--border-width) var(--border-color);
&:checked {
--border-color: var(--c-active);
}
&:not(:checked) {
transition: box-shadow .25s;
&:hover {
--border-width: 3px;
--border-color: var(--c-active);
}
}
}
}
.radio {
input {
&:checked {
--border-width: 6.75px;
}
& + svg {
--top-y: 0;
--dot-y: -17px;
--drop-y: -14px;
--top-s-x: 1.75;
--top-s-y: 1;
.top {
transform-origin: 12px -12px;
transform: translateY(var(--top-y)) scale(var(--top-s-x), var(--top-s-y)) translateZ(0);
}
.dot {
transform: translateY(var(--dot-y)) translateZ(0);
}
.drop {
transform: translateY(var(--drop-y)) translateZ(0);
}
}
}
}
.checkbox {
--border-radius: 5px;
--border-radius-corner: 5px;
input {
&:checked {
--border-width: 12px;
& + svg {
--tick-offset: 46.5px;
}
}
& + svg {
--dot-x: 14px;
--dot-y: -14px;
--dot-s: 1;
--tick-offset: 20.5px;
--tick-array: 16px;
--tick-s: 1;
--drop-s: 1;
.tick {
fill: none;
stroke-width: 3px;
stroke-linecap: round;
stroke-linejoin: round;
stroke: var(--c-active-inner);
stroke-dasharray: var(--tick-array) 33px;
stroke-dashoffset: var(--tick-offset);
transform-origin: 10.5px 16px;
transform: scale(var(--tick-s)) translateZ(0);
}
.dot {
transform-origin: 10.5px 15.5px;
transform: translate(var(--dot-x), var(--dot-y)) scale(var(--dot-s)) translateZ(0);
}
.drop {
transform-origin: 25px -1px;
transform: scale(var(--drop-s)) translateZ(0);
}
}
}
}
.switch {
--input-width: 38px;
input {
--input-background: var(--c-default);
&:checked {
--input-background: var(--c-active);
& + svg {
--default-s: 0;
--default-x: 8px;
--dot-s: 1;
--dot-x: 0px;
}
}
& + svg {
--input-background: var(--c-default);
--default-s: 1;
--default-x: 0px;
--dot-s: 0;
--dot-x: -8px;
}
}
.default {
transform-origin: 12px 12px;
transform: translateX(var(--default-x)) scale(var(--default-s)) translateZ(0);
}
.dot {
transform-origin: 26px 12px;
transform: translateX(var(--dot-x)) scale(var(--dot-s)) translateZ(0);
}
}
.btn {
appearance: none;
border: none;
position: relative;
background: var(--c-black);
color: #fff;
outline: none;
cursor: pointer;
font-size: 14px;
line-height: 21px;
font-weight: 600;
display: block;
width: 132px;
text-align: center;
border-radius: 7px;
margin: 0 auto;
padding: 12px 0;
span {
position: relative;
z-index: 1;
}
svg {
display: block;
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
fill: var(--c-active);
circle {
transition: transform var(--duration, .25s) ease-out;
&.top-left {
transform: translate(var(--spacing, -16px), var(--spacing, -40px));
}
&.middle-bottom {
transform: translate(var(--spacing, 8px), var(--spacing, 40px));
}
&.top-right {
transform: translate(var(--spacing, 40px), var(--spacing, -40px));
}
&.right-bottom {
transform: translate(var(--spacing, 4px), var(--spacing, 40px));
}
&.left-bottom {
transform: translate(var(--spacing, -40px), var(--spacing, 40px));
}
}
}
&:hover {
--spacing: 0;
--duration: .45s;
}
}
.grid {
display: grid;
grid-gap: 24px 32px;
grid-template-columns: repeat(3, auto);
grid-template-rows: repeat(3, auto);
grid-auto-flow: column;
.last {
grid-column: 1 / 4;
grid-row: 3;
}
}
html {
box-sizing: border-box;
font-smoothing: antialiased;
}
* {
box-sizing: inherit;
&:before,
&:after {
box-sizing: inherit;
}
}
// Center & dribbble
body {
min-height: 100vh;
display: flex;
font-family: 'Poppins', Arial;
justify-content: center;
align-items: center;
background: #fff;
.dribbble {
position: fixed;
display: block;
right: 20px;
bottom: 20px;
img {
display: block;
height: 28px;
}
}
.twitter {
position: fixed;
display: block;
right: 64px;
bottom: 14px;
svg {
width: 32px;
height: 32px;
fill: #1da1f2;
}
}
}
View Compiled
const { to, set, from, fromTo } = gsap
const getVar = (key, elem = document.documentElement) => getComputedStyle(elem).getPropertyValue(key)
document.querySelectorAll('.radio').forEach(elem => {
let svg = elem.querySelector('svg'),
input = elem.querySelector('input')
input.addEventListener('change', e => {
fromTo(input, {
'--border-width': '3px'
}, {
'--border-color': getVar('--c-active'),
'--border-width': '12px',
duration: .2
})
to(svg, {
keyframes: [{
'--top-y': '6px',
'--top-s-x': 1,
'--top-s-y': 1.25,
duration: .2,
delay: .2
}, {
'--top-y': '0px',
'--top-s-x': 1.75,
'--top-s-y': 1,
duration: .6
}]
})
to(svg, {
keyframes: [{
'--dot-y': '2px',
duration: .3,
delay: .2
}, {
'--dot-y': '0px',
duration: .3
}]
})
to(svg, {
'--drop-y': '0px',
duration: .6,
delay: .4,
clearProps: true,
onComplete() {
input.removeAttribute('style')
}
})
})
})
document.querySelectorAll('.checkbox').forEach(elem => {
let svg = elem.querySelector('svg'),
input = elem.querySelector('input')
input.addEventListener('change', e => {
let checked = input.checked
if(!checked) {
return
}
fromTo(input, {
'--border-width': '3px'
}, {
'--border-color': getVar('--c-active'),
'--border-width': '12px',
duration: .2,
clearProps: true
})
set(svg, {
'--dot-x': '14px',
'--dot-y': '-14px',
'--tick-offset': '20.5px',
'--tick-array': '16.5px',
'--drop-s': 1
})
to(elem, {
keyframes: [{
'--border-radius-corner': '14px',
duration: .2,
delay: .2
}, {
'--border-radius-corner': '5px',
duration: .3,
clearProps: true
}]
})
to(svg, {
'--dot-x': '0px',
'--dot-y': '0px',
'--dot-s': 1,
duration: .4,
delay: .4
})
to(svg, {
keyframes: [{
'--tick-offset': '48px',
'--tick-array': '14px',
duration: .3,
delay: .2
}, {
'--tick-offset': '46.5px',
'--tick-array': '16.5px',
duration: .35,
clearProps: true
}]
})
})
})
document.querySelectorAll('.switch').forEach(elem => {
let svg = elem.querySelector('svg'),
input = elem.querySelector('input')
input.addEventListener('pointerenter', e => {
if(elem.animated || input.checked) {
return
}
to(input, {
'--input-background': getVar('--c-default-dark'),
duration: .2
})
})
input.addEventListener('pointerleave', e => {
if(elem.animated || input.checked) {
return
}
to(input, {
'--input-background': getVar('--c-default'),
duration: .2
})
})
input.addEventListener('change', e => {
let checked = input.checked
let hide = checked ? 'default' : 'dot',
show = checked ? 'dot' : 'default'
fromTo(svg, {
'--default-s': checked ? 1 : 0,
'--default-x': checked ? '0px' : '8px',
'--dot-s': checked ? 0 : 1,
'--dot-x': checked ? '-8px' : '0px'
}, {
['--' + hide + '-s']: 0,
['--' + hide + '-x']: checked ? '8px' : '-8px',
duration: .25,
delay: .15
})
fromTo(input, {
'--input-background': getVar(checked ? '--c-default' : '--c-active'),
}, {
'--input-background': getVar(checked ? '--c-active' : '--c-default'),
duration: .35,
clearProps: true
})
to(svg, {
keyframes: [{
['--' + show + '-x']: checked ? '2px' : '-2px',
['--' + show + '-s']: 1,
duration: .25
}, {
['--' + show + '-x']: '0px',
duration: .2,
clearProps: true
}]
})
})
})