Using light-dark() would be appealing to me, but it is part of the 2024 baseline and somewhat raises the minimum browser version requirement set by TailwindCSS v4 - which is already high - originally targeting the 2023 baseline.
Required minimum browser versions:
Tailwind CSS v4 (without light-dark()) |
with light-dark() |
|
|---|---|---|
| Chrome 111 | Chrome 123 | (+) |
| Safari 16.4 | Safari 17.5 | (+) |
| Firefox 128 | Firefox 128 | (=) |
The LightningCSS engine that TailwindCSS v4 uses under the hood provides a solution for a polyfill-like replacement of light-dark() with some extra manual code.
light-dark() color functionHowever, for compatibility reasons, TailwindCSS has simply disabled the use of this feature here.
tailwindlabs/tailwindcss issue #15438 - [v4] light-dark is broken in optimized buildHow can I still use light-dark() without increasing the minimum browser version requirement, even without relying on this feature?
document.querySelector('button').addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
});
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
@custom-variant dark (&:where(.dark, .dark *));
@theme {
--color-primary: light-dark(#da373d, #fd96b0);
}
* {
color-scheme: light; /* apply "light" (first) color from light-dark() */
@variant dark {
color-scheme: dark; /* apply "dark" (second) color from light-dark() */
}
}
</style>
<h1 class="m-4 text-primary text-3xl font-bold underline text-clifford">
Hello world!
</h1>
<button class="m-4 px-4 py-2 bg-sky-700 hover:bg-sky-950 text-sky-50 rounded-md cursor-pointer">Toggle Light/Dark</button>
It works, but for the reasons detailed in the question, it increases the minimum browser version requirements.
For the reasons described above, the code snippet only works on Chrome 123+ and Safari 17.5+. However, I'd like an alternative so that I don't have to target these versions, but instead align with the Chrome 111+ and Safari 16.4+ versions preferred by v4.
The goal is to be able to declare the light and dark color-scheme values in a single line, similar to light-dark(), so that both can be seen at once in one line.
Note: I like the light-dark() solution, but I don't want to impose Baseline 2024 browser requirements on my project, as this could potentially cause me to lose visitors.
By ignoring LightningCSS's built-in polyfill, the solution can be implemented manually using --tw-light and --tw-dark variables. For each variable value not currently in use, assign the empty value for color-scheme; the variable that is active should be set to an initial value so that its fallback color is ultimately applied as the primary color. This approach requires using @variant dark along with a custom @custom-variant dark manual override.
document.querySelector('button').addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
});
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
@custom-variant dark (&:where(.dark, .dark *));
@theme inline {
--color-primary: var(--tw-light, #da373d) var(--tw-dark, #fd96b0);
}
* {
color-scheme: light;
--tw-light: initial;
--tw-dark: ;
@variant dark {
color-scheme: dark;
--tw-light: ;
--tw-dark: initial;
}
}
</style>
<h1 class="m-4 text-primary text-3xl font-bold underline text-clifford">
Hello world!
</h1>
<button class="m-4 px-4 py-2 bg-sky-700 hover:bg-sky-950 text-sky-50 rounded-md cursor-pointer">Toggle Light/Dark</button>
Note: Since @theme ships the given value into a global variable, the value cannot be a variable itself; otherwise, CSS cannot properly track the fallback values. Therefore, you should always use @theme inline. @theme inline does not embed the values into a global variable. However, it isn't necessary, since later on we don't want to override the color for other themes, as each theme can be declared locally in a single line.
Required minimum browser versions:
| Tailwind CSS v4 | with var(--tw-light, ...) var(--tw-dark, ...) |
|
|---|---|---|
| Chrome 111 | Chrome 111 | (=) |
| Safari 16.4 | Safari 16.4 | (=) |
| Firefox 128 | Firefox 128 | (=) |
The advantage of light-dark() is that it would work without a custom dark declaration, relying solely on the proper color-scheme setting. However, to maintain the minimum browser version, this must be implemented manually using CSS variables.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With