Switch
Tactile capsule toggle switch.
components/ui/switch.tsx
import * as React from 'react'
import { motion } from 'motion/react'
import { cn } from '@/lib/utils'
export interface SwitchProps {
checked?: boolean
defaultChecked?: boolean
onCheckedChange?: (checked: boolean) => void
label?: string
className?: string
disabled?: boolean
}
export function Switch({
checked: controlledChecked,
defaultChecked = false,
onCheckedChange,
label,
className,
disabled = false,
}: SwitchProps) {
const [internalChecked, setInternalChecked] = React.useState(defaultChecked)
const isChecked = controlledChecked !== undefined ? controlledChecked : internalChecked
const handleToggle = () => {
if (disabled) return
const next = !isChecked
setInternalChecked(next)
onCheckedChange?.(next)
}
return (
<label
onClick={handleToggle}
className={cn(
'inline-flex items-center gap-3 cursor-pointer select-none group',
disabled && 'opacity-50 pointer-events-none',
className
)}
>
<motion.div
whileTap={{ scale: 0.95 }}
className={cn(
'w-14 h-8 rounded-full rk-border-sm rk-shadow-sm p-1 transition-colors duration-200 relative flex items-center shrink-0',
isChecked ? 'bg-rk-primary' : 'bg-rk-canvas'
)}
>
<motion.div
layout
transition={{
type: 'spring',
stiffness: 500,
damping: 30,
}}
className={cn(
'w-5 h-5 rounded-full bg-rk-ink rk-shadow-sm shrink-0 aspect-square p-0',
isChecked ? 'ml-auto' : 'ml-0'
)}
/>
</motion.div>
{label && <span className="font-extrabold text-sm text-rk-ink">{label}</span>}
</label>
)
}
No selection tags yet — empty means no evidence, not lack of support.
DependenciesBundle weight · light
clsxtailwind-merge