Component

Theme Switcher

Theme switcher dropdown with light, dark, system, and sepia themes plus accent color selection.

  • Component
  • Tailwind
  • HTML
  • MIT

Preview

Live component

Interactive preview
Open preview
9:41 100%
devsnips.dev/library/tailwind/components/dropdowns/theme-switcher/
fluid · no fixed width 100%
No dependencies Production-ready

Install

Add to your project

terminal
npx devsnips add Tailwind/Components/Dropdowns/theme-switcher
registry path: Tailwind/Components/Dropdowns/theme-switcher

Source

Implementation

code.html
<!-- Theme Switcher -->
<div class="relative inline-block">
  <button
    id="theme-button"
    class="flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
    onclick="toggleDropdown('theme-menu')"
  >
    <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
    </svg>
    <span>Theme</span>
    <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
    </svg>
  </button>

  <div
    id="theme-menu"
    class="hidden absolute left-0 z-20 mt-2 w-72 bg-white rounded-xl shadow-xl border border-gray-200 overflow-hidden"
  >
    <div class="p-4">
      <p class="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-3">Choose Theme</p>
      
      <!-- Theme Options Grid -->
      <div class="grid grid-cols-2 gap-3">
        <!-- Light Theme -->
        <button
          class="theme-option relative flex flex-col items-center gap-2 p-3 rounded-xl border-2 border-gray-200 hover:border-blue-500 transition-all"
          data-theme="light"
          onclick="selectTheme(this)"
        >
          <div class="w-full h-16 rounded-lg bg-white border border-gray-200 flex items-center justify-center">
            <svg class="w-8 h-8 text-amber-500" fill="currentColor" viewBox="0 0 24 24">
              <path d="M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-2.25A.75.75 0 0112 18zM7.758 17.303a.75.75 0 00-1.061-1.06l-1.591 1.59a.75.75 0 001.06 1.061l1.591-1.59zM6 12a.75.75 0 01-.75.75H3a.75.75 0 010-1.5h2.25A.75.75 0 016 12zM6.697 7.757a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 00-1.061 1.06l1.59 1.591z"></path>
            </svg>
          </div>
          <span class="text-sm font-medium text-gray-700">Light</span>
          <svg class="absolute top-2 right-2 w-5 h-5 text-blue-600 hidden check-icon" fill="currentColor" viewBox="0 0 20 20">
            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
          </svg>
        </button>

        <!-- Dark Theme -->
        <button
          class="theme-option relative flex flex-col items-center gap-2 p-3 rounded-xl border-2 border-gray-200 hover:border-blue-500 transition-all"
          data-theme="dark"
          onclick="selectTheme(this)"
        >
          <div class="w-full h-16 rounded-lg bg-slate-800 flex items-center justify-center">
            <svg class="w-8 h-8 text-slate-200" fill="currentColor" viewBox="0 0 24 24">
              <path fill-rule="evenodd" d="M9.528 1.718a.75.75 0 01.162.819A8.97 8.97 0 009 6a9 9 0 009 9 8.97 8.97 0 003.463-.69.75.75 0 01.981.98 10.503 10.503 0 01-9.694 6.46c-5.799 0-10.5-4.701-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 01.818.162z" clip-rule="evenodd"></path>
            </svg>
          </div>
          <span class="text-sm font-medium text-gray-700">Dark</span>
          <svg class="absolute top-2 right-2 w-5 h-5 text-blue-600 hidden check-icon" fill="currentColor" viewBox="0 0 20 20">
            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
          </svg>
        </button>

        <!-- System Theme -->
        <button
          class="theme-option relative flex flex-col items-center gap-2 p-3 rounded-xl border-2 border-gray-200 hover:border-blue-500 transition-all"
          data-theme="system"
          onclick="selectTheme(this)"
        >
          <div class="w-full h-16 rounded-lg bg-gradient-to-br from-white to-slate-800 flex items-center justify-center">
            <svg class="w-8 h-8 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
            </svg>
          </div>
          <span class="text-sm font-medium text-gray-700">System</span>
          <svg class="absolute top-2 right-2 w-5 h-5 text-blue-600 hidden check-icon" fill="currentColor" viewBox="0 0 20 20">
            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
          </svg>
        </button>

        <!-- Sepia Theme -->
        <button
          class="theme-option relative flex flex-col items-center gap-2 p-3 rounded-xl border-2 border-gray-200 hover:border-blue-500 transition-all"
          data-theme="sepia"
          onclick="selectTheme(this)"
        >
          <div class="w-full h-16 rounded-lg bg-amber-50 border border-amber-200 flex items-center justify-center">
            <svg class="w-8 h-8 text-amber-700" fill="currentColor" viewBox="0 0 24 24">
              <path d="M6 2a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V4a2 2 0 00-2-2H6z"></path>
            </svg>
          </div>
          <span class="text-sm font-medium text-gray-700">Sepia</span>
          <svg class="absolute top-2 right-2 w-5 h-5 text-blue-600 hidden check-icon" fill="currentColor" viewBox="0 0 20 20">
            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
          </svg>
        </button>
      </div>
    </div>

    <!-- Accent Colors -->
    <div class="px-4 pb-4">
      <p class="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-3">Accent Color</p>
      <div class="flex gap-2">
        <button class="accent-option w-8 h-8 rounded-full bg-blue-600 ring-2 ring-offset-2 ring-blue-600 hover:scale-110 transition-transform" onclick="setAccentColor('blue')"></button>
        <button class="accent-option w-8 h-8 rounded-full bg-purple-600 ring-2 ring-offset-2 ring-transparent hover:ring-purple-600 hover:scale-110 transition-transform" onclick="setAccentColor('purple')"></button>
        <button class="accent-option w-8 h-8 rounded-full bg-pink-600 ring-2 ring-offset-2 ring-transparent hover:ring-pink-600 hover:scale-110 transition-transform" onclick="setAccentColor('pink')"></button>
        <button class="accent-option w-8 h-8 rounded-full bg-emerald-600 ring-2 ring-offset-2 ring-transparent hover:ring-emerald-600 hover:scale-110 transition-transform" onclick="setAccentColor('emerald')"></button>
        <button class="accent-option w-8 h-8 rounded-full bg-amber-600 ring-2 ring-offset-2 ring-transparent hover:ring-amber-600 hover:scale-110 transition-transform" onclick="setAccentColor('amber')"></button>
        <button class="accent-option w-8 h-8 rounded-full bg-red-600 ring-2 ring-offset-2 ring-transparent hover:ring-red-600 hover:scale-110 transition-transform" onclick="setAccentColor('red')"></button>
      </div>
    </div>
  </div>
