Pull Cord Switch
A copy-paste theme toggle component in pure HTML, CSS & vanilla JS. Zero dependencies, framework-agnostic, MIT-licensed.
Theme ToggleHTMLJavaScriptCSSany framework
Copy into your project
HTML
<!-- Pull Cord Switch — click to pull the cord and swap the bulb state -->
<button class="nuda-tt2-pullcord" type="button" aria-pressed="true" aria-label="Toggle theme">
<span class="nuda-tt2-pullcord__glow" aria-hidden="true"></span>
<span class="nuda-tt2-pullcord__bulb" aria-hidden="true"></span>
<span class="nuda-tt2-pullcord__string" aria-hidden="true"></span>
<span class="nuda-tt2-pullcord__knob" aria-hidden="true"></span>
</button>JavaScript
/* Pull Cord Switch — toggle aria-pressed to swing the cord and dim the bulb. */
(function () {
var btn = document.querySelector('.nuda-tt2-pullcord');
if (!btn) return;
btn.addEventListener('click', function () {
var pressed = btn.getAttribute('aria-pressed') === 'true';
btn.setAttribute('aria-pressed', String(!pressed));
document.documentElement.classList.toggle('dark', !pressed);
});
})();CSS
.nuda-tt2-pullcord {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 64px;
height: 120px;
background: transparent;
border: none;
cursor: pointer;
padding: 8px 0 0;
}
.nuda-tt2-pullcord:focus-visible {
outline: 2px solid #e4ff54;
outline-offset: 3px;
border-radius: 8px;
}
.nuda-tt2-pullcord__bulb {
width: 28px;
height: 28px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%,#fffbe6,#fbbf24);
box-shadow: 0 0 20px 6px rgba(251,191,36,.55);
transition: background .4s ease,box-shadow .4s ease;
}
.nuda-tt2-pullcord[aria-pressed="true"] .nuda-tt2-pullcord__bulb {
background: radial-gradient(circle at 35% 30%,#3a3a42,#18181c);
box-shadow: 0 0 0 rgba(0,0,0,0);
}
.nuda-tt2-pullcord__glow {
position: absolute;
top: 8px;
width: 60px;
height: 60px;
border-radius: 50%;
background: radial-gradient(circle,rgba(251,191,36,.35),transparent 70%);
opacity: 1;
transition: opacity .4s ease;
pointer-events: none;
}
.nuda-tt2-pullcord[aria-pressed="true"] .nuda-tt2-pullcord__glow {
opacity: 0;
}
.nuda-tt2-pullcord__string {
width: 2px;
height: 46px;
background: rgba(255,255,255,.35);
transform-origin: top center;
transform: rotate(0deg);
}
.nuda-tt2-pullcord[aria-pressed="true"] .nuda-tt2-pullcord__string {
animation: nuda-tt2-pullcord-swing .9s cubic-bezier(.34,1.56,.64,1);
}
.nuda-tt2-pullcord__knob {
width: 10px;
height: 14px;
border-radius: 5px;
background: #e4ff54;
margin-top: -2px;
box-shadow: 0 0 6px rgba(228,255,84,.5);
}
@keyframes nuda-tt2-pullcord-swing {
0% {
transform: rotate(0);
}
30% {
transform: rotate(14deg);
}
60% {
transform: rotate(-8deg);
}
100% {
transform: rotate(0);
}
}
@media (prefers-reduced-motion:reduce) {
.nuda-tt2-pullcord__string {
animation: none;
}
.nuda-tt2-pullcord__bulb,.nuda-tt2-pullcord__glow {
transition: none;
}
}
How to use Pull Cord Switch
Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.
Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.