Tailwind CSS plugin for client-side theming using CSS variables, with dark mode support

  • By Enzo Innocenzi
  • Last update: Jan 2, 2023
  • Comments: 14

Tailwind CSS Theming GitHub release NPM release Top Language

Note: Even though it should support Tailwind 2, I didn't thoroughly tested it. I plan to do it when I have time, and probably rewrite a bunch of stuff in order to improve quality of life and enable usage with Tailwind Play. The next version will probably be the actual v3, with breaking changes from the current beta. Knowing that, and although the plugin is perfectly fine, you may want to check out the alternatives. Thanks!

Table of contents

Introduction

Note - This plugin works with Tailwind CSS v1.2 upwards.

tailwindcss-theming is a Tailwind CSS plugin made to solve the common need to have multiple themes in an application. It is also perfect for making dark themes.

It uses CSS Custom Properties in order to make your themes interchangeable on the client-side. Swapping themes is as simple as changing a class of your body element. See an example in CodeSandbox.

Moreoever, this plugin has full support for the prefers-color-scheme media query, so you can define a theme that will automatically be picked based on browser preferences.

Get started:

$ yarn add [email protected] --dev

Compatibility

This plugin is based on CSS Custom Properties, which are not compatible with IE11. You can have partial support for the browsers that do not support them by using a PostCSS plugin that add a fallback for CSS variables, such as postcss-css-variables or postcss-custom-properties.

Keep in mind that only your default theme will work with that method.

Alternatives

This plugin is feature-complete, but some alternatives exist. If you're looking for a simpler approach, a different kind of configuration, or just want to know the alternatives, here is a list that you may find useful:

A more complete comparison of the different theming plugins can be found here.

Github

https://github.com/innocenzi/tailwindcss-theming

