Theming
Add a theme switcher and dark mode support to your Next.js application.
Theming
Adding dark mode support to your CyberCN project allows components to seamlessly switch between the default dark cyberpunk layout and their alternative high-contrast states.
Install next-themes
To manage client-side themes dynamically, begin by installing next-themes:
npm install next-themesInstall the Dropdown Menu
The mode toggle component uses the dropdown menu primitive. Make sure you install it in your project:
npx shadcn@latest add dropdown-menuCreate a Theme Provider
To make the current theme available throughout your application, create a ThemeProvider client component:
"use client";
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
export function ThemeProvider({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}Wrap your Root Layout
Add the ThemeProvider to your root layout. Make sure to also add the suppressHydrationWarning prop to your <html> element to prevent hydration errors from client-side theme transitions.
import { ThemeProvider } from "@/components/theme-provider";
import "./globals.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<head />
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
);
}Enable the Cyber scrollbar
CyberCN includes a custom scrollbar style in cybercn.css. To enable it globally, add the cyber-scrollbar class to the <html> element in your root layout.tsx.
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className="cyber-scrollbar" suppressHydrationWarning>
<head />
<body>{children}</body>
</html>
);
}Add a Mode Toggle
Create a mode-toggle.tsx file inside your components folder. This snippet uses customized brackets, a hard industrial rounded-none border, and sharp hover states designed specifically to match the CyberCN design system:
"use client";
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
export function ModeToggle() {
const { setTheme } = useTheme();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="icon"
className="relative rounded-none border-2 border-cybercn-primary bg-transparent transition-all duration-200 group overflow-hidden hover:bg-cybercn-primary hover:text-cybercn-primary-foreground dark:hover:bg-cybercn-primary dark:hover:text-cybercn-background focus-visible:ring-2 focus-visible:ring-cybercn-primary/40 focus-visible:ring-offset-0 focus-visible:outline-none"
>
<Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="w-44 rounded-none border-2 border-cybercn-primary bg-cybercn-background text-cybercn-foreground font-medium font-hacked uppercase tracking-tighter p-1"
>
<DropdownMenuItem
className="cursor-pointer whitespace-nowrap rounded-none focus:rounded-none focus:bg-cybercn-primary focus:text-cybercn-primary-foreground dark:focus:text-cybercn-background"
onClick={() => setTheme("light")}
>
[ Light_Mode ]
</DropdownMenuItem>
<DropdownMenuItem
className="cursor-pointer whitespace-nowrap rounded-none focus:rounded-none focus:bg-cybercn-primary focus:text-cybercn-primary-foreground dark:focus:text-cybercn-background"
onClick={() => setTheme("dark")}
>
[ Dark_Mode ]
</DropdownMenuItem>
<DropdownMenuItem
className="cursor-pointer whitespace-nowrap rounded-none focus:rounded-none focus:bg-cybercn-primary focus:text-cybercn-primary-foreground dark:focus:text-cybercn-background"
onClick={() => setTheme("system")}
>
[ System_Default ]
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}ModeToggle uses useTheme from next-themes internally and lets the user switch between light, dark, and system.
Core CSS variables
CyberCN styling is driven by CSS variables in components/ui/cybercn/styles/cybercn.css.
If you want to tune the visual system for your own project, override these variables in your global stylesheet.
Semantic theme tokens (recommended override layer)
These are the most important tokens to customize first:
--cybercn-background--cybercn-foreground--cybercn-primary--cybercn-primary-foreground--cybercn-secondary--cybercn-secondary-foreground--cybercn-muted--cybercn-muted-foreground--cybercn-accent--cybercn-accent-foreground--cybercn-border--cybercn-ring--cybercn-radius
Brand/palette helpers used by components
--cyber-yellow--cyber-red--cyber-blue--cyber-green--cyber-purple--cyber-orange--cyber-neon--cyber-border--cyber-black
Example overrides
:root {
--cybercn-background: oklch(0.94 0.16 100);
--cybercn-foreground: oklch(0.12 0.01 260);
--cybercn-primary: oklch(0.12 0.01 260);
--cybercn-primary-foreground: oklch(0.94 0.16 100);
--cybercn-accent: oklch(0.65 0.22 245);
--cybercn-border: oklch(0.2 0.02 260 / 0.2);
--cybercn-radius: 0rem;
}
.dark {
--cybercn-background: oklch(0.08 0 0);
--cybercn-foreground: oklch(0.93 0.2 107);
--cybercn-primary: oklch(0.93 0.2 107);
--cybercn-primary-foreground: oklch(0.08 0 0);
--cybercn-accent: oklch(0.6 0.24 250);
--cybercn-border: oklch(0.93 0.18 95 / 0.2);
}Start with semantic tokens and only override --cyber-* palette helpers when you need to reshape the brand colors globally.
Glitch timings (--glitched-duration-*) and Tailwind animate-* utilities are documented in Motion and effects.
clip-* utilities and border framing are summarized in Shapes and clipping.