{"id":19670785,"url":"https://github.com/jnavith/tailwindcss-theme-variants","last_synced_at":"2025-10-08T16:06:07.091Z","repository":{"id":46582540,"uuid":"261649699","full_name":"JNavith/tailwindcss-theme-variants","owner":"JNavith","description":"Media-query- or JavaScript-based theme variants with fallback for Tailwind CSS","archived":false,"fork":false,"pushed_at":"2023-10-04T02:42:03.000Z","size":1568,"stargazers_count":201,"open_issues_count":13,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-09-15T23:15:34.803Z","etag":null,"topics":["css-selectors","dark-mode","media-queries","prefers-color-scheme","tailwindcss","tailwindcss-plugin","theming"],"latest_commit_sha":null,"homepage":"https://tailwindcss-theme-variants.web.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JNavith.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-05-06T04:14:49.000Z","updated_at":"2025-08-29T01:07:39.000Z","dependencies_parsed_at":"2024-01-22T04:49:01.806Z","dependency_job_id":null,"html_url":"https://github.com/JNavith/tailwindcss-theme-variants","commit_stats":null,"previous_names":["jakenavith/tailwindcss-theme-variants","sirnavith/tailwindcss-theme-variants"],"tags_count":64,"template":false,"template_full_name":null,"purl":"pkg:github/JNavith/tailwindcss-theme-variants","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JNavith%2Ftailwindcss-theme-variants","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JNavith%2Ftailwindcss-theme-variants/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JNavith%2Ftailwindcss-theme-variants/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JNavith%2Ftailwindcss-theme-variants/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JNavith","download_url":"https://codeload.github.com/JNavith/tailwindcss-theme-variants/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JNavith%2Ftailwindcss-theme-variants/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278972389,"owners_count":26078028,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css-selectors","dark-mode","media-queries","prefers-color-scheme","tailwindcss","tailwindcss-plugin","theming"],"created_at":"2024-11-11T17:07:07.329Z","updated_at":"2025-10-08T16:06:07.072Z","avatar_url":"https://github.com/JNavith.png","language":"TypeScript","readme":"# 🌗 Tailwind CSS Theme Variants\n\n\n**This Tailwind CSS plugin registers variants for theming beyond just light and dark modes *without needing custom properties***. It has support for \n* Controlling themes with \n  * **Media queries**, like [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme), `print`, or anything you want\n  * **CSS selectors**, like classes and data attributes\n  * Or both at the same time!\n* **Responsive** variants\n* **Stacking** on extra **variants**, like `hover` so you can change a link's hover color depending on the theme\n* **Falling back** to a certain theme when no other one could become active, like if a visitor's browser doesn't support JavaScript or the new `prefers-` media queries\n* **As many themes as you want**: light theme, dark theme, red theme, blue theme—just bring your own definitions! The experimental `semantics` feature makes multiple themes easy to deal with!\n\nYou are recommended to check out [the comparison table of all Tailwind CSS theming plugins below](#alternatives) before committing to any one. By the way, you might have noticed this plugin's documentation / `README` is *very* long—don't let that frighten you! I designed it to be *overdocumented* and as exhaustive as possible, and since most of it is long code snippets, it's shorter than it looks *and* you don't need to go through it all to do well!\n\n\n# ⬇️ Installation\n\n```sh\nnpm install --save-dev tailwindcss-theme-variants\n```\n\n\n# 🛠 Basic usage\n\n## Using selectors to choose the active theme\n\nWith this Tailwind configuration,\n\n```js\nconst { themeVariants } = require(\"tailwindcss-theme-variants\");\n\nmodule.exports = {\n    theme: {\n        backgroundColor: {\n            \"gray-900\": \"#1A202C\",\n        },\n    },\n\n    plugins: [\n        themeVariants({\n            themes: {\n                light: {\n                    selector: \".light-theme\",\n                },\n                dark: {\n                    selector: \".dark-theme\",\n                },\n            },\n        }),\n    ],\n};\n```\n\nthis CSS is generated:\n\n```css\n.bg-gray-900 {\n    background-color: #1A202C\n}\n\n/* If you're having trouble understanding,\n   imagine it said html instead of :root,\n   like in the example HTML below */\n\n:root.light-theme .light\\:bg-gray-900 {\n    background-color: #1A202C\n}\n\n:root.dark-theme .dark\\:bg-gray-900 {\n    background-color: #1A202C\n}\n```\n\nWe can implement a simple themed button in HTML like this:\n\n```html\n\u003chtml class=\"light-theme\"\u003e \u003c!-- Change to dark-theme --\u003e\n    \u003cbutton class=\"light:bg-teal-200   dark:bg-teal-800 \n                   light:text-teal-700 dark:text-teal-100\"\u003e\n        \n        Sign up\n    \u003c/button\u003e\n\u003c/html\u003e\n```\n\nThis will result in dark blue text on a light blue background in the light theme, and light blue text on a dark blue background in the dark theme.\n\n💡 You can choose more than just classes for your selectors. Other, good options include data attributes, like `[data-padding=compact]`. You *can* go as crazy as `.class[data-theme=light]:dir(rtl)`, for example, but I think that's a bad idea!\n\n\n## Using media queries to choose the active theme\n\nYou may rather choose to tie your theme selection to matched media queries, like [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme):\n\n```js\nconst { themeVariants, prefersLight, prefersDark } = require(\"tailwindcss-theme-variants\");\n\nmodule.exports = {\n    theme: {\n        backgroundColor: {\n            \"teal-500\": \"#38B2AC\",\n        },\n    },\n\n    plugins: [\n        themeVariants({\n            themes: {\n                light: {\n                    mediaQuery: prefersLight /* \"@media (prefers-color-scheme: light)\" */,\n                },\n                dark: {\n                    mediaQuery: prefersDark /* \"@media (prefers-color-scheme: dark)\" */,\n                },\n            },\n        }),\n    ],\n};\n```\n\nWhich generates this CSS:\n\n```css\n.bg-teal-500 {\n    background-color: #38B2AC\n}\n\n@media (prefers-color-scheme: light) {\n    .light\\:bg-teal-500 {\n        background-color: #38B2AC\n    }\n}\n\n@media (prefers-color-scheme: dark) {\n    .dark\\:bg-teal-500 {\n        background-color: #38B2AC\n    }\n}\n```\n\n\n# ⚙️ Full configuration\n\nThis plugin expects configuration of the form\n\n```ts\n{\n    themes: {\n        [name: string]: {\n            // At least one is required\n            selector?: string,\n            mediaQuery?: string,\n        },\n    },\n\n    baseSelector?: string,\n    fallback?: boolean,\n}\n```\n\nWhere each parameter means:\n\n- `themes`: an object mapping a theme name to the conditions that determine whether or not the theme will be active.\n\n   - `selector`: this theme will be active when this selector is on `baseSelector`. For instance, if `baseSelector` is `html`, and the `light` theme's `selector` is `.light-theme`, then the `light` theme variants will be in effect whenever `html` has the `light-theme` class on it.\n\n   - `mediaQuery`: this theme will be active when this media query is active. For instance, if the `reduced-motion` theme has `mediaQuery` `\"@media (prefers-reduced-motion: reduce)\"` (importable as `prefersReducedMotion`), then the `reduced-motion` theme variants will be active whenever that media query matches: if the visitor's browser reports preferring reduced motion.\n\n- `baseSelector` (default `\"\"` (empty string) if you **only** use media queries to activate your themes, otherwise `\":root\"`): the selector that each theme's `selector` will be applied to to determine the active theme.\n\n- `fallback` (default `false`): when none of the given media queries or selectors are active, then the first theme you listed in `themes` will activate. You can think of it as the *default* theme for your site.\n\n\n# Examples\n💡 If you want to see the plugin get stretched to its limits, see the test suite in [`the tests directory`](https://github.com/JakeNavith/tailwindcss-theme-variants/blob/main/tests).\n\n## Fallback\n### Media queries\nWith the same media-query-activated themes as [above](#using-media-queries-to-choose-the-active-theme),\n```js\nthemes: {\n    light: {\n        mediaQuery: prefersLight /* \"@media (prefers-color-scheme: light)\" */,\n    },\n    dark: {\n        mediaQuery: prefersDark /* \"@media (prefers-color-scheme: dark)\" */,\n    },\n},\n```\nwe can create a table to show what the active theme will be under all possible conditions:\n\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr align=\"center\"\u003e\n\u003cth align=\"left\"\u003eMatching media query\u003c/th\u003e\n\u003cth\u003eNeither\u003c/th\u003e\n\u003cth\u003e\u003ccode\u003eprefers-color-scheme: light\u003c/code\u003e\u003c/th\u003e\n\u003cth\u003e\u003ccode\u003eprefers-color-scheme: dark\u003c/code\u003e\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\n\u003ctbody\u003e\n\u003ctr align=\"center\"\u003e\n\u003cth align=\"left\"\u003eActive theme\u003c/th\u003e\n\u003ctd\u003eNone\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003elight\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003edark\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n**The whole point of the fallback feature is to address that *None* case.** It could mean that the visitor is using a browser that doesn't [support `prefers-color-scheme`](https://caniuse.com/#search=prefers-color-scheme), such as IE11. Instead of leaving them on an unthemed site, we can \"push\" them into a particular theme by specifying `fallback`.\n\n```js\nthemes: {\n    light: {\n        mediaQuery: prefersLight /* \"@media (prefers-color-scheme: light)\" */,\n    },\n    dark: {\n        mediaQuery: prefersDark /* \"@media (prefers-color-scheme: dark)\" */,\n    },\n},\n// New addition\nfallback: true,\n// Because `light` is the first theme in the list, that is what will be fallen back to\n```\n\nWhich will change the generated CSS to activate `light` earlier than any media queries—since those are later in the file, they could still take precedent over this fallback case. **You could think of `light` as the *default theme*** in this case.\n```css\n.bg-teal-500 {\n    background-color: #38B2AC\n}\n\n/* Different! */\n.light\\:bg-teal-500 {\n    background-color: #38B2AC\n}\n\n@media (prefers-color-scheme: dark) {\n    .dark\\:bg-teal-500 {\n        background-color: #38B2AC\n    }\n}\n```\n\nWhich, in turn, changes the active theme table to:\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr align=\"center\"\u003e\n\u003cth align=\"left\"\u003eMatching media query\u003c/th\u003e\n\u003cth\u003eNeither\u003c/th\u003e\n\u003cth\u003e\u003ccode\u003eprefers-color-scheme: light\u003c/code\u003e\u003c/th\u003e\n\u003cth\u003e\u003ccode\u003eprefers-color-scheme: dark\u003c/code\u003e\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\n\u003ctbody\u003e\n\u003ctr align=\"center\"\u003e\n\u003cth align=\"left\"\u003eActive theme\u003c/th\u003e\n\u003ctd\u003e\u003ccode\u003elight\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003elight\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003edark\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n💡 Even though `background-color` has been used in every example so far, theme variants are available for *any* utility. \n\n### Selectors\n`fallback` also works for selector-activated themes.\n\n💡 If you control themes on your site by adding / removing classes or attributes on the `html` or `body` element with JavaScript, then visitors without JavaScript enabled would see the `fallback` theme!\n\n```js\nthemes: {\n    dark: {\n        selector: \".dark-theme\",\n    },\n    light: {\n        selector: \".light-theme\",\n    },\n},\nfallback: true, // Fall back to `dark`\n```\n\n**Fallback always chooses the first theme in your list of themes.** To choose a different theme, change the order of `themes`.\n\nThese options, with the same Tailwind config as before with `backgroundColor: [\"dark\", \"light\"]` (because that matches the order in `themes`) in `variants`, will generate:\n```css\n.bg-gray-900 {\n    background-color: #1A202C;\n}\n\n:root .dark\\:bg-gray-900 {\n    background-color: #1A202C;\n}\n\n:root.light-theme .light\\:bg-gray-900 {\n    background-color: #1A202C;\n}\n```\n\nWhich has the active theme table:\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth align=\"left\"\u003eMatching selector\u003c/th\u003e\n\u003cth align=\"left\"\u003eActive theme\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003cth align=\"left\"\u003eNeither\u003c/th\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003edark\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003cth align=\"left\"\u003e\u003ccode\u003e:root.light-theme\u003c/code\u003e\u003c/th\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003elight\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003cth align=\"left\"\u003e\u003ccode\u003e:root.dark-theme\u003c/code\u003e\u003c/th\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003edark\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n\n## Stacked variants\nYou can \"stack\" built-in or custom variants on top of the existing theme variants. We call it *stacking* because multiple variants are required: like in `night:focus:border-white`, the border will only be white if the `night` theme is active **and** the element is `:focus`ed on.\n\nHere's an example of combining [`prefers-contrast: high`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast) with the `:hover` variant:\n```js\nconst { themeVariants, prefersHighContrast } = require(\"tailwindcss-theme-variants\");\n\nmodule.exports = {\n    theme: {\n        // Your Tailwind CSS theme configuration\n    },\n    plugins: [\n        themeVariants({\n            themes: {\n                \"high-contrast\": {\n                    mediaQuery: prefersHighContrast /* \"@media (prefers-contrast: high)\" */,\n                },\n            },\n        }),\n    ],\n};\n```\n\nYou could create a simple card that uses contrast pleasant for fully sighted visitors, or functional high contrast for those who specify it:\n```html\n\u003cdiv class=\"bg-gray-100   high-contrast:bg-white\n            text-gray-800 high-contrast:text-black\"\u003e\n    \n    \u003ch1\u003eLet me tell you all about...\u003c/h1\u003e\n    \u003ch2\u003e... this great idea I have!\u003c/h2\u003e\n\n    \u003ca href=\"text-blue-500       high-contrast:text-blue-700\n             hover:text-blue-600 high-contrast:hover:text-blue-900\"\u003e\n\n        See more\n    \u003c/a\u003e\n\u003c/div\u003e\n```\n\nAnother—complex—example: suppose you want to zebra stripe your tables, matching the current theme, and change it on hover:\n\n```js\nconst { themeVariants } = require(\"tailwindcss-theme-variants\");\n\nmodule.exports = {\n    theme: {\n        // Your Tailwind CSS theme configuration\n    },\n\n    plugins: [\n        themeVariants({\n            baseSelector: \"table.themed\",\n            themes: {\n                \"no-accent\": { selector: \"\" },\n                \"green-accent\": { selector: \".themed-green\" },\n                \"orange-accent\": { selector: \".themed-orange\" },\n            },\n        }),\n    ],\n};\n```\n\nWe can then implement the themeable table in HTML (Svelte) like so:\n\n```html\n\u003ctable class=\"themed themed-green\"\u003e \u003c!-- Try changing themed-green to themed-orange or removing it --\u003e\n    {#each people as person}\n        \u003ctr class=\"no-accent:bg-white               green-accent:bg-green-50             orange-accent:bg-orange-50\n                   no-accent:hover:bg-gray-100      green-accent:hover:bg-green-100      orange-accent:hover:bg-orange-100\n                   no-accent:odd:bg-gray-100        green-accent:odd:bg-green-100        orange-accent:odd:bg-orange-100\n                   no-accent:odd:hover:bg-gray-200  green-accent:odd:hover:bg-green-200  orange-accent:odd:hover:bg-orange-100\n                  \"\u003e\n\n            \u003ctd\u003e{person.firstName} {person.lastName}\u003c/td\u003e\n            \u003ctd\u003e{person.responsibility}\u003c/td\u003e\n            \u003c!-- ... --\u003e\n        \u003c/tr\u003e\n    {/each}\n\u003c/table\u003e\n```\n\n\n### Responsive variants\nResponsive variants let you distinguish the current breakpoint per theme. For example, `lg:green-theme:border-green-200` will have a `green-200` border *only* when the breakpoint is `lg` (or larger) **and** `green-theme` is active.\n\n```js\nconst { themeVariants } = require(\"tailwindcss-theme-variants\");\n\nmodule.exports = {\n    theme: {\n        // Your Tailwind CSS theme configuration\n    },\n    \n    plugins: [\n        themeVariants({\n            themes: {\n                day: { selector: \"[data-time=day]\" },\n                night: { selector: \"[data-time=night]\" },\n            },\n        }),\n    ],\n};\n```\n\nWith this, we could make the landing page's title line change color at different screen sizes \"within\" each theme:\n```html\n\u003ch1 class=\"day:text-black          night:text-white\n           sm:day:text-orange-800  sm:night:text-yellow-100\n           lg:day:text-orange-600  lg:night:text-yellow-300\"\u003e\n    \n    The best thing that has ever happened. Ever.\n\u003c/h1\u003e\n```\n\n\nWe could also make a group of themes for data density, like you can [configure in GMail](https://www.solveyourtech.com/switch-compact-view-gmail/):\n\n```js\nconst { themeVariants } = require(\"tailwindcss-theme-variants\");\n\nmodule.exports = {\n    theme: {\n        // Your Tailwind CSS theme configuration\n    },\n\n    plugins: [\n        themeVariants({\n            // baseSelector is \":root\"\n            themes: {\n                comfortable: { selector: \"[data-density=comfortable]\" },\n                compact: { selector: \"[data-density=compact]\" },\n            },\n            // Fall back to the first theme listed (comfortable) when density is not configured\n            fallback: true,\n        }),\n    ],\n};\n```\n\nThis will allow us to configure the padding for each theme for each breakpoint, of a list of emails in the inbox (so original!):\n```html\n\u003cli class=\"comfortable:p-2     compact:p-0\n           md:comfortable:p-4  md:compact:p-1\n           xl:comfortable:p-6  xl:compact:p-2\"\u003e\n    \n    FWD: FWD: The real truth behind...\n\u003c/li\u003e\n```\n\n#### Extra stacked variants\nYou can still stack extra variants even while using responsive variants, but this is not commonly needed.\n\nHere's an example:\n```js\nconst { themeVariants, landscape, portrait } = require(\"tailwindcss-theme-variants\");\n\nmodule.exports = {\n    theme: {\n        // Your Tailwind CSS theme configuration\n    },\n\n    plugins: [\n        themeVariants({\n            themes: {\n                landscape: {\n                    mediaQuery: landscape,\n                },\n                portrait: {\n                    mediaQuery: portrait,\n                },\n            },\n            fallback: true,\n        }),\n    ],\n};\n```\n\nWe can make an `h1` change size based on orientation *and* breakpoint *and* hover for readability (this is definitely a contrived example):\n\n```html\n\u003ch1 class=\"landscape:text-base          portrait:text-xs\n           sm:landscape:text-lg         sm:portrait:text-sm\n           sm:landscape:hover:text-xl   sm:portrait:hover:text-md\n           lg:landscape:text-2xl        lg:portrait:text-lg\n           lg:landscape:hover:text-3xl  lg:portrait:hover:text-xl\"\u003e\n    \n    This article title will try to change size so that it stays readable... hopefully.\n\u003c/h1\u003e\n```\n\nMore realistically, you might just want to change a link color on hover depending on the breakpoint and theme.\n\n## Using both selectors and media queries\n⚠️ If you use both selectors and media queries to activate themes, then **make sure that each specified class is specified as an *all or nothing* approach**. For instance, if you have `winter` and `summer` themes and want to add the `winter:bg-teal-100` class, then you also need to add the `summer:bg-orange-200` class. If you don't do this, then it will look like the values from an theme that's *supposed* to be inactive are \"leaking\" into the active theme.\n\n**Every feature previously discussed will still work as you'd expect**, even when you decide to also add selectors or media queries to theme control. When both selectors and media queries are in use, **selectors will always take priority over media queries**. This allows the flexibility of *defaulting* to media queries and *overriding* with JavaScript!\n\nFor example, see this plugin call:\n```js\n// Rest of the Tailwind CSS config and imports...\nplugins: [\n    themeVariants({\n        themes: {\n            cyan: {\n                selector: \".day\",\n                mediaQuery: prefersLight,\n            },\n            navy: {\n                selector: \".night\",\n                mediaQuery: prefersDark,\n            },\n        },\n    }),\n],\n```\n\nIt has the corresponding active theme table:\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth align=\"left\"\u003eMatch\u003c/th\u003e\n\u003cth align=\"left\"\u003eNeither\u003c/th\u003e\n\u003cth align=\"left\"\u003e\u003ccode\u003eprefers-color-scheme: light\u003c/code\u003e\u003c/th\u003e\n\u003cth align=\"left\"\u003e\u003ccode\u003eprefers-color-scheme: dark\u003c/code\u003e\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003cth align=\"left\"\u003eNeither\u003c/th\u003e\n\u003ctd align=\"center\"\u003eNone\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003ecyan\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003enavy\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003cth align=\"left\"\u003e\u003ccode\u003e:root.day\u003c/code\u003e\u003c/th\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003ecyan\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003ecyan\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003ecyan\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003cth align=\"left\"\u003e\u003ccode\u003e:root.night\u003c/code\u003e\u003c/th\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003enavy\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003enavy\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003enavy\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\nAs 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.\n\n⚠️ If you are stacking variants on while using both selectors and media queries to activate themes, then **make sure that each stacked variant is specified as an *all or nothing* approach** on each element. For instance, if you have `normal-motion` and `reduced-motion` themes and want to add the `reduced-motion:hover:transition-none` class, then you also need to add the `normal-motion:hover:transition` class (or any [value of `transitionProperty`](https://tailwindcss.com/docs/transition-property/)). If you don't do this, then it will look like the values from a theme that's *supposed* to be inactive are \"leaking\" into the active theme.\n\n### Fallback\nLike when just selectors or just media queries are used for theme selection, the fallback feature for both media queries and selectors serves to \"force\" a theme match for the `None` / both `Neither` case in the active theme table.\n\nHere's an example:\n\n```js\n// Rest of the Tailwind CSS config and imports...\nplugins: [\n    themeVariants({\n        baseSelector: \"html\",\n        themes: {\n            \"not-inverted\": {\n                selector: \"[data-colors=normal]\",\n                mediaQuery: colorsNotInverted /* @media (inverted-colors: none) */,\n            },\n            \"inverted\": {\n                selector: \"[data-colors=invert]\",\n                mediaQuery: colorsInverted /* @media (inverted-colors: inverted) */,\n            },\n        },\n        // Since `inverted-colors` has limited browser support, \n        // assume visitors using unsupported browsers do not have their colors inverted\n        // and fall back to the \"not-inverted\" theme\n        fallback: true,\n        // 💡 Since selectors are being used too, we could even provide \n        // a button on the site that will manually enable/disable inverted colors\n    }),\n],\n```\n\nIt has the corresponding active theme table:\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth align=\"left\"\u003eMatch\u003c/th\u003e\n\u003cth align=\"left\"\u003eNeither\u003c/th\u003e\n\u003cth align=\"left\"\u003e\u003ccode\u003einverted-colors: none\u003c/code\u003e\u003c/th\u003e\n\u003cth align=\"left\"\u003e\u003ccode\u003einverted-colors: inverted\u003c/code\u003e\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003cth align=\"left\"\u003eNeither\u003c/th\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003enot-inverted\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003enot-inverted\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003einverted\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003cth align=\"left\"\u003e\u003ccode\u003ehtml[data-colors=normal]\u003c/code\u003e\u003c/th\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003enot-inverted\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003enot-inverted\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003enot-inverted\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003cth align=\"left\"\u003e\u003ccode\u003ehtml[data-colors=invert]\u003c/code\u003e\u003c/th\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003einverted\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003einverted\u003c/code\u003e\u003c/td\u003e\n\u003ctd align=\"center\"\u003e\u003ccode\u003einverted\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n## Call the plugin more than once to separate unrelated themes\nThe list of themes passed to one call of this plugin are intended to be *mutually exclusive*. So, if you have unrelated themes, like a set for motion, and another for light/dark, it doesn't make sense to stuff them all into the same plugin call. Instead, spread them out into two configs to be controlled independently:\n```js\n// Rest of the Tailwind CSS config and imports...\nplugins: [\n    themeVariants({\n        baseSelector: \"html\",\n        themes: {\n            light: { selector: \"[data-theme=light]\" },\n            dark: { selector: \"[data-theme=dark]\" },\n        },\n    }),\n\n    themeVariants({\n        themes: {\n            \"motion\": { mediaQuery: prefersAnyMotion },\n            \"no-motion\": { mediaQuery: prefersReducedMotion },\n        },\n        fallback: true,\n    }),\n]\n```\n\n## The ultimate example: how I use every feature together\nBecause I primarily made this plugin to solve my own problems (a shocking reason, I know!), I take advantage of every feature this plugin provides. Here's an excerpt of the Tailwind CSS config I use on my site:\n\n```js\nconst { themeVariants, prefersDark, prefersLight } = require(\"tailwindcss-theme-variants\");\n\nmodule.exports = {\n    theme: { \n        // ...\n    },\n\n    plugins: [\n        themeVariants({\n            baseSelector: \"html\",\n            fallback: true,\n            themes: {\n                \"light-theme\": { selector: \"[data-theme=light]\", mediaQuery: prefersLight },\n                \"dark-theme\": { selector: \"[data-theme=dark]\", mediaQuery: prefersDark },\n            },\n        }),\n    ]\n}\n```\n\n## Usage with the Tailwind CSS Typography plugin\nTo use theme variants with the official [Tailwind CSS Typography](https://github.com/tailwindlabs/tailwindcss-typography) plugin, create `prose` modifiers for each theme and use them in the HTML.\n\nHere's an example of changing the prose colors with themes. This covers all of the color settings in the [default typography styles](https://github.com/tailwindlabs/tailwindcss-typography/blob/master/src/styles.js):\n\n```js\nconst typography = require(\"@tailwindcss/typography\");\nconst { themeVariants } = require(\"tailwindcss-theme-variants\");\n\nmodule.exports = {\n    theme: {\n        extend: {\n            typography: (theme) =\u003e ({\n                light: {\n                    css: {\n                        color: theme(\"colors.gray.700\"),\n\n                        \"a\": {\n                            color: theme(\"colors.blue.700\"),\n                        },\n\n                        \"strong\": {   \n                            color: theme(\"colors.gray.900\"),\n                        },\n\n                        \"ol \u003e li::before\": {\n                            color: theme(\"colors.gray.600\"),\n                        },\n                        \"ul \u003e li::before\": {\n                            backgroundColor: theme(\"colors.gray.400\"),\n                        },\n                        \n                        \"hr\": {\n                            borderColor: theme(\"colors.gray.300\"),\n                        },\n\n                        \"blockquote\": {\n                            color: theme(\"colors.gray.900\"),\n                            borderLeftColor: theme(\"colors.gray.300\"),\n                        },\n\n                        \"h1\": {\n                            color: theme(\"colors.gray.900\"),\n                        },\n                        \"h2\": {\n                            color: theme(\"colors.gray.900\"),\n                        },\n                        \"h3\": {\n                            color: theme(\"colors.gray.900\"),\n                        },\n                        \"h4\": {\n                            color: theme(\"colors.gray.900\"),\n                        },\n\n                        \"figure figcaption\": {\n                            color: theme(\"colors.gray.600\"),\n                        },\n\n                        \"code\": {\n                            color: theme(\"colors.gray.900\"),\n                        },\n                        \"pre\": {\n                            color: theme(\"colors.gray.900\"),\n                            backgroundColor: theme(\"colors.gray.100\"),\n                        },\n                        \n                        \"thead\": {\n                            color: theme(\"colors.gray.900\"),\n                            borderBottomColor: theme(\"colors.gray.400\"),\n                        },\n                        \"tbody tr\": {\n                            borderBottomColor: theme(\"colors.gray.300\"),\n                        },\n                    },\n                },\n\n                dark: {\n                    css: {\n                        // These colors were chosen with gray-900 presumed \n                        // to be the page's background color\n                        color: theme(\"colors.gray.200\"),\n\n                        \"a\": {\n                            color: theme(\"colors.blue.400\"),\n                        },\n\n                        \"strong\": {   \n                            color: theme(\"colors.white\"),\n                        },\n\n                        \"ol \u003e li::before\": {\n                            color: theme(\"colors.gray.300\"),\n                        },\n                        \"ul \u003e li::before\": {\n                            backgroundColor: theme(\"colors.gray.500\"),\n                        },\n                        \n                        \"hr\": {\n                            borderColor: theme(\"colors.gray.600\"),\n                        },\n\n                        \"blockquote\": {\n                            color: theme(\"colors.white\"),\n                            borderLeftColor: theme(\"colors.gray.600\"),\n                        },\n\n                        \"h1\": {\n                            color: theme(\"colors.white\"),\n                        },\n                        \"h2\": {\n                            color: theme(\"colors.white\"),\n                        },\n                        \"h3\": {\n                            color: theme(\"colors.white\"),\n                        },\n                        \"h4\": {\n                            color: theme(\"colors.white\"),\n                        },\n\n                        \"figure figcaption\": {\n                            color: theme(\"colors.gray.300\"),\n                        },\n\n                        \"code\": {\n                            color: theme(\"colors.white\"),\n                        },\n                        \"pre\": {\n                            color: theme(\"colors.white\"),\n                            backgroundColor: theme(\"colors.gray.800\"),\n                        },\n                        \n                        \"thead\": {\n                            color: theme(\"colors.white\"),\n                            borderBottomColor: theme(\"colors.gray.600\"),\n                        },\n                        \"tbody tr\": {\n                            borderBottomColor: theme(\"colors.gray.600\"),\n                        },\n                    },\n                },\n            }),\n        },\n    },\n\n    plugins: [\n        typography,\n\n        themeVariants({\n            themes: {\n                \"light-theme\": { ... },\n                \"dark-theme\": { ... },\n            },\n            fallback: true,\n        }),\n    ],\n};\n```\n\nThanks to @stefanzweifel's [article on the subject](https://stefanzweifel.io/posts/2020/07/20/add-dark-mode-support-to-at-tailwindcsstypography/) and @pspeter3's [issue](https://github.com/tailwindlabs/tailwindcss-typography/issues/69)!\n\nNow that we have appropriate variants for `prose`, let's upgrade our HTML to use them:\n\n```html\n\u003cbody class=\"light-theme:bg-white dark-theme:bg-gray-900\"\u003e\n    \u003carticle class=\"prose light-theme:prose-light dark-theme:prose-dark\"\u003e\n        \u003cp\u003e\n            Content...\n        \u003c/p\u003e\n    \u003c/article\u003e\n\u003c/body\u003e\n```\n\nWe will revisit this example in the Semantics section below once I've written that out 😁. Until then, you can reference [this plugin's documentation site's configuration](https://github.com/JakeNavith/tailwindcss-theme-variants/blob/main/site/prose-styles.js) as an extremely rough guide.\n\n\n# Semantics\nSemantics are an **experimental feature** for this plugin that serve as a better approach to [custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/--*). If you're on Tailwind CSS 1.7 to 1.9, this means they still work on IE11!\n\nTODO. Semantic classes bundle up your design system with this plugin's generated variants. Because I (the plugin author 👋) have to write them, only certain utilities are supported so far:\n* `backgroundColor`\n* `borderColor`\n* `boxShadow`\n* `divideColor`\n* `gradientColorStops`\n* `textColor`\n\nBut, when you use the variables feature, you can use *any* utility as long as you can reference `var(--semantic-name)`.\n\n\n⚠️ They support variants provided by Tailwind's core and by other variant-registering plugins, but ***not* variants created by this plugin!** \n\n## Constants\nTODO. Constants are the easiest way to get started with semantics. They're called \"constant\" but actually change with each theme; they're just declared \"up front\" in the `tailwindcss-theme-variants` plugin call / configuration. \nTODO. Constants are declared by specifying a value from your `theme` configuration for each configurable utility in the `semantics` option for each theme in `themes`, like so:\n\n```js\nthemeVariants({\n    themes: {\n        light: {\n            mediaQuery: prefersLight,\n            semantics: {\n                colors: {\n                    \"body\": \"white\",\n                    // Use Tailwind CSS's default palette's 800 shade of gray\n                    // (unless you overrode it in your regular Tailwind CSS theme config)\n                    \"on-body\": \"gray.800\",\n                },\n            },\n        },\n        dark: {\n            mediaQuery: prefersDark,\n            semantics: {\n                colors: {\n                    \"body\": \"gray.900\",\n                    \"on-body\": \"gray.100\",\n                },\n            },\n        },\n    }\n}),\n```\nNow you have classes like `bg-body` and `text-on-body` that represent `light:bg-white dark:bg-gray-900` and `light:text-gray-800 dark:text-gray-100` respectively at your disposal! Because you can now write semantically named classes, this feature is called *`semantics`*.\n\n### Examples\nTODO\n\n## Variables\nTODO. Variables are an optional extension on top of constants. If you specify `target: \"ie11\"` in your **Tailwind** config, then they will be excluded, reducing the generated CSS size.\n\n⚠️ Don't give the same semantic name to multiple utilities in `semantics`; when using variables, they'll collide because they share a global \"namespace\". TODO: make this not the case.\n\nTODO. Every semantic name also has a corresponding variable. Each variable defaults to the active theme's constant declared in its `semantics` configuration. Variables are automatically used by the semantic utility classes, so you don't have to do anything special to make them work.\n\nFor that reason, you can also assign values to semantic variables with the typical custom property syntax\n```css\n--semantic-variable: 0, 128, 255;\n```\n\nTo maintain compatibility with the `text-opacity`, `bg-opacity`, etc, utilities, write semantic colors as `r, g, b`.\n\n### Examples\nTODO\n\n## Custom semantic utilities\n\nTODO. Just like you can write custom stacked variants, you can write custom semantic utilities. Pass `utilities`, an object of named utilities to `SemanticUtility` interface-compatible objects.\n\n\n\n# Alternatives\nBoth because there are many theme plugins for Tailwind CSS, and because *what's the right way to do theming?* is a frequently asked question, we've compiled this table listing every theme plugin to compare their features and ultimately answer that question.\n\nThis table is complicated, so a text summary is also available in [tailwindcss-theming's Alternatives section](https://github.com/innocenzi/tailwindcss-theming#alternatives).\n\n\u003ctable\u003e\n    \u003cthead\u003e\n        \u003ctr\u003e\n            \u003cth\u003e\u003c/th\u003e\n            \u003cth\u003e\u003ca href=\"https://tailwindcss.com/docs/dark-mode\"\u003eBuilt-in dark mode\u003c/a\u003e\u003c/th\u003e\n            \u003cth\u003e\u003ca href=\"https://github.com/benface/tailwindcss-alt\"\u003etailwindcss-alt\u003c/a\u003e\u003c/th\u003e\n            \u003cth\u003e\u003ca href=\"https://github.com/ChanceArthur/tailwindcss-dark-mode\"\u003etailwindcss-dark-mode\u003c/a\u003e\u003c/th\u003e\n            \u003cth\u003e\u003ca href=\"https://github.com/danestves/tailwindcss-darkmode\"\u003etailwindcss-darkmode\u003c/a\u003e\u003c/th\u003e\n            \u003cth\u003e\u003ca href=\"https://github.com/estevanmaito/tailwindcss-multi-theme\"\u003etailwindcss-multi-theme\u003c/a\u003e\u003c/th\u003e\n            \u003cth\u003e\u003ca href=\"https://github.com/javifm86/tailwindcss-prefers-dark-mode\"\u003etailwindcss-prefers-dark-mode\u003c/a\u003e\u003c/th\u003e\n            \u003cth\u003e\u003ca href=\"https://github.com/crswll/tailwindcss-theme-swapper\"\u003etailwindcss-theme-swapper\u003c/a\u003e\u003c/th\u003e\n            \u003cth\u003e\u003ca href=\"https://github.com/JakeNavith/tailwindcss-theme-variants\"\u003etailwindcss-theme-variants\u003c/a\u003e\u003c/th\u003e\n            \u003cth\u003e\u003ca href=\"https://github.com/innocenzi/tailwindcss-theming\"\u003etailwindcss-theming\u003c/a\u003e\u003c/th\u003e\n        \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n        \u003ctr\u003e\n            \u003cth\u003eControllable with selectors (classes or data attributes)\u003c/th\u003e\n            \u003ctd\u003e🟡\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e🟡\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003cth\u003eResponsive\u003c/th\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003cth\u003eSupports \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme\"\u003e\u003ccode\u003eprefers-color-scheme: dark\u003c/code\u003e\u003c/a\u003e\u003c/th\u003e\n            \u003ctd\u003e🟡\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e🟡\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003cth\u003eSupports \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme\"\u003e\u003ccode\u003eprefers-color-scheme: light\u003c/code\u003e\u003c/a\u003e\u003c/th\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003cth\u003eSupports other media queries like \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-transparency\"\u003e\u003ccode\u003eprefers-reduced-transparency\u003c/code\u003e\u003c/a\u003e\u003c/th\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e✅\u003c/td\u003e\n            \u003ctd\u003e❌\u003c/td\u003e\n        \u003c/tr\u003e\n    \u003c/tbody\u003e\n\u003c/table\u003e\n\n## Legend\n**Responsive**: While \"inside\" of a theme, it must be possible to \"activate\" classes depending on the current breakpoint. For instance, it has to be possible to change `background-color` when **both** the screen is `sm` **and** the current theme is `dark`.\n\n**Supports `prefers-color-scheme` or other media queries**: Because [any media query can be detected in JavaScript](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia), any plugin marked as not supporting [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) could \"support\" it by adding or removing classes or data attributes, like the [`prefers-dark.js` script](https://github.com/ChanceArthur/tailwindcss-dark-mode/blob/master/prefers-dark.js) does. This approach still comes with the caveats that\n1. JavaScriptless visitors will not have the site's theme reflect their preferred one\n2. It could still be possible for a flash of unthemed content to appear before the appropriate theme is activated (unless you block rendering by executing the script immediately in `head`)\n3. Your site will immediately jump between light and dark instead of smoothly transitioning with the rest of the screen on macOS\n\n**[tailwindcss-prefers-dark-mode](https://github.com/javifm86/tailwindcss-prefers-dark-mode)** and **[built-in dark mode](https://tailwindcss.com/docs/dark-mode)**: cannot use selectors and media queries at the same time; it's one or the other, so you have to put a ✅ in one row and ❌ in the other.\n\n\n# 📄 License and Contributing\n\nMIT licensed. There are no contributing guidelines. Just do whatever you want to point out an issue or feature request and I'll work with it.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjnavith%2Ftailwindcss-theme-variants","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjnavith%2Ftailwindcss-theme-variants","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjnavith%2Ftailwindcss-theme-variants/lists"}