网站首页 > 技术文章 正文
前言
在 Next.js 中要实现暗黑模式,需要用到一个库:next-themes,它可以帮助我们很轻易地实现暗黑模式切换。
具体步骤
1、安装 next-themes 依赖:
pnpm add next-themes
2、新增 /components/ThemeProvider/index.tsx 文件:
'use client';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
import * as React from 'react';
export default function ThemeProvider({ children, ...props }: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}
3、/app/layout.tsx 文件中注入 ThemeProvider :
import { ThemeProvider } from "@/components/theme-provider"
export default function RootLayout({ children }: RootLayoutProps) {
return (
<>
<html lang="en" suppressHydrationWarning>
<head />
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
</>
)
}
4、新增 /components/ThemeModeButton/index.tsx 主题切换组件:
'use client';
import { Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import { Button } from '@/components/ui/button';
export default function ThemeModeButton() {
const { theme, setTheme } = useTheme();
return (
<Button variant="ghost" size="icon" onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
<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">Toggle theme</span>
</Button>
);
}
过渡动画
1、如果你想加入过渡动画,可以把代码改成这样:
'use client';
import { Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import { Button } from '@/components/ui/button';
export default function ThemeModeButton() {
const { theme, setTheme } = useTheme();
// 判断是否支持 startViewTransition API
const enableTransitions = () =>
'startViewTransition' in document && window.matchMedia('(prefers-reduced-motion: no-preference)').matches;
// 切换动画
async function toggleDark({ clientX: x, clientY: y }: MouseEvent) {
const isDark = theme === 'dark';
if (!enableTransitions()) {
setTheme(theme === 'light' ? 'dark' : 'light');
return;
}
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y))}px at ${x}px ${y}px)`,
];
await document.startViewTransition(async () => {
setTheme(theme === 'light' ? 'dark' : 'light');
}).ready;
document.documentElement.animate(
{ clipPath: !isDark ? clipPath.reverse() : clipPath },
{
duration: 300,
easing: 'ease-in',
pseudoElement: `::view-transition-${!isDark ? 'old' : 'new'}(root)`,
},
);
}
return (
<Button variant="ghost" size="icon" onClick={toggleDark}>
<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">Toggle theme</span>
</Button>
);
}
2、/app/glocals.css 文件中加入过渡样式:
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-old(root),
.dark::view-transition-new(root) {
z-index: 1;
}
::view-transition-new(root),
.dark::view-transition-old(root) {
z-index: 9999;
}
使用方法
在需要的位置引入组件:
import ThemeModeButton from '@/components/ThemeModeButton';
<ThemeModeButton />
最终效果
猜你喜欢
- 2025-09-06 还在用 Transition 和 Animation?View Transition出炉了!
- 2025-09-06 css实现转圈加载动画效果_css3实现转动效果
- 2025-09-06 能让下属尊敬同时追随的领导,都有这6个特征
- 2025-09-06 Strategic autonomy key to India improving relations with China
- 2025-09-06 第8章 路由与导航_路由的导航钩子
- 2025-09-06 谁用谁知道,拿走不谢!input框钻石切割边框+渐变 蓝色光晕效果
- 2025-09-06 为什么我放弃了Tailwind,回归了"朴素"的BEM + Sass/SCSS?
- 2025-09-06 滚动字幕怎么制作?_区域滚动字幕怎么制作
- 2025-06-18 如何在 WordPress 中添加 CSS 进度条(无 jQuery 或 JavaScript)
- 2025-06-18 原生JS实现惯性滚动,给鼠标滚轮增加阻尼感,纵享丝滑
- 最近发表
-
- count(*)、count1(1)、count(主键)、count(字段) 哪个更快?
- 深入探索 Spring Boot3 中 MyBatis 的 association 标签用法
- js异步操作 Promise fetch API 带来的网络请求变革—仙盟创梦IDE
- HTTP状态码超详细说明_http 状态码有哪些
- 聊聊跨域的原理与解决方法_跨域解决方案及原理
- 告别懵圈!产品新人的接口文档轻松入门指南
- 在Javaweb中实现发送简单邮件_java web发布
- 优化必备基础:Oracle中常见的三种表连接方式
- Oracle常用工具使用 - AWR_oracle工具有哪些
- 搭载USB 3.1接口:msi 微星 发布 990FXA Gaming 游戏主板
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)