I'm trying to implement dark mode in my Next.js app using TailwindCSS. However, TailwindCSS's dark: classes are not being applied, even when the .dark class is present on the <html> tag.
global.css
@import "tailwindcss";
@import "tw-animate-css";
@plugin "@tailwindcss/typography";
/* @custom-variant dark (&:is(.dark *)); */
/*
body::before {
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(
rgba(0, 0, 0, 0.1),
rgba(0, 0, 0, 0.1)
),url("/test.jpg");
background-size: 750px;
background-repeat: repeat;
background-position: center;
z-index: -1;
pointer-events: none;
}
*/
@theme inline {
--color-secondary: var(--color-neutral-500);
--shadow-aceternity:
0px 2px 3px -1px rgba(0, 0, 0, 0.1),0px 1px 0px 0px rgba(25,28,33,0.02),
0px 0px 0px 1px rgba(25, 28, 33, 0.08)
;
--shadow-inset:inset 0px 0px 4px 0.1px #00000010;
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-primary-foreground: var(--primary-foreground);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--color-foreground: var(--foreground);
--color-background: var(--background);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}
:root {
--color-primary: var(--color-neutral-800);
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
/* --primary: oklch(0.205 0 0); */
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--shadow-aceternity: 0px 2px 8px -1px rgba(255, 255, 255, 0.8),
0px 4px 12px -2px rgba(255, 255, 255, 0.8),
0px 0px 0px 1px rgba(255, 255, 255, 0.8);
--color-primary: var(--color-neutral-100);
--color-secondary: var(--color-neutral-400);
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
/* --primary: oklch(0.922 0 0); */
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
heading.tsx
"use client";
import React from "react";
import { cn } from "@/lib/utils";
import { motion } from "motion/react";
const Heading = ({
as: Tag = "h1",
children,
className,
}: {
as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
children: React.ReactNode;
className?: string;
}) => {
return (
<motion.div
initial={{ opacity: 0, filter: "blur(10px)", y: 10 }}
whileInView={{ opacity: 1, filter: "blur(0px)", y: 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
viewport={{ once: true }}
>
<Tag
className={cn(
"text-primary dark:text-primary px-4 text-2xl font-bold tracking-tighter drop-shadow-lg md:text-4xl",
className,
)}
>
{children}
</Tag>
</motion.div>
);
};
export default Heading;
When I switch to dark mode:
- Background and foreground colors update correctly.
- But Tailwind’s
dark:variant does not work (e.g.,dark:text-primary).
I already verified that:
.darkclass exists on<html>- Tailwind is configured with
darkMode: "class"
Why are Tailwind dark: classes not applying, even though the .dark class is set?
2 Answers 2
By default the dark: variant uses @media (prefers-color-scheme: dark) instead of a class name.
Thus, to get dark: to work with the dark class name, consider uncommenting the line:
/* @custom-variant dark (&:is(.dark *)); */
Or use the one explicitly given in the docs:
@custom-variant dark (&:where(.dark, .dark *));
This will modify the dark: variant to apply based on the dark class name.
Comments
Your @theme inline block and custom CSS variables override all color utilities, but they don’t define Tailwind’s actual color tokens.
Tailwind generates:
.dark .text-primary { color: theme("colors.primary") }
But your global CSS overrides --primary and --primary-foreground directly inside .dark.
And since your base layer forces:
* {
@apply border-border outline-ring/50;
}
And:
body {
@apply bg-background text-foreground;
}
Those use CSS variables, which override internal Tailwind utilities, including dark: variants.
Tailwind’s dark: variant only works if .dark is on <html> or <body>.
Comments
Explore related questions
See similar questions with these tags.
darkModeproperty intailwind.config.js. I think this is essentially a duplicate of the v4 'how to configure a theme' (e.g., a dark theme) question.