Dual-Dome Neumorphic Switch • OFF & ON•React + Tailwind + Framer Motion
Tactile Neumorphic Dual-Dome Switch
Photorealistic skeuomorphic switch featuring an outer recessed bevel cavity, deep carved shadow trench, illuminated emerald photon channel, and dual-dome sculpted tactile thumb.
preview.tsx
Interactions:0
Hover or click to interact
Customizer
ALL VARIANTS & PRESETS
All Variants & States
Click or hover over switches to test spring physics & photorealistic lighting
Off Statestate="off"
On Statestate="on"
Interactive Springmode="live"
Cyber Laser Cyantheme="cyber-cyan"
UI Pirate Magmatheme="magma-orange"
Dark Obsidian Stealththeme="dark-obsidian"
Installation & Setup
Install the required dependencies for Framer Motion spring animations and utility classes:
npm install framer-motion clsx
Code & Integration
TactileNeumorphicSwitch.tsx• Production Ready
"use client";
import React, { useState } from "react";
import { motion } from "framer-motion";
export interface TactileNeumorphicSwitchProps {
checked?: boolean;
defaultChecked?: boolean;
onChange?: (checked: boolean) => void;
theme?: "emerald-photon" | "cyber-cyan" | "magma-orange" | "dark-obsidian" | "hyper-violet" | "amber-crt";
size?: "sm" | "md" | "lg" | "xl";
disabled?: boolean;
label?: string;
showGrid?: boolean;
}
export function TactileNeumorphicSwitch({
checked: controlledChecked,
defaultChecked = false,
onChange,
theme = "emerald-photon",
size = "md",
disabled = false,
label,
showGrid = true,
}: TactileNeumorphicSwitchProps) {
const [internalChecked, setInternalChecked] = useState(defaultChecked);
const isChecked = controlledChecked !== undefined ? controlledChecked : internalChecked;
const handleToggle = () => {
if (disabled) return;
const next = !isChecked;
if (controlledChecked === undefined) setInternalChecked(next);
onChange?.(next);
};
return (
<div
role="switch"
aria-checked={isChecked}
tabIndex={disabled ? -1 : 0}
onClick={handleToggle}
className="relative w-[320px] h-[140px] rounded-[70px] bg-[#DFE3EB] shadow-[inset_5px_6px_12px_rgba(150,162,182,0.65),inset_-5px_-6px_12px_rgba(255,255,255,0.95)] flex items-center justify-center cursor-pointer select-none"
>
{/* Deep Carved Shadow Trench */}
<div className="relative w-[276px] h-[84px] rounded-[42px] bg-gradient-to-b from-[#C2C9D6] via-[#D4DBE6] to-[#E7ECF3] shadow-[inset_0_8px_12px_rgba(60,72,92,0.45),inset_0_-2px_4px_rgba(255,255,255,0.85)] overflow-hidden flex items-center">
{/* Emerald Illuminated Active Fill */}
<motion.div
animate={{
width: isChecked ? "256px" : "0px",
opacity: isChecked ? 1 : 0,
}}
transition={{ type: "spring", stiffness: 380, damping: 26 }}
className="absolute left-0 top-0 bottom-0 bg-gradient-to-r from-[#02B86E] via-[#0AD483] to-[#2BF3A4] shadow-[inset_0_7px_10px_rgba(0,85,48,0.55),0_0_18px_rgba(16,229,153,0.35)]"
/>
</div>
{/* Dual-Dome Sculpted Thumb */}
<motion.div
animate={{ x: isChecked ? 70 : -70 }}
transition={{ type: "spring", stiffness: 380, damping: 26 }}
className="absolute z-20 w-[136px] h-[96px] rounded-[48px] bg-gradient-to-br from-white via-[#F5F7FA] to-[#DFE4EC] shadow-[0_16px_28px_-3px_rgba(35,48,70,0.32),0_8px_14px_-2px_rgba(35,48,70,0.22),inset_0_2px_3px_white,inset_0_-3px_4px_rgba(150,162,180,0.35)] flex items-center justify-between px-2"
>
<div className="w-[60px] h-[60px] rounded-full bg-[radial-gradient(circle_at_35%_32%,white_0%,rgba(255,255,255,0.75)_35%,rgba(185,195,212,0.7)_100%)] shadow-[inset_0_2px_3px_rgba(255,255,255,0.95),inset_0_-2px_3px_rgba(140,152,172,0.4)]" />
<div className="w-[2px] h-[55%] rounded-full bg-gradient-to-b from-white via-slate-400 to-white opacity-35" />
<div className="w-[60px] h-[60px] rounded-full bg-[radial-gradient(circle_at_35%_32%,white_0%,rgba(255,255,255,0.75)_35%,rgba(185,195,212,0.7)_100%)] shadow-[inset_0_2px_3px_rgba(255,255,255,0.95),inset_0_-2px_3px_rgba(140,152,172,0.4)]" />
</motion.div>
</div>
);
}Component API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| checked | boolean | undefined | Controlled boolean state of the switch (true = ON, false = OFF) |
| defaultChecked | boolean | false | Initial boolean state when uncontrolled |
| stateMode | "interactive" | "off" | "on" | "interactive" | Force a static OFF or ON state, or interactive toggle |
| theme | TactileSwitchTheme | "emerald-photon" | Color scheme for illuminated channel and shadow aesthetics |
| size | "sm" | "md" | "lg" | "xl" | "md" | Proportional scaling dimension preset |
| onChange | (checked: boolean) => void | undefined | Callback fired whenever the switch is toggled |
| showGrid | boolean | true | Renders the fine mesh canvas grid behind the switch |
Personalized Help
Struggling with skeuomorphic switches, neumorphic UI, or custom React toggles? Let's talk about your product.
UI Pirate is a product design & development agency trusted by 50+ SaaS founders and enterprise teams across the US, UK & beyond. Tell us what you need.