Switch

The Switch component prioritizes instantaneous state reflection. I opted for a calibrated `ease-out` curve rather than a physics-based `spring`. While springs offer a delightful 'bounce,' they introduce perceived latency in a binary toggle where the user expects immediate confirmation. Since this component is triggered by a click rather than a gesture-based drag, the 'bounce' felt disconnected from the user's physical input. Technically, I omitted internal symbols (like 'on/off' icons) to favor component composability. By keeping the 'thumb' and 'track' as clean primitives, the library remains unopinionated. This shifts the responsibility of branding to the implementation layer, ensuring the component doesn't become 'prop-heavy' as users request different icon sets or labels.

Code

// Switch.tsx
import { motion} from 'motion/react';
import { useState } from "react";

const MotionDiv = motion.create('div');

const Switch = () => {

const [isOn, setIsOn] = useState(true);

const toggleSwitch = () => {
    setIsOn(!isOn);
}

  return (
    <MotionDiv
    className = "w-10 h-5 rounded-full flex items-center cursor-pointer relative p-1"
    onClick={toggleSwitch}
    animate = {{backgroundColor: isOn ? '#0f327c' : '#e5e7eb'}}
    >
            <MotionDiv 
                initial = {{translateX: 20}}
                animate = {{translateX: isOn ? 20 : 0}}
                transition = {{type: 'spring', stiffness: 300, damping: 30}}
                className = "bg-white w-5 h-5 rounded-full absolute left-0"
            />
        </MotionDiv>
  )
}

export default Switch