Components / Inputs & Forms
Switch
Binary toggle for on/off states.
Default
Off
Sizes
Small
Medium (default)
Large
Disabled
Disabled
Source SwitchDemo.tsx
The exact code behind the live demo above. Fetch it raw at /components/source/switch.txt.
import { useState } from "react";
function Section({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="mb-8">
<h3 className="text-sm font-bold text-[var(--flux-heading)] mb-3">{title}</h3>
<div className="p-6 rounded border border-[var(--flux-grey-100)] bg-[var(--flux-surface)]">
{children}
</div>
</div>
);
}
function Switch({
checked,
onChange,
disabled = false,
size = "md",
}: {
checked: boolean;
onChange: (checked: boolean) => void;
disabled?: boolean;
size?: "sm" | "md" | "lg";
}) {
const dims = {
sm: { track: "w-9 h-5", thumb: "w-4 h-4", translate: "translate-x-4" },
md: { track: "w-11 h-6", thumb: "w-5 h-5", translate: "translate-x-5" },
lg: { track: "w-[52px] h-7", thumb: "w-6 h-6", translate: "translate-x-6" },
}[size];
return (
<button
type="button"
role="switch"
aria-checked={checked}
disabled={disabled}
onClick={() => onChange(!checked)}
className={`
relative inline-flex items-center rounded-full shrink-0
transition-colors duration-200 ease-in-out
${dims.track}
${checked ? "bg-[var(--flux-primary-400)]" : "bg-[var(--flux-grey-200)]"}
${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
`}
>
<span
className={`
inline-block rounded-full bg-white shadow-md
transition-transform duration-200 ease-in-out
${dims.thumb}
${checked ? dims.translate : "translate-x-0.5"}
`}
/>
</button>
);
}
export default function SwitchDemo() {
const [defaultOn, setDefaultOn] = useState(false);
const [smOn, setSmOn] = useState(false);
const [lgOn, setLgOn] = useState(true);
return (
<div>
<Section title="Default">
<div className="flex items-center gap-3">
<Switch checked={defaultOn} onChange={setDefaultOn} />
<span className="text-sm text-[var(--flux-black)]">{defaultOn ? "On" : "Off"}</span>
</div>
</Section>
<Section title="Sizes">
<div className="flex flex-col gap-4">
<div className="flex items-center gap-3">
<Switch checked={smOn} onChange={setSmOn} size="sm" />
<span className="text-xs text-[var(--flux-black)]">Small</span>
</div>
<div className="flex items-center gap-3">
<Switch checked={defaultOn} onChange={setDefaultOn} />
<span className="text-xs text-[var(--flux-black)]">Medium (default)</span>
</div>
<div className="flex items-center gap-3">
<Switch checked={lgOn} onChange={setLgOn} size="lg" />
<span className="text-xs text-[var(--flux-black)]">Large</span>
</div>
</div>
</Section>
<Section title="Disabled">
<div className="flex items-center gap-3">
<Switch checked={false} onChange={() => {}} disabled />
<span className="text-sm text-[var(--flux-black)]">Disabled</span>
</div>
</Section>
</div>
);
}