Comments(14)

  • 1

    [Feature request] Tailwind 1.4.0 support - new color opacity utilities

    After TailwindCSS introduced new color opacity utilities current approach of opacity and colors generation became not inlined with the official way colors are generated in tailwindCSS. As a result classes such as bg-opacity-25, text-opacity-25 makes no difference to the opacity level.

    @hawezo I actually have mixed feelings about 1.4.0 color utilities and I like how you implemented it - more, but what can we do here? I guess when using tailwind-theming we should still have an access to full list of tailwind features. But what is really great about your approach with colors is that it's fully polyfillable for IE11 and 1.4.0 colors - are not. So maybe let's discuss what is the best thing that should be done here

  • 2

    Tailwind 2 compatibility?

    First of all thanks for the amazing work on this plugin, it really came at the right time for our project!

    I'm currently trying to upgrade from tailwind 1.9.6 tailwind 2.0.2. After completing the upgrade guide I noticed the themes were not loading correctly. Looking at the generated css it seems all the color class names are being suffixed with -default now. For example the color named on-background will generate text-on-background-default.

    This issue is happening in 2.4.3 and v3.0.0-beta.2.

    Any ideas on how we complete the upgrade?

  • 3

    Create a theme at runtime

    Hi, thanks for your work! Is it possible to create a theme at runtime ? For example, based on the user settings. Or what would be a recommended approach to do so ?

  • 4

    Fix strategy precedence when applied to `html`

    Currently, the assignable option makes the themes assignable (the manner depends on the choosen strategy), but the :root theme takes precedence if the strategy is applied to html.

    For instance, <html data-theme="dark"> won't work because :root will take precedence.

    I didn't realize this before because I was used to apply the strategies on body.

    • The "workaround" is to not use the strategy on html, but on anything else.
    • The fix is to modify the assignable themes' selectors.
  • 5

    theme.config.js file not found

    Hi,

    first thanks for this awesome plugin really like the theme manager approach!

    I've some problems to load the custom theme.config.js file.

    image

    The documentation is partly misleading but I tried all variations:

    • tailwind.config.js ✅
          require('tailwindcss-theming')({
    preset: 'nord'
    })
    

          require('tailwindcss-theming'),
    

    ❌ documented in #18

    module.exports = {
      theme: {},
      variants: {},
      plugins: [
        require('./theme.config.js')
      ],
    };
    
    • theme.config.js
    
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const { ThemeManager, Theme } = require('tailwindcss-theming/api');
    
    
    const light = new Theme()
      .setName('light')
      .targetable()
      .addColors({
        sun: '#717171',
        moon: '#717171',
        sparkles: '#717171',
        background: '#f1f1f1',
        'on-background': 'black',
      });
    
    const dark = new Theme()
      .setName('dark')
      .targetable()
      .addColors({
        sun: '#ffd94d',
        moon: '#909090',
        sparkles: '#9e9ed6',
        background: '#212121',
        'on-background': 'white',
        });
    
    module.exports = new ThemeManager()
      .setDefaultTheme(light)
      .setDefaultDarkTheme(dark);
    
  • 6

    When I required theme.config.js IN tailwind.config.js, then failed to compile.

    Failed to compile.
    
    ./app/javascript/stylesheets/application.scss (./node_modules/css-loader/dist/cjs.js??ref--7-1!./node_modules/postcss-loader/src??ref--7-2!./node_modules/sass-loader/dist/cjs.js??ref--7-3!./app/javascript/stylesheets/application.scss)
    Module build failed (from ./node_modules/postcss-loader/src/index.js):
    SyntaxError
    
    (2:2) `@apply` cannot be used with `.text-gray-600` because `.text-gray-600` either cannot be found, or its actual definition includes a pseudo-selector like :hover, :active, etc. If you're sure that `.text-gray-600` exists, make sure that any `@import` statements are being properly processed *before* Tailwind CSS sees your CSS, as `@apply` can only be used for classes in the same CSS tree.
    
      1 | body {
    > 2 |   @apply font-sans text-sm leading-normal text-gray-600;
        |  ^
      3 | }
      4 | 
    

    Everything is JUST the same as "https://github.com/hawezo/tailwindcss-theming/blob/master/docs/setup.md", But it just failed.

    So how can I get it work.

  • 7

    Variants Clashing

    Awesome library - thanks!

    I'm needing to define multiple colorVariants on core colors like in the example below. As can be seen they are a light and dark variants of core colors accent and primary. These are not to be confused with a light and dark themes - they are unrelated - they are variants on the core theme colors of each theme.

    When I build this I get an error Error: Variant light already exists.

    I would have expected these to not clash since they are scoped uniquely. I have had a look at the source and notice that class ColorVariant does not consider the scope as part of it's uniqueness, hence the clashes.

    As a workaround for now I will move the variant colors into the colors(...) definitions.

    const lightTheme = new Theme()
      .name('light')
      .default()
      .assignable()
      .colors({
        'on-accent': '#FFFFFF',
        'accent': '#CB116E',
    
        'on-primary': '#292929',
        'primary': '#FAFAFA',
      })
      .colorVariant('light', '#FF569C', 'accent')
      .colorVariant('dark', '#940043', 'accent')
      .colorVariant('light', '#FFFFFF', 'primary')
      .colorVariant('dark', '#C7C7C7', 'primary');
    
  • 8

    There is no license file that includes the copyright and permission notice

    I know that this plug is MIT license, but I could't find the license file. Would you add the file? Or would you tell me how to use this plug considering license aspect.

  • 9

    Compatibility with TailwindUI?

    Hello, We're really excited to see what tailwindcss-theming can do. We spent most the day trying to get it to work along side tailwindui. It appears the plugins conflict somehow. Whichever plugin is listed first, writes the css - whichever follows, doesn't write anything after the :root stuff.

    In tailwind.config.js, we have this in 'plugins' plugins: [ require('@tailwindcss/ui'), require('tailwindcss-theming') ]

    After running npm run build - none of the CSS Custom Properties are written (for things like .addColors).
    If we change the order of the plugins, then it works, but the tailwindui colors aren't written.

    Will you help? (please and THANKYOU)

  • 10

    Multi-word, kebab-cased font variables get camel-cased

    Thanks for creating the plugin!

    So in the v3.0.0-beta.0 release (I haven't tried and don't plan on testing this in any older version of the plugin), when I try to use a multi-word, kebab-cased font variable name in my theme.config.js, it gets outputted in Tailwind as a camel-cased variable name after the font- prefix.

    E.g.: .setVariable('sans-serif', ['Inter', 'Segoe UI', 'Roboto'], 'fontFamily') gets exported as font-sansSerif, when my original intent is to create a font-sans-serif class.

    Would you be able to confirm if this is intended behavior, and if there would be any considerations for supporting/preserving kebab-cased variable names?

  • 11

    how can I avoid having all the default colours replaced?

    Hi there, I'm giving your plugin and try and I would like to make gradual changes to my website so I can introduce a dark mode and possibly other themes.

    I noticed when I followed your instructions that the moment I try to setup your plugin, it completely gets rid of all default Tailwind colours such as bg-white and bg-gray-500 etc

    Even though I haven't even begun creating and using, and going through the code to replace colours by custom ones that can be themed. It would be nice if the default tailwind colours could co-exist with the custom ones. Is there something I can do to prevent all the default colours from being "replaced". Because right now they are not really "replaced" as you says in the docs.. they are completely removed and you are expected to replace them yourself which is a big job for a big website

  • 12

    build(deps): bump json5 from 2.1.3 to 2.2.3

    Bumps json5 from 2.1.3 to 2.2.3.

    Release notes

    Sourced from json5's releases.

    v2.2.3

    v2.2.2

    • Fix: Properties with the name __proto__ are added to objects and arrays. (#199) This also fixes a prototype pollution vulnerability reported by Jonathan Gregson! (#295).

    v2.2.1

    • Fix: Removed dependence on minimist to patch CVE-2021-44906. (#266)

    v2.2.0

    • New: Accurate and documented TypeScript declarations are now included. There is no need to install @types/json5. (#236, #244)
    Changelog

    Sourced from json5's changelog.

    v2.2.3 [code, diff]

    v2.2.2 [code, diff]

    • Fix: Properties with the name __proto__ are added to objects and arrays. (#199) This also fixes a prototype pollution vulnerability reported by Jonathan Gregson! (#295).

    v2.2.1 [code, diff]

    • Fix: Removed dependence on minimist to patch CVE-2021-44906. (#266)

    v2.2.0 [code, diff]

    • New: Accurate and documented TypeScript declarations are now included. There is no need to install @types/json5. (#236, #244)
    Commits
    • c3a7524 2.2.3
    • 94fd06d docs: update CHANGELOG for v2.2.3
    • 3b8cebf docs(security): use GitHub security advisories
    • f0fd9e1 docs: publish a security policy
    • 6a91a05 docs(template): bug -> bug report
    • 14f8cb1 2.2.2
    • 10cc7ca docs: update CHANGELOG for v2.2.2
    • 7774c10 fix: add proto to objects and arrays
    • edde30a Readme: slight tweak to intro
    • 97286f8 Improve example in readme
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

  • 13

    build(deps): bump qs from 6.5.2 to 6.5.3

    Bumps qs from 6.5.2 to 6.5.3.

    Changelog

    Sourced from qs's changelog.

    6.5.3

    • [Fix] parse: ignore __proto__ keys (#428)
    • [Fix] utils.merge: avoid a crash with a null target and a truthy non-array source
    • [Fix] correctly parse nested arrays
    • [Fix] stringify: fix a crash with strictNullHandling and a custom filter/serializeDate (#279)
    • [Fix] utils: merge: fix crash when source is a truthy primitive & no options are provided
    • [Fix] when parseArrays is false, properly handle keys ending in []
    • [Fix] fix for an impossible situation: when the formatter is called with a non-string value
    • [Fix] utils.merge: avoid a crash with a null target and an array source
    • [Refactor] utils: reduce observable [[Get]]s
    • [Refactor] use cached Array.isArray
    • [Refactor] stringify: Avoid arr = arr.concat(...), push to the existing instance (#269)
    • [Refactor] parse: only need to reassign the var once
    • [Robustness] stringify: avoid relying on a global undefined (#427)
    • [readme] remove travis badge; add github actions/codecov badges; update URLs
    • [Docs] Clean up license text so it’s properly detected as BSD-3-Clause
    • [Docs] Clarify the need for "arrayLimit" option
    • [meta] fix README.md (#399)
    • [meta] add FUNDING.yml
    • [actions] backport actions from main
    • [Tests] always use String(x) over x.toString()
    • [Tests] remove nonexistent tape option
    • [Dev Deps] backport from main
    Commits
    • 298bfa5 v6.5.3
    • ed0f5dc [Fix] parse: ignore __proto__ keys (#428)
    • 691e739 [Robustness] stringify: avoid relying on a global undefined (#427)
    • 1072d57 [readme] remove travis badge; add github actions/codecov badges; update URLs
    • 12ac1c4 [meta] fix README.md (#399)
    • 0338716 [actions] backport actions from main
    • 5639c20 Clean up license text so it’s properly detected as BSD-3-Clause
    • 51b8a0b add FUNDING.yml
    • 45f6759 [Fix] fix for an impossible situation: when the formatter is called with a no...
    • f814a7f [Dev Deps] backport from main
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

  • 14

    build(deps): bump decode-uri-component from 0.2.0 to 0.2.2

    Bumps decode-uri-component from 0.2.0 to 0.2.2.

    Release notes

    Sourced from decode-uri-component's releases.

    v0.2.2

    • Prevent overwriting previously decoded tokens 980e0bf

    https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.1...v0.2.2

    v0.2.1

    • Switch to GitHub workflows 76abc93
    • Fix issue where decode throws - fixes #6 746ca5d
    • Update license (#1) 486d7e2
    • Tidelift tasks a650457
    • Meta tweaks 66e1c28

    https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.1

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.