All components
uiJSX
Wooden Switch
A high-fidelity skeuomorphic toggle featuring procedural audio and organic textures.
Motion
JSX
"use client";
import React, { useState, useRef } from "react";
import { motion, AnimatePresence } from "framer-motion";
class="code-comment">// --- 1. PROCEDURAL AUDIO ENGINE ---
const playWoodSound = (type = class="code-string">'click') => {
if (typeof window === class="code-string">'undefined') return;
const AudioContext = window.AudioContext || window.webkitAudioContext;
if (!AudioContext) return;
const ctx = new AudioContext();
const t = ctx.currentTime;
const osc = ctx.createOscillator();
const gain = ctx.createGain();
const filter = ctx.createBiquadFilter();
const startFreq = type === class="code-string">'on' ? 350 : 300;
const endFreq = 50;
const duration = 0.12;
osc.type = "sine";
osc.frequency.setValueAtTime(startFreq, t);
osc.frequency.exponentialRampToValueAtTime(endFreq, t + duration);
filter.type = "lowpass";
filter.frequency.setValueAtTime(800, t);
gain.gain.setValueAtTime(0, t);
gain.gain.linearRampToValueAtTime(1, t + 0.01);
gain.gain.exponentialRampToValueAtTime(0.001, t + duration);
osc.connect(filter);
filter.connect(gain);
gain.connect(ctx.destination);
osc.start(t);
osc.stop(t + duration);
};
class="code-comment">// --- 2. THE COMPONENT ---
const WoodenToggle = () => {
const [isOn, setIsOn] = useState(false);
const containerRef = useRef(null);
const toggleSwitch = () => {
const newState = !isOn;
setIsOn(newState);
playWoodSound(newState ? class="code-string">'on' : class="code-string">'off');
};
return (
<main className="min-h-screen flex flex-col items-center justify-center relative overflow-hidden bg-[#e0e5ec] px-4">
{class="code-comment">/* --- BACKGROUND ATMOSPHERE --- */}
<div className="absolute inset-0 z-0">
<div className="absolute inset-0 bg-gradient-to-br from-gray-200 to-gray-400"></div>
<div className="absolute top-[-20%] left-[-20%] w-[80%] h-[80%] bg-blue-300/20 rounded-full blur-[120px]"></div>
<div className="absolute bottom-[-20%] right-[-20%] w-[80%] h-[80%] bg-orange-300/20 rounded-full blur-[120px]"></div>
<div className="absolute inset-0 opacity-40 mix-blend-overlay bg-[url(class="code-string">'https:class="code-comment">//grainy-gradients.vercel.app/noise.svg')]"></div>
</div>
{class="code-comment">/* --- HOUSING CONTAINER (Responsive) --- */}
<div
className="relative z-10 w-full max-w-[320px] sm:max-w-[500px] h-[100px] sm:h-[160px] rounded-full p-2 sm:p-4 flex items-center overflow-hidden cursor-pointer active:scale-[0.99] transition-transform duration-200"
style={{
backgroundColor: class="code-string">'#e0e5ec',
boxShadow: class="code-string">`
20px 20px 60px #bec3c9,
-20px -20px 60px #ffffff
`
}}
onClick={toggleSwitch}
ref={containerRef}
>
{class="code-comment">/* Subtle Stone Texture */}
<div className="absolute inset-0 opacity-10 bg-[url(class="code-string">'https:class="code-comment">//grainy-gradients.vercel.app/noise.svg')] mix-blend-multiply pointer-events-none"></div>
{class="code-comment">/* --- THE TRACK (DEBOSSED) --- */}
<div className="relative w-full h-full rounded-full bg-[#d1d9e6] overflow-hidden transition-colors duration-500 shadow-inner"
style={{
boxShadow: class="code-string">`
inset 10px 10px 20px #b1b9c4,
inset -10px -10px 20px #ffffff
`
}}
>
{class="code-comment">/* Background Texture for Track */}
<div className="absolute inset-0 opacity-10 bg-[url(class="code-string">'https:class="code-comment">//grainy-gradients.vercel.app/noise.svg')] mix-blend-multiply pointer-events-none"></div>
{class="code-comment">/* Track Labels (Responsive Text Size) */}
<div className="absolute inset-0 flex items-center justify-between px-8 sm:px-16 pointer-events-none select-none z-0">
{class="code-comment">/* LEFT LABEL */}
<span className={class="code-string">`text-lg sm:text-2xl font-black tracking-widest transition-all duration-500 ease-out
${!isOn ? 'text-neutral-400/60 scale-100' : 'text-neutral-400/10 scale-95'}`}
style={{ textShadow: class="code-string">'1px 1px 0px rgba(255,255,255,0.7)' }}
>
PUBLIC
</span>
{class="code-comment">/* RIGHT LABEL */}
<span className={class="code-string">`text-lg sm:text-2xl font-black tracking-widest transition-all duration-500 ease-out
${isOn ? 'text-neutral-400/60 scale-100' : 'text-neutral-400/10 scale-95'}`}
style={{ textShadow: class="code-string">'1px 1px 0px rgba(255,255,255,0.7)' }}
>
PRIVATE
</span>
</div>
{class="code-comment">/* --- THE WOODEN SLIDER --- */}
<motion.div
className="absolute top-1.5 bottom-1.5 sm:top-2 sm:bottom-2 w-[50%] rounded-full z-10 flex items-center justify-center shadow-2xl"
initial={false}
animate={{
x: isOn ? "96%" : "4%",
}}
transition={{
type: "spring",
stiffness: 110,
damping: 22,
mass: 1.3
}}
>
{class="code-comment">/* 1. WOOD TEXTURE */}
<div
className="absolute inset-0 rounded-full overflow-hidden"
style={{
backgroundImage: class="code-string">`url('https:class="code-comment">//images.unsplash.com/photo-1632199495802-18f7d21f323b?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D')`,
backgroundSize: class="code-string">'150%',
backgroundPosition: class="code-string">'center',
filter: class="code-string">'saturate(1.1) contrast(1.1) brightness(1)'
}}
>
<div className="absolute inset-0 opacity-20 mix-blend-multiply bg-[url(class="code-string">'https:class="code-comment">//grainy-gradients.vercel.app/noise.svg')]"></div>
</div>
{class="code-comment">/* 2. LIGHTING */}
<div className="absolute inset-0 rounded-full pointer-events-none"
style={{
boxShadow: class="code-string">`
inset 0px 1px 2px rgba(255,255,255,0.6),
inset 0px -2px 4px rgba(0,0,0,0.4),
0 10px 15px -3px rgba(0,0,0,0.5),
0 4px 6px -2px rgba(0,0,0,0.1)
`
}}
/>
{class="code-comment">/* Glossy Reflection */}
<div className="absolute top-2 sm:top-3 left-8 sm:left-12 right-8 sm:right-12 h-[30%] bg-gradient-to-b from-white/25 to-transparent rounded-full blur-[3px] pointer-events-none"></div>
{class="code-comment">/* 3. ACTIVE TEXT (Engraved Look) */}
<div className="relative z-20 overflow-hidden mix-blend-overlay">
<AnimatePresence mode="wait" initial={false}>
<motion.span
key={isOn ? "private" : "public"}
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
className="block text-lg sm:text-xl font-black tracking-widest text-[#3d2312]"
style={{
textShadow: class="code-string">`
0px 1px 1px rgba(255,255,255,0.5),
0px -1px 1px rgba(0,0,0,0.8)
`
}}
>
{isOn ? "PRIVATE" : "PUBLIC"}
</motion.span>
</AnimatePresence>
</div>
</motion.div>
</div>
</div>
{class="code-comment">/* --- FOOTER TEXT --- */}
<div className="relative z-10 mt-12 sm:mt-16">
<span className="text-[10px] sm:text-xs font-mono uppercase tracking-[0.2em] text-neutral-400 select-none">
Click or Tap to Toggle
</span>
</div>
</main>
);
};
export default WoodenToggle;Live Preview
Need a custom component built?
I build reusable, production-ready components for Web3 and AI projects.
Book a call