You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现shadcn全主题切换功能?仿GitHub主题选择交互

实现shadcn主题选择卡片的交互功能

1. 先明确shadcn主题的切换逻辑

shadcn的主题依赖Tailwind CSS的自定义配置,通常是通过给根元素(html)设置data-theme属性,或者替换全局CSS变量来实现主题切换。先确认你的项目中主题是绑定到data-theme还是直接用类名,大部分场景用data-theme更通用。

2. 核心交互实现步骤

(1)状态管理与初始化

不管用框架(React/Vue)还是原生JS,都需要跟踪当前选中的主题,并从本地存储读取用户之前的选择(如果有),默认可跟随系统主题偏好。

React示例:

import { useState, useEffect } from 'react';

export function ThemeSelector() {
  // 从localStorage读取历史主题,默认用系统偏好或light
  const [activeTheme, setActiveTheme] = useState(() => {
    const saved = localStorage.getItem('shadcn-theme');
    return saved || window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
  });

  // 切换主题时更新根元素属性与本地存储
  useEffect(() => {
    document.documentElement.setAttribute('data-theme', activeTheme);
    localStorage.setItem('shadcn-theme', activeTheme);
  }, [activeTheme]);

  // 定义所有shadcn主题列表
  const shadcnThemes = [
    { id: 'light', name: 'Light', previewColor: '#f9fafb' },
    { id: 'dark', name: 'Dark', previewColor: '#111827' },
    { id: 'slate', name: 'Slate', previewColor: '#1e293b' },
    { id: 'gray', name: 'Gray', previewColor: '#374151' },
    { id: 'zinc', name: 'Zinc', previewColor: '#27272a' },
    { id: 'neutral', name: 'Neutral', previewColor: '#2a2a2a' },
    { id: 'stone', name: 'Stone', previewColor: '#2c2c2c' },
  ];

  return (
    <div className="grid grid-cols-4 gap-3 p-4">
      {shadcnThemes.map(theme => (
        <div
          key={theme.id}
          className={`theme-card rounded-lg p-3 cursor-pointer transition-all border-2 ${
            activeTheme === theme.id ? 'border-blue-500' : 'border-transparent'
          }`}
          onClick={() => setActiveTheme(theme.id)}
          style={{ backgroundColor: theme.previewColor }}
        >
          <span className={`text-sm font-medium ${activeTheme === theme.id ? 'text-white' : 'text-gray-800'}`}>
            {theme.name}
          </span>
          {activeTheme === theme.id && (
            <span className="inline-block ml-2 bg-white text-gray-900 rounded-full px-2 text-xs">✓</span>
          )}
        </div>
      ))}
    </div>
  );
}

原生JS示例:

<div class="theme-selector grid grid-cols-4 gap-3 p-4" id="themeSelector">
  <!-- 主题卡片 -->
  <div class="theme-card rounded-lg p-3 cursor-pointer border-transparent border-2" data-theme="light" style="background-color: #f9fafb">
    <span class="text-sm font-medium text-gray-800">Light</span>
  </div>
  <div class="theme-card rounded-lg p-3 cursor-pointer border-transparent border-2" data-theme="dark" style="background-color: #111827">
    <span class="text-sm font-medium text-white">Dark</span>
  </div>
  <!-- 其他shadcn主题卡片重复上述结构 -->
</div>

<script>
  // 初始化主题
  const savedTheme = localStorage.getItem('shadcn-theme') || 
    (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
  document.documentElement.setAttribute('data-theme', savedTheme);
  // 标记激活卡片
  document.querySelector(`[data-theme="${savedTheme}"]`).classList.add('border-blue-500');
  document.querySelector(`[data-theme="${savedTheme}"]`).innerHTML += '<span class="inline-block ml-2 bg-white text-gray-900 rounded-full px-2 text-xs">✓</span>';

  // 绑定点击事件
  document.querySelectorAll('.theme-card').forEach(card => {
    card.addEventListener('click', () => {
      const targetTheme = card.getAttribute('data-theme');
      
      // 更新根元素主题属性
      document.documentElement.setAttribute('data-theme', targetTheme);
      // 保存到本地存储
      localStorage.setItem('shadcn-theme', targetTheme);
      
      // 更新所有卡片的激活状态
      document.querySelectorAll('.theme-card').forEach(c => {
        c.classList.remove('border-blue-500');
        const checkmark = c.querySelector('span:last-child');
        if (checkmark) checkmark.remove();
      });
      card.classList.add('border-blue-500');
      card.innerHTML += '<span class="inline-block ml-2 bg-white text-gray-900 rounded-full px-2 text-xs">✓</span>';
    });
  });
</script>

(2)激活状态视觉反馈

给选中的主题卡片添加明显样式,比如蓝色边框、勾选标记,让用户直观感知当前选中项:

.theme-card:hover {
  transform: translateY(-2px);
}
.theme-card.active {
  border-color: #3b82f6;
}

(3)系统主题同步(可选)

如果需要跟随系统主题变化,添加监听逻辑:

window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
  const systemTheme = e.matches ? 'dark' : 'light';
  // 仅当用户未手动设置过主题时,跟随系统
  if (!localStorage.getItem('shadcn-theme')) {
    document.documentElement.setAttribute('data-theme', systemTheme);
    // 更新卡片激活状态(复用之前的逻辑)
  }
});

内容的提问来源于stack exchange,提问作者alieldeba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 22:41:10