Original by Plinth · MIT
Forms
Toggle Switch
An accessible switch with a satisfying overshoot when it lands.
#toggle#switch#form#css
Install
npx shadcn@latest add https://plinthui.com/r/toggle-switch.json'use client';
import { useState } from 'react';
export function ToggleSwitch({
label = 'Notifications',
initialChecked = false,
onChange,
}: {
label?: string;
initialChecked?: boolean;
onChange?: (checked: boolean) => void;
}) {
const [checked, setChecked] = useState(initialChecked);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setChecked(event.target.checked);
onChange?.(event.target.checked);
};
const thumbStyle: React.CSSProperties = {
transform: checked ? 'translateX(20px)' : 'translateX(0px)',
transition: 'transform 300ms cubic-bezier(0.34, 1.56, 0.64, 1)',
};
return (
<label className="relative inline-flex cursor-pointer select-none items-center gap-2.5 text-sm text-[#6f6d64] dark:text-[#8a8d96]">
{/* Hidden but still focusable, so the checkbox keeps its keyboard behaviour. */}
<input
type="checkbox"
role="switch"
checked={checked}
onChange={handleChange}
className="peer absolute m-0 h-px w-px opacity-0"
/>
<span
aria-hidden="true"
style={{ transition: 'background-color 200ms ease' }}
className={`relative h-6 w-11 flex-none rounded-[12px] peer-focus-visible:[outline:2px_solid_#8f7635] peer-focus-visible:[outline-offset:2px] dark:peer-focus-visible:[outline-color:#c8a96a] ${
checked
? 'bg-[#8f7635] dark:bg-[#c8a96a]'
: 'bg-[rgba(111,109,100,0.85)] dark:bg-[rgba(138,141,150,0.8)]'
}`}
>
<span
style={thumbStyle}
className="absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white shadow-[0_1px_2px_rgba(0,0,0,0.25)] dark:bg-[#0e0f11]"
/>
</span>
<span>{label}</span>
</label>
);
}