tailwindcss-theme-variants icon indicating copy to clipboard operation
tailwindcss-theme-variants copied to clipboard

Using both selectors and media-queries not working as expected.

Open mia-riezebos opened this issue 1 year ago • 9 comments

In the docs on the website, there's a table specifying that if a selector matches, it takes precedence over any media query.

match neither prefers-color-scheme: light prefers-color-scheme: dark
neither none cyan navy
.cyan cyan cyan cyan
.navy navy navy navy

As previously noted, when a required selector is present, it takes precendence over the media queries. Stated another way, the media queries only matter when no selector matches.

The thing is, in my project, my themes are not behaving as described above.

My config is like such:

// tailwind.config.cjs
// ...
	variants: {
		extend: {
			backgroundColor: ['schemes'],
			textColor: ['schemes'],
			fill: ['schemes'],
			stroke: ['schemes'],
			borderColor: ['schemes'],
			outlineColor: ['schemes'],
			ringColor: ['schemes'],
			ringOffsetColor: ['schemes'],
			boxShadow: ['schemes']
		}
	},
	plugins: [
		// ...
		// Color Schemes
		themeVariants({
			group: 'schemes',
			themes: {
				// Basic Dark / Light Themes
				dark: {
					mediaQuery: prefersDark,
					selector: "[data-theme='dark']",
				},

				light: {
					mediaQuery: prefersLight,
					selector: "[data-theme='light']",
				}

				// // Inverted Colors (make sure there's no compatibility issues with inverted colors)
				// 'colors-inverted': {
				// 	mediaQuery: colorsInverted
				// }
			},
			// baseSelector: 'html',
			// fallback: 'dark'
		}),
		// ...
	]

The generated CSS (for example on the light theme), looks like this:

@media (prefers-color-scheme: light){
        :root[data-theme='light'] .light\:bg-other-green-300{
                --tw-bg-opacity: 1;
                background-color: rgb(134 239 172 / var(--tw-bg-opacity));
        }
}

and behaves as you would expect from the generated css; BOTH the media query AND the selector [data-theme='light'] have to match for the background to turn green.

mia-riezebos avatar Jun 15 '24 20:06 mia-riezebos

Thanks for the report! I can reproduce this. I'm going to try my best to fix this, but Tailwind CSS and related has changed a lot since my last update to this project, and I haven't been working with JavaScript lately, so it might be a struggle and I might just have to post a snippet of code that bypasses this plugin that you can use in case I can't update this project without breakage. We'll see, but here's hoping.

JNavith avatar Jun 15 '24 22:06 JNavith

This is happening because recent versions of Tailwind CSS reserve special meaning for the dark variant. Even though it's unfortunate that this can't be disabled, they at least allow it to be customized. So, as long as I'm right to assume you are using version 3 (e.g. 3.4.4 is what I'm testing on), you can trim out this plugin and use an easier and terser approach:

// possibly const plugin = require("tailwindcss/plugin"); instead, but you can switch from .cjs to .js and use ESM syntax in modern Tailwind versions
import plugin from 'tailwindcss/plugin';

/** @type {import('tailwindcss').Config} */
// possibly module.exports = instead, but you can switch from .cjs to .js and use ESM syntax in modern Tailwind versions
export default {
	darkMode: ['variant', ['@media (prefers-color-scheme: dark) { :root & }', ':root[data-theme=dark] &']],

	theme: {
                // ... your customizations here
		extend: {}
	},
        // variants: section no longer needed in v3
	plugins: [
		plugin(({ addVariant }) => {
			addVariant('light', ['@media (prefers-color-scheme: light) { :root & }', ':root[data-theme=light] &']);
		})
	]
}

This works for your current configuration without having to refactor the HTML.

A note on potential future needs, though: because their dark variant will only appear after the light variant in the CSS, it wouldn't be possible to have dark as the fallback theme without raising the specificity in the light variant if it comes to that.

