So the Tailwind 4 released. Most breaking change was introduction of new theme configuration method - you can only do it in CSS now. Here is an official example:
@import 'tailwindcss';
@theme {
--font-display: 'Satoshi', 'sans-serif';
--breakpoint-3xl: 1920px;
--color-avocado-100: oklch(0.99 0 0);
--color-avocado-200: oklch(0.98 0.04 113.22);
--color-avocado-300: oklch(0.94 0.11 115.03);
--color-avocado-400: oklch(0.92 0.19 114.08);
--color-avocado-500: oklch(0.84 0.18 117.33);
--color-avocado-600: oklch(0.53 0.12 118.34);
--ease-fluid: cubic-bezier(0.3, 0, 0, 1);
--ease-snappy: cubic-bezier(0.2, 0, 0, 1);
/* ... */
}
The problem with this approach is that there is no easy way to define dark mode theme here as complained on their GitHub.
Solution
This is how I solved this issue in my React components system @creation-ui/react using @variant
directive:
@import 'tailwindcss';
@variant dark (&:where(.dark, .dark *));
:root {
--text-primary: oklch(0 0 0);
--text-secondary: oklch(0.556 0 0);
--background-primary: oklch(0.99 0 0);
--background-secondary: oklch(0.985 0 0);
--border: oklch(0.87 0 0);
@variant dark {
--text-primary: oklch(0.97 0 0);
--text-secondary: oklch(0.87 0 0);
--background-primary: oklch(0.269 0 0);
--background-secondary: oklch(0.371 0 0);
--border: oklch(0.35 0 0);
}
}
@theme {
--color-primary: oklch(60.48% 0.2165 257.21);
--color-warning: oklch(77.97% 0.1665 72.45);
--color-error: oklch(66.16% 0.2249 25.88);
--color-success: oklch(75.14% 0.1514 166.5);
--color-text-primary: var(--text-primary);
--color-text-secondary: var(--text-secondary);
--color-background-primary: var(--background-primary);
--color-background-secondary: var(--background-secondary);
--color-border: var(--border);
}
Basically in :root
we define both our light and dark color variables where @variant dark
decides of the variable values.
Because Tailwind generates color CSS classes (text-[color]-value
, bg-[color]-value
, border-[color]-value
, etc.) from --color-
variables, we can use them in our React components like so:
const Button = ({ children, ...props }) => {
return (
<button className={`bg-text-primary text-text-primary`} {...props}>
{children}
</button>
)
}
And that's it! Now you can use dynamic light/dark theme in your app and it will automatically switch to dark mode when you set dark
variant (here: a .dark
class on e.g. body
element).