基于 shadcn + tailwind 4.1的页面主题切换方案(附源码)

发布于:2025-06-18 ⋅ 阅读:(19) ⋅ 点赞:(0)

项目不算复杂,文章不想看的源码可自取
github地址:shadcn-tailwind-theme-switcher

介绍

从0开始开发一个基于shadcn和tailwindcss v4.1的主题切换功能。

查看效果:页面预览

如何刚好是你想要的再读不迟 ~

几个作用。现在很多项目都有主题切换功能,但搞的太复杂理不清其中的逻辑;另外,大家混用shadcn/tailwind常常搞不清二者的功能边界;最后tailwind从3到4也有很多变化。借此梳理一下,让内心稍微平静一点。

创建项目

使用vite工具快速创建一个react项目,以该项目作为基础逐步丰富代码
个人感觉pnpm四个字母打字时有点别扭,就配成p了,下文所有的p指定代表pnpm

  • p create vite . 在当前目录使用vite创建项目,后面选择react typescript
  • p install 安装依赖
  • p dev 启动服务
  • 点击控制台url打开页面,上面这几步无任何障碍,搭建react项目首选vite很丝滑。

引入tailwindCSS

  • p install tailwindcss @tailwindcss/vite
  • 添加插件给vite
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
	plugins: [
		tailwindcss(),
	],
})
  • 创建 src/global.css,导入tailwind默认能力。(v4之前要分别导入,现在一句顶三句)
@import "tailwindcss";
  • 在App.tsx中引入该 gloabl.css,后面就可以愉快的使用tailwind了
function App() {

	return (
	<>
		<h1 className="text-3xl font-bold text-red-500">Hello world!</h1>
	</>
	)
}
  • 更多的tailwind知识如变量、样式覆盖等可以看看现成的文章 这篇

增加深/浅模式切换功能

tailwindcss v4 中采用了 CSS-first 配置方式,可以直接在 CSS 文件中配置所有内容,理论上不需要有tailwind.config.ts文件了,但依旧保持对该文件的支持(向下兼容)。鉴于现在大部分资料都基于tailwind.config.ts,彻底删除可能会带来不小的额外成本(需要查css中如何对齐config的能力)。

添加tailwind darkMode

  • 创建tailwind.config.ts文件,增加 darkMode: 'class'
export default {
	darkMode: 'class',
	content: [
		"./index.html",
		"./src/**/*.{js,ts,jsx,tsx}",
	],
	theme: {
		extend: {
			colors: {
				green: {
					100: '#FF0000',
				},
				red: {
					100: '#00FF00',
				},
			},
		},
	},
	plugins: [],
}
  • 设置完上述操作后,如果页面中使用了bg: 开头的样式,那当设置<html class="dark"></html> 时该样式便会生效。

介绍shadcn/ui

  • shadcn/ui 跟常规ui库最大的差异是shadcn/ui会把你需要的组件代码直接copy到你的项目中,而不是增加npm包,所以他的导入方式不是pnpm add shadcn/ui。
  • 但使用后会发现,package.json中增加了很多 @radix-ui/ 开头多包,radix-ui是一个只提供基础能力但不提供样式的ui库,即无头headless样式库,shadcn是在它的基础上使用tailwind语法实现了自己的组件样式。
  • 要不要纠结radix-ui的引入破坏了shadcn声称的把代码权限完全交给开发者,而不是给一个npm包理念。个人觉得99%的场景下不用纠结,因为radix-ui基于WAI-ARIA 设计模式只实现了最基础的能力,把样式的自定义完全留给开发者。

安装shadcn/ui

  • 在 tsconfig.json 和 tsconfig.app.json 中增加下面配置
{ 
	"compilerOptions": 
		{ 
			// ... 
			"baseUrl": ".", 
			"paths": { "@/*": [ "./src/*" ] } 
			// ... 
		}
	}
}	
  • 在vite.config.ts 中增加
resolve: { 
	alias: { 
		"@": path.resolve(__dirname, "./src"), 
	}, 
}
  • p dlx shadcn@latest init 初始化shadcn,p dlx 等价于 npx

    • 初始化后会创建 components.json
    • 还会自动找到tailwind到样式文件,global.css,然后插入一堆默认的样式变量,如下:
      在这里插入图片描述
  • 在模式切换中tailwind和shadcn的作用:

    • tailwind可以自动根据根目录的dark类,切换bg: 开头的变量。自己在写样式时如果需要给暗黑模式下一个单独的样式,就需要写很多bg:xxx
    • shadcn库里的组件,本身就实现了对不同主题下的风格可变性,就是通过css变量实现的。简单讲,shadcn所有组件的颜色都是走全局变量的(不会写死),当切主题时只要改变这些变量即可。当然这些变量都已经暴露出来了很方便修改。

增加模式切换逻辑

  • 增加一个mode-toggle.tsx
import { Moon, Sun } from "lucide-react"
import { Button } from "@/shadcn/components/button"
import {
	DropdownMenu,
	DropdownMenuContent,
	DropdownMenuItem,
	DropdownMenuTrigger,
} from "@/shadcn/components/dropdown-menu"
import { useTheme } from "@/theme/theme-provider"
export function ModeToggle() {
	const { setTheme } = useTheme()
	return (
	<DropdownMenu>
		<DropdownMenuTrigger asChild>
			<Button variant="outline" size="icon">
				<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
				<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
				<span className="sr-only">切换主题</span>
			</Button>
		</DropdownMenuTrigger>
		<DropdownMenuContent align="end">
			<DropdownMenuItem onClick={() => setTheme("light")}>
				明亮
			</DropdownMenuItem>
			<DropdownMenuItem onClick={() => setTheme("dark")}>
				暗黑
			</DropdownMenuItem>
			<DropdownMenuItem onClick={() => setTheme("system")}>
				系统
			</DropdownMenuItem>
		</DropdownMenuContent>
	</DropdownMenu>
	)
}
  • 增加 theme-provider.tsx 添加具体的切换逻辑
  • 然后在主文件,main.tsx 和 App.tsx 调用上述代码即可

增加颜色主题模式功能

  • 色系变化的核心逻辑:
    • global.css 先看下该文件下的配置,重点是其中的 :root[data-theme="rose"] .dark[data-theme="rose"] :root[data-theme="green"] .dark[data-theme="green"] 这些作用域下各自定义了响应色系下的变量,当切换色系时只需动态改变<html data-theme="rose"></html> 里面的颜色属性即可
  • shadcn/ui themes 这里直接copy出自己想要的色系对应的变量,放到global.css里。
  • 新增 theme-toggle.tsx 文件,处理色系的选择事件。
  • 再在 theme-provider.tsx 添加色系主题相关的变动逻辑即可。

参考:


网站公告

今日签到

点亮在社区的每一天
去签到