JNavith avatar Jun 15 '24 23:06 JNavith

An alternative option that takes more work to implement:

It is also an option not to use the built-in darkMode, but disabling it is not possible, so you'd have to rename the theme variant so that it doesn't clash:

// if you continue to use my plugin
themes: {
        // example names
	"dark-theme": { ... },
	"light-theme": { ... }
}


// if you use the approach bypassing this plugin
addVariant('theme-dark', ['@media (prefers-color-scheme: dark) { :root & }', ':root[data-theme=dark] &']);
addVariant('theme-light', ['@media (prefers-color-scheme: light) { :root & }', ':root[data-theme=light] &']);

and go back and change all occurrences of light: and dark: in the HTML (youch - why I wouldn't go through with this option except if you know you need a capability that the built-in darkMode customization can't provide (e.g. fallback)) to their new variant names.

JNavith avatar Jun 15 '24 23:06 JNavith

Implementing fallback without my plugin would be done by changing the order of the addVariant calls to change the order they appear in the CSS, and writing :not calls like this to replicate how my plugin would've done it:

// theme-dark will apply as long as the light theme hasn't been activated by `prefers-color-scheme: light` or by `[data-theme=light]`
addVariant('theme-dark', ':root &');			
// but this can override it (it will have enough specificity in the CSS)
addVariant('theme-light', [
    '@media (prefers-color-scheme: light) { :root:not([data-theme=dark]) & }',
    '[data-theme=light] &'
]);

(where here, and in the messages before this, :root is the base selector)

JNavith avatar Jun 16 '24 00:06 JNavith

Regardless of these workarounds, this plugin and its documentation need to be updated to address this issue. Thanks again for the report.

JNavith avatar Jun 16 '24 00:06 JNavith

Disabling darkMode in the tailwind config is actually possible using darkMode: false. I ran into this with Supermaven and it's not documented, but it does work. This will have the plugin generate the dark: variant properly again, but the generated styles, even for light mode don't match the described behaviour.

thanks for looking into this!

mia-riezebos avatar Jun 16 '24 18:06 mia-riezebos

I quite like the syntax and provided media query exports of this plugin, and would enjoy continued use of it!

At this time, I am circumventing the issue by adding two auto themes

		// Color Schemes
		themeVariants({
			group: 'schemes',
			themes: {
				// Basic Dark / Light Themes
				dark: {
					selector: ":is([data-theme='dark'], :has(>[data-theme='dark']))"
				},

				light: {
					selector: ":is([data-theme='light'], :has(>[data-theme='light']))"
				},

				'auto-light': {
					selector: ":is([data-theme='auto'], :has([data-theme='auto']))",
					mediaQuery: prefersLight
				},

				'auto-dark': {
					selector: ":is([data-theme='auto'], :has([data-theme='auto']))",
					mediaQuery: prefersDark
				}

				// // Inverted Colors (make sure there's no compatibility issues with inverted colors)
				// 'colors-inverted': {
				// 	mediaQuery: colorsInverted
				// }
			},
			baseSelector: '*'
			// fallback: 'auto-dark'
		}),

It is a little... verbose, but it was the solution I came up with the quickest, which is what mattered at this time. The main annoyance is providing all theme variants whenever I style an element.

again, thanks for getting back to me, I'll give the built-in variant config a try.

mia-riezebos avatar Jun 16 '24 18:06 mia-riezebos

Ok so I like your workaround approach but I did still wanna keep the syntax from your plugin sooo

@mia-cx/tailwindcss-themes

I will add attribution in the readme in the next commit, there's already a little comment at the top of the index file

mia-riezebos avatar Jun 17 '24 03:06 mia-riezebos

Seen as this repo seems stale, I would be down to maintain a spiritual successor of this plugin. I don't see a future where I stop working in ts/js soon.

Let me know!

mia-riezebos avatar Jun 19 '24 12:06 mia-riezebos