Theme Dropdown
A copy-paste theme toggle component in pure HTML & CSS. Zero dependencies, framework-agnostic, MIT-licensed.
Theme ToggleHTMLCSSany framework
Copy into your project
HTML
<!-- Theme Dropdown -->
<div class="nuda-tdd">
<button class="nuda-tdd__trigger" type="button">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="4" />
<path d="M12 2 L12 4" /><path d="M12 20 L12 22" />
<path d="M4.9 4.9 L6.3 6.3" /><path d="M17.7 17.7 L19.1 19.1" />
</svg>
Theme
<svg class="nuda-tdd__chev" viewBox="0 0 12 12" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 4.5 L6 7.5 L9 4.5" />
</svg>
</button>
<ul class="nuda-tdd__menu">
<li class="nuda-tdd__item is-active"><span class="nuda-tdd__check"></span>Light</li>
<li class="nuda-tdd__item"><span class="nuda-tdd__check"></span>Dark</li>
<li class="nuda-tdd__item"><span class="nuda-tdd__check"></span>System</li>
</ul>
</div>CSS
/* Theme Dropdown
Trigger reveals a menu on hover with three options + a glowing check.
Customize: --tdd-accent. Replace :hover with .is-open from JS for click-trigger. */
.nuda-tdd {
--tdd-accent: #e4ff54;
position: relative;
display: inline-block;
}
.nuda-tdd__trigger {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 10px 8px 12px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
color: #fafafa;
font: 600 0.875rem ui-sans-serif, system-ui, sans-serif;
cursor: pointer;
transition: border-color 0.25s;
}
.nuda-tdd__trigger svg { width: 15px; height: 15px; }
.nuda-tdd__trigger:hover { border-color: rgba(228, 255, 84, 0.3); }
.nuda-tdd__chev {
transition: transform 0.25s;
color: #a1a1aa;
}
.nuda-tdd:hover .nuda-tdd__chev,
.nuda-tdd.is-open .nuda-tdd__chev { transform: rotate(180deg); }
.nuda-tdd__menu {
position: absolute;
top: calc(100% + 4px);
left: 0;
list-style: none;
padding: 6px;
margin: 0;
background: rgba(20, 20, 24, 0.95);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
min-width: 100%;
backdrop-filter: blur(8px);
box-shadow: 0 8px 20px -4px rgba(0, 0, 0, 0.5);
opacity: 0;
transform: translateY(-4px);
pointer-events: none;
transition: opacity 0.2s, transform 0.2s;
z-index: 10;
}
.nuda-tdd:hover .nuda-tdd__menu,
.nuda-tdd.is-open .nuda-tdd__menu {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.nuda-tdd__item {
display: flex;
align-items: center;
gap: 8px;
padding: 7px 10px;
font: 500 0.875rem ui-sans-serif, system-ui, sans-serif;
color: #a1a1aa;
border-radius: 6px;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.nuda-tdd__item:hover {
background: rgba(255, 255, 255, 0.04);
color: #fafafa;
}
.nuda-tdd__check {
width: 10px;
height: 10px;
border-radius: 50%;
background: transparent;
border: 1.5px solid rgba(255, 255, 255, 0.15);
transition: all 0.25s;
}
.nuda-tdd__item.is-active { color: #fafafa; }
.nuda-tdd__item.is-active .nuda-tdd__check {
background: var(--tdd-accent);
border-color: var(--tdd-accent);
box-shadow: 0 0 6px rgba(228, 255, 84, 0.5);
}How to use Theme Dropdown
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.