Radio Input

October 2023

Tech Stack

ReactFramer MotionTailwind CSS

One of the most unexplored form inputs in UI design is the radio input. Why limit ourselves to simple circles and a dot? So here's an elegant, sleek, and modern radio input. My love for grayscale, glares, and gradients is evident in this design.

One of the creative challenges I faced was building the background pattern. The effect starts with a dot pattern made using a <circle /> element.

<pattern
id="dots-pattern"
width="20"
height="20"
patternUnits="userSpaceOnUse"
>
<circle cx="1" cy="1" r="1" fill="white" />
</pattern>
<mask id="mask-dots">
<rect width="100%" height="100%" fill="url(#dots-pattern)" />
</mask>
<pattern
id="dots-pattern"
width="20"
height="20"
patternUnits="userSpaceOnUse"
>
<circle cx="1" cy="1" r="1" fill="white" />
</pattern>
<mask id="mask-dots">
<rect width="100%" height="100%" fill="url(#dots-pattern)" />
</mask>

We then build a <linearGradient /> who's stop-color values are manipulated using Framer Motion based on the selected option.

const motionTransition = {
type: "spring",
stiffness: 75,
damping: 25,
};
<linearGradient id="linear-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<motion.stop
stopColor="rgba(250, 250, 250, 0.06125)"
initial={false}
animate={{
offset: gradientTop - gradientHeight + "%",
}}
transition={motionTransition}
/>
<motion.stop
stopColor="rgba(250, 250, 250, 0.5)"
initial={false}
animate={{
offset: gradientTop + "%",
}}
transition={motionTransition}
/>
<motion.stop
stopColor="rgba(250, 250, 250, 0.06125)"
initial={false}
animate={{
offset: gradientTop + gradientHeight + "%",
}}
transition={motionTransition}
/>
</linearGradient>;
const motionTransition = {
type: "spring",
stiffness: 75,
damping: 25,
};
<linearGradient id="linear-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<motion.stop
stopColor="rgba(250, 250, 250, 0.06125)"
initial={false}
animate={{
offset: gradientTop - gradientHeight + "%",
}}
transition={motionTransition}
/>
<motion.stop
stopColor="rgba(250, 250, 250, 0.5)"
initial={false}
animate={{
offset: gradientTop + "%",
}}
transition={motionTransition}
/>
<motion.stop
stopColor="rgba(250, 250, 250, 0.06125)"
initial={false}
animate={{
offset: gradientTop + gradientHeight + "%",
}}
transition={motionTransition}
/>
</linearGradient>;

Putting them together, we create a <mask /> using these two to make the dots brighter when the linear gradient's position is near them. It works by making parts of the <rect /> be transparent where the "dots pattern" is black. The remaining parts are the white dots themselves, which show the linear gradient behind them being used as a fill.

<mask id="mask-dots-on-linear">
<rect
width="100%"
height="100%"
fill="url(#linear-gradient)"
mask="url(#mask-dots)"
/>
</mask>
<mask id="mask-dots-on-linear">
<rect
width="100%"
height="100%"
fill="url(#linear-gradient)"
mask="url(#mask-dots)"
/>
</mask>
combining the dots pattern and the linear gradient

combining the dots pattern and the linear gradient

While this by itself achives the effect we were going for, the finishing touch comes with the use of a <radialGradient /> on which the mask will be used over. This gives the highlighted area a more organic and dynamic shape, giving off the impression of a light source.

<radialGradient
id="radial-gradient"
cx="50%"
cy="50%"
r="40%"
fx="50%"
fy="50%"
>
<stop offset="0%" stopColor="white" />
<stop offset="100%" stopColor="rgba(250, 250, 250, 0.125)" />
</radialGradient>
<radialGradient
id="radial-gradient"
cx="50%"
cy="50%"
r="40%"
fx="50%"
fy="50%"
>
<stop offset="0%" stopColor="white" />
<stop offset="100%" stopColor="rgba(250, 250, 250, 0.125)" />
</radialGradient>

And now we finally apply the radial gradient as a fill to our main <rect />, and use the mask we created earlier, to achieve the final effect.

<rect
width="100%"
height="100%"
fill="url(#radial-gradient)"
mask="url(#mask-dots-on-linear)"
/>
<rect
width="100%"
height="100%"
fill="url(#radial-gradient)"
mask="url(#mask-dots-on-linear)"
/>
combining our effect and the radial gradient

combining our effect and the radial gradient

There's something beautiful about exploring unchartered territories, even for something as simple as a radio input. It's a reminder that there's always opportunities to innovate and create something new.