</div>

<script>
  function toggleDropdown(id) {
    document.getElementById(id).classList.toggle('hidden');
  }

  function selectTheme(element) {
    document.querySelectorAll('.theme-option').forEach(opt => {
      opt.classList.remove('border-blue-500', 'bg-blue-50');
      opt.classList.add('border-gray-200');
      opt.querySelector('.check-icon').classList.add('hidden');
    });
    
    element.classList.remove('border-gray-200');
    element.classList.add('border-blue-500', 'bg-blue-50');
    element.querySelector('.check-icon').classList.remove('hidden');
    
    // Toggle dropdown close
    // toggleDropdown('theme-menu');
  }

  function setAccentColor(color) {
    document.querySelectorAll('.accent-option').forEach(btn => {
      btn.classList.remove('ring-blue-600', 'ring-purple-600', 'ring-pink-600', 'ring-emerald-600', 'ring-amber-600', 'ring-red-600');
      btn.classList.add('ring-transparent');
    });
    
    event.target.classList.remove('ring-transparent');
    event.target.classList.add(`ring-${color}-600`);
  }

  document.addEventListener('click', function(event) {
    const container = document.querySelector('.relative.inline-block');
    if (!container.contains(event.target)) {
      document.getElementById('theme-menu').classList.add('hidden');
    }
  });
</script>
147 lines UTF-8 · LF · Spaces: 2

AI-ready

Available to your agent.

This component is reachable through the DevSnips CLI, MCP server and Skills integrations — one registry, one resource path.

Resource path

devsnips://components/dropdowns/theme-switcher

Continue browsing

View all