Switch
fluid-functionalism/switchFreeComponent
Animated toggle switch with spring-based thumb animation and fluid font-weight label transitions. Built on Radix UI Switch.
Install it
npx shadcn@latest add https://fluidfunctionalism.com/r/switch.jsonSource
1"use client";23import {4 forwardRef,5 useRef,6 useState,7 useEffect,8 useCallback,9 useId,10 type HTMLAttributes,11} from "react";12import { motion, useMotionValue, animate, type Transition } from "framer-motion";13import * as SwitchPrimitive from "@radix-ui/react-switch";14import { cn } from "@/lib/utils";15import { spring } from "@/lib/springs";1617interface SwitchProps extends HTMLAttributes<HTMLDivElement> {18 label: string;19 checked: boolean;20 onToggle: () => void;21 disabled?: boolean;22 thumbTransition?: Transition;23}2425const TRACK_WIDTH = 34;26const TRACK_HEIGHT = 20;27const THUMB_SIZE = 16;28const THUMB_OFFSET = 2;29const THUMB_TRAVEL = TRACK_WIDTH - THUMB_SIZE - THUMB_OFFSET * 2;30const PILL_EXTEND = 2;31const PRESS_EXTEND = 4;32const PRESS_SHRINK = 4;33const DRAG_DEAD_ZONE = 2;3435const Switch = forwardRef<HTMLDivElement, SwitchProps>(36 ({ label, checked, onToggle, disabled = false, thumbTransition, className, ...props }, ref) => {37 const labelId = useId();38 const hasMounted = useRef(false);39 const [hovered, setHovered] = useState(false);40 const [pressed, setPressed] = useState(false);4142 // Drag refs (not state to avoid re-renders during drag)43 const dragging = useRef(false);44 const didDrag = useRef(false);45 const pointerStart = useRef<{46 clientX: number;47 originX: number;48 } | null>(null);4950 // Motion value for thumb x-axis51 const motionX = useMotionValue(52 checked ? THUMB_OFFSET + THUMB_TRAVEL : THUMB_OFFSET53 );5455 useEffect(() => {56 hasMounted.current = true;57 }, []);5859 // Compute thumb shape60 const thumbWidth = pressed61 ? THUMB_SIZE + PRESS_EXTEND62 : hovered63 ? THUMB_SIZE + PILL_EXTEND64 : THUMB_SIZE;65 const thumbHeight = pressed ? THUMB_SIZE - PRESS_SHRINK : THUMB_SIZE;66 const thumbY = pressed ? THUMB_OFFSET + PRESS_SHRINK / 2 : THUMB_OFFSET;67 const extraWidth = thumbWidth - THUMB_SIZE;68 const thumbX = checked69 ? THUMB_OFFSET + THUMB_TRAVEL - extraWidth70 : THUMB_OFFSET;7172 // Sync motionX when thumbX changes (hover/press/checked) and not dragging73 useEffect(() => {74 if (dragging.current) return;75 if (!hasMounted.current) {76 motionX.set(thumbX);77 } else {78 animate(motionX, thumbX, thumbTransition ?? spring.moderate);79 }80 }, [thumbX, motionX, thumbTransition]);8182 // --- Pointer handlers ---8384 const handlePointerDown = useCallback(85 (e: React.PointerEvent<HTMLDivElement>) => {86// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from fluid-functionalism
All 53 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | fluid-functionalism | Components | Free | 3 deps | |
| Accordion (Base UI)accordion-base | fluid-functionalism | Components | Free | 3 deps | |
| Ask User Questionsask-user-questions | fluid-functionalism | Components | Free | 2 deps | |
| Badgebadge | fluid-functionalism | Components | Free | 1 dep | |
| Buttonbutton | fluid-functionalism | Components | Free | 4 deps | |
| Button (Base UI)button-base | fluid-functionalism | Components | Free | 4 deps | |
| Cardcard | fluid-functionalism | Components | Free | 1 dep | |
| Chat Messagechat-message | fluid-functionalism | Components | Free | 1 dep |
