Introduction#

This demo shows how to create a Toggle Theme using only Tailwind and HTML. Then it also shows how to create a simple HERO section with image background and loop video.

  • Toggle theme with Tailwind and CSS
  • Toggle theme with Tailwind and Next.js

Toggle Theme#

Init a new tailwind project

npm install -D tailwindcss
npx tailwindcss init

Below is the project structure

|--src
|--toggle-theme.html
|--input.css
|--masthead.m4v
|--masthead-poster.jpg
|--dist
|--output.css
|--package.json
|--run.sh
|--tailwind.config.js

This is the content of package.json

{
"devDependencies": {
"tailwindcss": "^3.3.2"
}
}

The command to compile tailwind css

npx tailwindcss -i ./src/input.css -o ./dist/output.css --watch

First update the tailwind.config.js with darkMode: class

module.exports = {
darkMode: "class",
content: ["./src/**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}

then follow docs to setup javascript to togglet theme

localStorage.theme = "dark";
document.getElementById("button").addEventListener("click", () => {
if (document.getElementById("sun").classList.contains("hidden")) {
document.getElementById("sun").classList.remove("hidden");
document.getElementById("moon").classList.add("hidden");
} else {
document.getElementById("moon").classList.remove("hidden");
document.getElementById("sun").classList.add("hidden");
}
if (localStorage.theme === "dark") {
localStorage.theme = "light";
document.documentElement.classList.remove("dark");
document.documentElement.classList.add("light");
} else {
localStorage.theme = "dark";
document.documentElement.classList.remove("light");
document.documentElement.classList.add("dark");
}
});

Use some button icons (moon and sun) from Heroicons

<body>
<div class="min-h-screen dark:bg-slate-800">
<h1 class="dark:text-white">Hello</h1>
<button
class="bg-orange-400 rounded-3xl p-2 w-14 cursor-pointer"
id="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
id="sun"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z"
/>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="{1.5}"
stroke="currentColor"
className="w-6 h-6"
id="moon"
class="hidden"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z"
/>
</svg>
</button>
<script>{toggle script there}</script>
</div>

Togge Theme Next.js#

Create a new Next.js project

npx create-app-next@latest .

Project structure

|--app
|--global.css
|--layout.tsx
|--page.tsx
|--components
|--header.tsx
|--public
|--next.config.js
|--package.json
|--tailwind.config.js

Similarly, update the tailwind.config.js with darkMode:class

module.exports = {
darkMode: "class",
content: ["./src/**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}

Then create a simple Toggle theme button, actually it wrapped in to a Header component

"use client";
import { useEffect, useState } from "react";
import { IoSunny, IoMoon } from "react-icons/io5/index.js";
const themes = ["light", "dark"];
export const Header = () => {
const [theme, setTheme] = useState(() => {
// theme from local storage
if (typeof localStorage !== "undefined" && localStorage.getItem("theme")) {
return localStorage.getItem("theme");
}
// theme from preferes
if (
typeof window !== "undefined" &&
window.matchMedia("(prefers-color-scheme: dark)").matches
) {
return "dark";
}
return "light";
});
useEffect(() => {
if (theme === "light") {
document.documentElement.classList.remove("dark");
} else {
document.documentElement.classList.add("dark");
}
}, [theme]);
return (
<div className="bg-slate-200 dark:bg-slate-900 dark:text-white">
<div className="max-w-3xl mx-auto flex flex-row justify-between items-center px-3 py-3">
<h2 className="font-semibold tracking-tighter font-mplus text-lg">
Takuya Matsuyama
</h2>
{MAGIC TOGGLE BUTTON}
</div>
</div>
);
};

Please take note the magic toggle button

<div className="inline-flex items-center p-[3px] rounded-3xl bg-orange-300 dark:bg-zinc-600">
{themes.map((t) => {
const checked = t === theme;
return (
<button
className={`${
checked ? "bg-white text-black" : ""
} cursor-pointer rounded-3xl p-2`}
onClick={() => {
console.log("change Theme");
const t = theme === "light" ? "dark" : "light";
localStorage.setItem("theme", t);
setTheme(t);
}}
>
{t === "light" ? <IoSunny /> : <IoMoon />}
</button>
);
})}
</div>

Finally, we can use the Header component in the root layout (layout.tsx)

import "./globals.css";
import { Inter } from "next/font/google";
import { Header } from "@/components/header";
const inter = Inter({ subsets: ["latin"] });
export const metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className="dark">
<body className={inter.className}>
<Header></Header>
{children}
</body>
</html>
);
}

Reference#