Next Theme
Установка
yarn add next-themes @heroicons/react
Я использую в данном решение @heroicons, но можно использовать, например: React Icons
Настройки
module.exports = { darkMode: 'class',}
import DarkMode from '../../components/DarkMode'import Providers from '../../components/Providers'
export default function RootLayout({ children }) { return ( <html lang="en"> <body className='dark:bg-slate-900 dark:text-white'> <Providers> <DarkMode /> // Тут Buttom и Select {children} </Providers> </body> </html> )}
Компоненты
'use client'
import { ThemeProvider } from "next-themes";
export default function Providers({children}) { return ( <ThemeProvider forcedTheme={children.theme || undefined} attribute="class"> {children} </ThemeProvider> )}
'use client'
import { useTheme } from 'next-themes'import { useEffect, useState } from 'react'import { SunIcon } from '@heroicons/react/24/outline'import { MoonIcon } from '@heroicons/react/24/outline'
export default function DarkModeButton() { const [mounted, setMounted] = useState(false) const { theme, setTheme, systemTheme } = useTheme()
// useEffect выполняется только на клиенте, поэтому теперь мы можем безопасно показать пользовательский интерфейс useEffect(() => { setMounted(true) }, [])
if (!mounted) { return null }
const currentTheme = theme === 'system' ? systemTheme : theme
return ( <div className="flex items-center gap-4"> <select className='dark:text-black py-1 focus:outline-none focus:ring-0 border-0 bg-gray-100 rounded-md text-sm' value={theme} onChange={e => setTheme(e.target.value)}> <option value="system">Системная</option> <option value="dark">Темная тема</option> <option value="light">Светлая тема</option> </select>
{currentTheme === 'dark' ? ( <SunIcon onClick={() => setTheme('light')} className="h-6 w-6 text-yellow-500 cursor-pointer" /> ) : ( <MoonIcon onClick={() => setTheme('dark')} className="h-6 w-6 text-slate-900 cursor-pointer" /> )} </div> )}
Headless UI
yarn add @headlessui/react @heroicons/react
'use client'
import { useTheme } from 'next-themes'import { Fragment, useEffect, useState } from 'react'import { Menu, Transition } from '@headlessui/react'import { ComputerDesktopIcon, MoonIcon, SunIcon } from '@heroicons/react/20/solid'
export default function Theme() { const [mounted, setMounted] = useState(false) const { theme, setTheme, systemTheme } = useTheme()
// useEffect выполняется только на клиенте, поэтому теперь мы можем безопасно показать пользовательский интерфейс useEffect(() => { setMounted(true) }, [])
if (!mounted) { return null }
const currentTheme = theme === 'theme' ? systemTheme : theme
return ( <> <Menu as="div" className="relative inline-block text-left"> <div> <Menu.Button className="inline-flex w-full justify-center p-2 font-semibold border rounded-lg"> {currentTheme === 'system' && <ComputerDesktopIcon className="h-5 w-5" aria-hidden="true" />} {currentTheme === 'light' && <SunIcon className="h-5 w-5" aria-hidden="true" />} {currentTheme === 'dark' && <MoonIcon className="h-5 w-5" aria-hidden="true" />} </Menu.Button> </div> <Transition as={Fragment} enter="transition ease-out duration-100" enterFrom="transform opacity-0 scale-95" enterTo="transform opacity-100 scale-100" leave="transition ease-in duration-75" leaveFrom="transform opacity-100 scale-100" leaveTo="transform opacity-0 scale-95" > <Menu.Items className="absolute right-0 z-20 mt-2 w-48 p-2 origin-top-right rounded-lg bg-white shadow-lg font-medium">
<Menu.Item> {({ active }) => ( <button onClick={() => setTheme('light')} className={`${ active ? 'bg-light-primary text-white' : 'text-gray-900' } group flex w-full items-center rounded-md px-2 py-2 text-sm`} > {active ? ( <SunIcon className="mr-2 h-6 w-6" aria-hidden="true" /> ) : ( <SunIcon className="mr-2 h-6 w-6" aria-hidden="true" /> )} Всегда светлый </button> )} </Menu.Item> <Menu.Item> {({ active }) => ( <button onClick={() => setTheme('dark')} className={`${ active ? 'bg-light-primary text-white' : 'text-gray-900' } group flex w-full items-center rounded-md px-2 py-2 text-sm`} > {active ? ( <MoonIcon className="mr-2 h-6 w-6" aria-hidden="true" /> ) : ( <MoonIcon className="mr-2 h-6 w-6" aria-hidden="true" /> )} Всегда темный </button> )} </Menu.Item>
<Menu.Item> {({ active }) => ( <button onClick={() => setTheme('system')} className={`${ active ? 'bg-light-primary text-white' : 'text-gray-900' } group flex w-full items-center rounded-md px-2 py-2 text-sm`} > {active ? ( <ComputerDesktopIcon className="mr-2 h-6 w-6" aria-hidden="true" /> ) : ( <ComputerDesktopIcon className="mr-2 h-6 w-6" aria-hidden="true" /> )} Как в системе </button> )} </Menu.Item>
</Menu.Items> </Transition> </Menu> </> )}