This is an extension of Next /pacocoursey/next-themes repository
The /pacocoursey/next-themes repository give us the possibility to change set the theme to light
, dark
, and system
by default.
The library also provide a way to apply custom themes but when using it, it is not possible to set the correct invert
options for Tailwind
.
So, this library is compatible with:
- Next
- Tailwind
- Radix
- Shadcn ui
Which gives us a flexibility to set custom themes.
Tailwind sometimes use the dark:
flag to invert image colors.
dark:invert
So, when your theme has another name then not dark
the invertion will not work.
The solution is to use a theme-mode
with a theme-name
.
setTheme('dark', 'gym')
An abstraction for themes in your Next.js app.
Check out the Example to try it for yourself.
$ npm install next-many-themes
# or
$ yarn add next-many-themes
// src/context/theme-context.tsx
'use client'
import { ThemeProvider } from 'next-super-themes'
import { ThemeProviderProps } from 'next-super-themes/dist/types'
export default function CustomThemeProvider({ children, ...props }: ThemeProviderProps) {
return <ThemeProvider {...props}>{children}</ThemeProvider>
}
// app/layout.tsx
import { Exo_2 } from 'next/font/google'
import './globals.css'
import ThemeProvider from 'src/context/theme-context'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className={exo2.className}>
<ThemeProvider defaultMode="system" enableSystem>
{children}
</ThemeProvider>
</body>
</html>
)
}
//src/app/theme-changer.tsx
// import this component in your page to see the button to change the theme
'use client'
import { Wand } from 'lucide-react'
import { useTheme } from 'next-super-themes'
import { useEffect, useState } from 'react'
import { Button } from 'src/components/ui/button'
const themes: { mode: 'light' | 'dark'; theme: string | undefined }[] = [
{
mode: 'light',
theme: undefined
},
{
mode: 'dark',
theme: undefined
},
{
mode: 'dark',
theme: 'gym'
},
{
mode: 'dark',
theme: 'carservice'
},
{
mode: 'light',
theme: 'clinic'
},
{
mode: 'light',
theme: 'aesthetics'
},
{
mode: 'light',
theme: 'food'
}
]
export default function ThemeChager() {
const { setTheme, themeMode } = useTheme()
const [position, setPosition] = useState(-1)
function setNextTheme() {
if (position === themes.length - 1) {
setPosition(0)
} else {
setPosition(position + 1)
}
}
useEffect(() => {
if (position > -1) {
const theme = themes[position]
setTheme(theme.mode, theme.theme)
}
}, [position, setTheme])
return (
<div className="w-full flex justify-end">
<Button
className="bg-background hover:bg-background"
variant="ghost"
onClick={() => {
setNextTheme()
}}
>
<Wand />
</Button>
</div>
)
}
import ThemeChager from './theme-changer'
export default function Page() {
return (
<>
<AuthThemeChager />
</>
)
}
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
/* Colors */
/*End Colors*/
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 173 79% 52%;
--primary-foreground: 210 40% 2%;
--secondary: 221 92% 52%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--success: 138 87% 40%;
--success-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 71.4%;
--ring: 222.2 84% 4.9%;
--radius: 1.5rem;
}
.dark {
--background: 255 3% 10%;
--foreground: 210 60% 100%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 244 100% 60%;
--primary-foreground: 0 100% 100%;
--secondary: 173 100% 56%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 55% 60.6%;
--destructive-foreground: 210 40% 98%;
--success: 138 87% 50%;
--success-foreground: 210 40% 98%;
--border: 217.2 0% 50%;
--input: 217.2 0% 50%;
--ring: 212.7 26.8% 83.9%;
}
.dark.gym {
--background: 0 0% 8%;
--foreground: 210 20% 80%;
--primary: 118 100% 50%;
--primary-foreground: 100 20% 10%;
--secondary: 38 100% 50%;
--secondary-foreground: 210 40% 98%;
--input: var(--secondary);
--radius: 0.3rem;
}
.dark.carservice {
--background: 255 3% 10%;
--foreground: 210 60% 100%;
--primary: 0 100% 50%;
--primary-foreground: 255 100% 100%;
--secondary: 25 100% 65%;
--secondary-foreground: 210 40% 98%;
}
.light.clinic {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 210 100% 52%;
--primary-foreground: 210 40% 100%;
--secondary: 182 100% 25%;
--secondary-foreground: 222.2 47.4% 11.2%;
--radius: 0.5rem;
}
.light.aesthetics {
--background: 310 100% 99%;
--foreground: 222.2 84% 4.9%;
--primary: 310 100% 70%;
--primary-foreground: 210 40% 100%;
--secondary: 216 100% 70%;
--secondary-foreground: 222.2 47.4% 11.2%;
--radius: 0.8rem;
}
.light.food {
--background: 50 100% 50%;
--foreground: 222.2 84% 4.9%;
--primary: 15 100% 50%;
--primary-foreground: 210 40% 100%;
--secondary: 0 100% 50%;
--secondary-foreground: 222.2 47.4% 11.2%;
--radius: 0.8rem;
--input: var(--foreground);
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
html,
body {
width: 100%;
height: 100%;
}