# Implementing Dark and Light Theme Transitions in Cloned Websites: A Complete Guide

> Learn to implement dark and light theme transitions in cloned websites using CSS custom properties and Tailwind CSS. Follow our complete guide for seamless theme switching in Next.js applications.

- Repository: [JCodesMore/ai-website-cloner-template](https://github.com/JCodesMore/ai-website-cloner-template)
- Tags: how-to-guide
- Published: 2026-06-28

---

**The ai-website-cloner-template uses CSS custom properties, a custom Tailwind dark variant, and a class-toggling mechanism on the root html element to enable seamless theme transitions in cloned Next.js 16 applications.**

The **ai-website-cloner-template** repository provides a production-ready foundation for recreating websites with pixel-perfect fidelity. Its theming architecture leverages Next.js 16, Tailwind 4, and a utility-first approach to deliver smooth dark and light theme transitions that persist across user sessions.

## How the Theming System Works

The template implements a three-layer theming strategy that separates design tokens from implementation logic.

### CSS Custom Properties for Design Tokens

All color values, border radii, and font families are defined as CSS variables in [`src/app/globals.css`](https://github.com/JCodesMore/ai-website-cloner-template/blob/main/src/app/globals.css). The file declares two distinct palettes: a `:root` block for light mode and a `.dark` block for dark mode. This variable-based approach ensures that any component can reference semantic names like `--background` or `--foreground` without hardcoding hex values.

### Tailwind's Custom Dark Variant

Rather than relying on media queries alone, the template registers a custom variant in [`src/app/globals.css`](https://github.com/JCodesMore/ai-website-cloner-template/blob/main/src/app/globals.css) using the `@custom-variant` directive:

```css
@custom-variant dark (&:is(.dark *));

```

This allows any Tailwind utility class to react to a parent `.dark` class, enabling explicit manual control over theme states while maintaining utility-first ergonomics.

### Deterministic Class Merging with `cn`

The `cn` helper function in [`src/lib/utils.ts`](https://github.com/JCodesMore/ai-website-cloner-template/blob/main/src/lib/utils.ts) wraps `clsx` and `tailwind-merge` to produce deterministic class strings. When toggling themes dynamically, this prevents class conflicts and ensures the final CSS output remains predictable, especially when conditionally applying dark-mode utilities.

## Step-by-Step Implementation

Implementing theme transitions requires coordinating CSS definitions, React state, and DOM manipulation.

### Define Color Palettes in CSS Variables

Configure your design tokens in [`src/app/globals.css`](https://github.com/JCodesMore/ai-website-cloner-template/blob/main/src/app/globals.css) using CSS custom properties. Define light mode values in the `:root` selector and dark mode overrides in the `.dark` selector:

```css
:root {
  --background: #ffffff;
  --foreground: #171717;
  --card: #ffffff;
  --card-foreground: #171717;
  --border: #e5e5e5;
}

.dark {
  --background: #0a0a0a;
  --foreground: #fafafa;
  --card: #0a0a0a;
  --card-foreground: #fafafa;
  --border: #262626;
}

```

### Register the Tailwind Dark Variant

Add the custom variant declaration at the top of [`src/app/globals.css`](https://github.com/JCodesMore/ai-website-cloner-template/blob/main/src/app/globals.css) to enable the `dark:` modifier in your Tailwind classes:

```css
@custom-variant dark (&:is(.dark *));

```

This registers the variant with Tailwind 4's engine, allowing utilities like `dark:bg-background` or `dark:text-foreground` to function correctly when the `.dark` class is present on a parent element.

### Create the Theme Toggle Component

Build a React component that manages theme state and persists preferences to `localStorage`. The component toggles the `.dark` class on `document.documentElement`:

```tsx
import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";

export function ThemeToggle() {
  const [dark, setDark] = useState(() => {
    if (typeof localStorage !== "undefined") {
      return localStorage.getItem("theme") === "dark";
    }
    return false;
  });

  useEffect(() => {
    const html = document.documentElement;
    if (dark) {
      html.classList.add("dark");
      localStorage.setItem("theme", "dark");
    } else {
      html.classList.remove("dark");
      localStorage.setItem("theme", "light");
    }
  }, [dark]);

  return (
    <button
      onClick={() => setDark(!dark)}
      className={cn(
        "p-2 rounded transition-colors",
        dark ? "bg-muted-foreground text-muted" : "bg-muted text-muted-foreground"
      )}
      aria-label="Toggle dark / light theme"
    >
      {dark ? "🌙" : "☀️"}
    </button>
  );
}

```

### Integrate with the Root Layout

The [`src/app/layout.tsx`](https://github.com/JCodesMore/ai-website-cloner-template/blob/main/src/app/layout.tsx) file renders the root `<html>` element, making it the ideal location to initialize theme logic. Import the `ThemeToggle` component and place it in your header or navigation area:

```tsx
import { ThemeToggle } from "@/components/ThemeToggle";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className="h-full antialiased">
      <body className="min-h-full flex flex-col">
        <header className="p-4 flex justify-end">
          <ThemeToggle />
        </header>
        {children}
      </body>
    </html>
  );
}

```

## Using Theme-Aware Classes in Components

When building UI components, use semantic Tailwind classes that map to your CSS variables. The `cn` utility ensures safe class composition when conditionally applying styles:

```tsx
import { cn } from "@/lib/utils";

export function Card({ children }: { children: React.ReactNode }) {
  return (
    <div
      className={cn(
        "rounded-lg p-6 shadow",
        "bg-card text-card-foreground",
        "border border-border"
      )}
    >
      {children}
    </div>
  );
}

```

Because `bg-card` and `text-card-foreground` reference CSS variables that change values between `:root` and `.dark` contexts, the component automatically adapts to the current theme without explicit dark-mode logic in the component code.

## Summary

- **CSS variables** in [`src/app/globals.css`](https://github.com/JCodesMore/ai-website-cloner-template/blob/main/src/app/globals.css) define light and dark color palettes under `:root` and `.dark` selectors.
- **Tailwind's custom variant** (`@custom-variant dark`) enables the `dark:` prefix for utility classes.
- **DOM class toggling** on the root `<html>` element triggers theme transitions and persists via `localStorage`.
- **The `cn` helper** in [`src/lib/utils.ts`](https://github.com/JCodesMore/ai-website-cloner-template/blob/main/src/lib/utils.ts) provides deterministic class merging for dynamic theme classes.
- **Root layout integration** in [`src/app/layout.tsx`](https://github.com/JCodesMore/ai-website-cloner-template/blob/main/src/app/layout.tsx) ensures the theme applies to the entire document tree.

## Frequently Asked Questions

### How does the theme preference persist across page reloads?

The theme toggle component initializes its state by checking `localStorage` for a stored "theme" key. When the user clicks the toggle, a `useEffect` hook updates both the DOM class and the storage value, ensuring the preference remains active across sessions and page navigations.

### Why use a custom Tailwind variant instead of the media query strategy?

The `@custom-variant dark (&:is(.dark *));` approach provides explicit manual control, allowing users to override system preferences. This is essential for cloned websites where designers may want to force a specific theme or provide a toggle independent of the operating system's dark mode setting.

### What is the role of the `cn` utility function?

The `cn` function combines `clsx` for conditional class handling and `tailwind-merge` for conflicting class resolution. When implementing theme transitions, it ensures that dynamically constructed class strings (like those containing `dark:` variants) resolve to a single, predictable CSS output without duplicates or conflicting rules.

### Can I use these CSS variables outside of Tailwind classes?

Yes. Because the colors are defined as standard CSS custom properties (`--background`, `--foreground`, etc.), you can reference them in inline styles, CSS modules, or even JavaScript via `getComputedStyle`. This flexibility allows custom components or third-party libraries to respect the theme even when not using Tailwind utilities.