Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

tw-colors

L-Blondy185.9kMIT3.3.2TypeScript support: included

Tailwind plugin for switching color theme with just one className

tailwind, tailwindcss, color, colors, theme, themes, dark

readme

tw-colors

Introducing the ultimate game-changer for your Tailwind app! Say goodbye to cluttered dark variants and messy CSS variables. With this tailwind plugin, switching between color themes is as effortless as changing one className.

Highlights

  • 🚀 Scalable, add as many themes and colors as you want. There is no limit on the number of themes and color you can use
  • 💫 Flexible, don't limit yourself to colors, with the built-in variants you can theme any css property
  • Easy to adopt, no need to change your markup, it just works!
  • 🤩 Nested themes are supported for complex use cases
  • 🎯 Full Tailwind CSS Intellisense support 🔥🔥🔥

Changelog

See the full changelog here

Usage

npm i -D tw-colors

Take an existing tailwind config and move the colors in the createThemes plugin, giving it a name (e.g. light).

tailwind.config.js

+  const { createThemes } = require('tw-colors');

   module.exports = {
      content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
      theme: {
         extends: {
-           colors: {
-              'primary': 'steelblue',
-              'secondary': 'darkblue',
-              'brand': '#F3F3F3',
-              // ...other colors
-           }
         },
      },
      plugins: [
+        createThemes({
+           light: { 
+              'primary': 'steelblue',
+              'secondary': 'darkblue',
+              'brand': '#F3F3F3',
+              // ...other colors
+           }
+        })
      ],
   };

💡 tip: you can use any color name as you usually do, not just the ones from the example. The same goes for the theme names.

Apply class='light' or data-theme='light' anywhere in your app (the html or the body tag is a good spot for it)

See the options to customize the className

-  <html>
+  <html class='light'>
      ...
      <div class='bg-brand'>
         <h1 class='text-primary'>...</h1>
         <p class='text-secondary'>...</p>
      </div>
      ...
   </html>

That's it, your site has a light theme!

Adding more themes

tailwind.config.js

   const { createThemes } = require('tw-colors');

   module.exports = {
      content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
      plugins: [
         createThemes({
            light: { 
               'primary': 'steelblue',
               'secondary': 'darkblue',
               'brand': '#F3F3F3',
            },
+           dark: { 
+              'primary': 'turquoise',
+              'secondary': 'tomato',
+              'brand': '#4A4A4A',
+           },
+           forest: { 
+              'primary': '#2A9D8F',
+              'secondary': '#E9C46A',
+              'brand': '#264653',
+           },
+           winter: { 
+              'primary': 'hsl(45 39% 69%)',
+              'secondary': 'rgb(120 210 50)',
+              'brand': 'hsl(54 93% 96%)',
+           },
         })
      ],
   };

You now have a light, a dark and a forest theme!

Switching themes

Simply switch the class or the data-theme attribute

-  <html class='light'>
+  <html class='dark'>
      ...
   </html>

Variants

Based on the current theme, specific styles can be applied using variants.

Note: In the following example the variants would have no effect with data-theme='light'

   <!-- Use "serif" font for the dark theme only -->
   <div data-theme='dark' class='font-sans dark:font-serif'>
      ...
      <div>Serif font here</div>

      <!-- this button is rounded when the theme is `dark` -->
      <button class='dark:rounded'>Click Me</button>
   </div>

See the options to customize the variant name

<summary> Caveat: group-{modifier} </summary> Always use the group variant before the theme variant.
      <html class='theme-dark'>
         ...
         <div class='group'>
            <div class='theme-dark:group-hover:bg-red-500'>
               ❌ the group variant does not work
            </div>
            <div class='group-hover:theme-dark:bg-red-500'>
               ✅ the group variant works properly
            </div>
         </div>    
      </html>

Nested themes

With <samp>data-theme</samp>

Just nest the themes...

   <html data-theme='dark'>
      ...
      <div data-theme='winter'>
         ...
      </div>

      <div data-theme='forest'>
         ...
      </div>
   </html>

With <samp>class</samp>

For variants to work properly in nested themes, an empty data-theme attribute must be added alongside the nested theme class

   <html class='dark'>
      ...
      <div data-theme class='winter'>
         ...
      </div>

      <div data-theme class='forest'>
         ...
      </div>
   </html>

Caveats:

<summary> Do not set opacity in the color definition </summary> When using nested themes, it is better not to provide a base opacity in your color definitions.

With this setup the 0.8 opacity defined on the primary color of the "parent" theme will be inherited by the "child" theme's primary color.

   createThemes({
      parent: { 
         'primary': 'hsl(50 50% 50% / 0.8)', // don't do this, the default opacity will propagate to the child theme
         'secondary': 'darkblue',
      },
      child: { 
         'primary': 'turquoise',
         'secondary': 'tomato',
      },
   })
   <html data-theme='parent'>

      <div data-theme='child'>
         <!-- The primary color has an unexpected 0.8 opacity -->
         <button class='bg-primary'>Click me</button>

        ...
      </div>
   </html>  
<summary> Inherited properties </summary>

Inherited properties (e.g. "font-family") are inherited by all descendants, including nested themes. In order to stop the propagation the base styles should be re-declared on nested themes

❌ Unexpected behavior

      <html class='theme-dark font-sans theme-dark:font-serif'>
         ...
         <div>
            ✅ Serif is active
         </div>

         <div class="theme-light">
            ❌ Serif is still active, it got inherited from the parent theme
         </div>     
      </html>

✅ Works as expected

      <html class='theme-dark font-sans theme-dark:font-serif'>
         ...
         <div>
            ✅ Serif is active
         </div>

         <div class="theme-light font-sans theme-dark:font-serif">
            ✅ Sans is active
         </div>   
      </html>

CSS color-scheme

createThemes also accepts a function that exposes the light and dark functions. \ To apply the color-scheme CSS property, simply wrap a theme with light or dark."

See the MDN docs for more details on this feature.

tailwind.config.js

createThemes(({ light, dark }) => ({
   'winter': light({ 
      'primary': 'steelblue',
      'base': 'darkblue',
      'surface': '#F3F3F3',
   }),
   'forest': dark({ 
      'primary': 'turquoise',
      'base': 'tomato',
      'surface': '#4A4A4A',
   }),
}))

CSS Variables

tw-colors creates CSS variables using the format --twc-[color name] by default, they contain HSL values.

For example, with the following configuration, the variables --twc-primary, --twc-base, --twc-surface will be created.

tailwind.config.js

   module.exports = {
      // ...your tailwind config
      plugins: [
         createThemes({
            'winter': { 
               'primary': 'steelblue',
               'base': 'darkblue',
               'surface': '#F3F3F3',
            },
            'forest': { 
               'primary': 'turquoise',
               'base': 'tomato',
               'surface': '#4A4A4A',
            },
         })
      ],
   };

Example usage:

.my-class {
   color: hsl(var(--twc-primary));
   background: hsl(var(--twc-primary) / 0.5);
}

See the options to customize the css variables

Options

The options can be passed as the second argument to the plugin

createThemes({
   // your colors config here...
}, {
   produceCssVariable: (colorName) => `--twc-${colorName}`,
   produceThemeClass: (themeName) => `theme-${themeName}`
   produceThemeVariant: (themeName) => `theme-${themeName}`
   defaultTheme: 'light'
   strict: false
})

<samp>defaultTheme</samp>

The default theme to use, think of it as a fallback theme when no theme is declared.

Example: simple default theme

createThemes({
   'winter': { 
      'primary': 'steelblue',
   },
   'halloween': { 
      'primary': 'crimson',
   },
}, {
   defaultTheme: 'winter' // 'winter' | 'halloween'
})

The default theme can also be chosen according to the user light or dark preference (see MDN prefers-color-scheme)

Example: adapting to user preference

createThemes({
   'winter': { 
      'primary': 'steelblue',
   },
   'halloween': { 
      'primary': 'crimson',
   },
}, {
   defaultTheme: {
      /**
       * when `@media (prefers-color-scheme: light)` is matched, 
       * the default theme is the "winter" theme 
       */
      light: 'winter', 
      /**
       * when `@media (prefers-color-scheme: dark)` is matched, 
       * the default theme is the "halloween" theme 
       */
      dark: 'halloween', 
   }
})

<samp>strict</samp>

default: false

If false invalid colors are ignored. \ If true invalid colors produce an error.

Example

createThemes({
   'christmas': { 
      // invalid color
      'primary': 'redish',
   },
   'darkula': { 
      'primary': 'crimson',
   },
}, {
   // an error will be thrown
   strict: true
})

<samp>produceCssVariable</samp>

default: (colorName) => `--twc-${colorName}`

Customize the css variables generated by the plugin.

With the below configuration, the following variables will be created:

  • --a-primary-z (instead of twc-primary)
  • --a-secondary-z (instead of twc-secondary)
  • --a-base-z (instead of twc-base)
createThemes({
   'light': { 
      'primary': 'steelblue',
      'secondary': 'darkblue',
      'base': '#F3F3F3',
   },
   'dark': { 
      'primary': 'turquoise',
      'secondary': 'tomato',
      'base': '#4A4A4A',
   },
}, {
   produceCssVariable: (colorName) => `--a-${colorName}-z`
})

<samp>produceThemeClass</samp>

default: (themeName) => themeName

Customize the classNames of the themes and variants

With the below configuration, the following theme classNames and variants will be created:

  • theme-light (instead of light)
  • theme-dark (instead of dark)
createThemes({
   'light': { 
      'primary': 'steelblue',
      'secondary': 'darkblue',
      'base': '#F3F3F3',
   },
   'dark': { 
      'primary': 'turquoise',
      'secondary': 'tomato',
      'base': '#4A4A4A',
   },
}, {
   produceThemeClass: (themeName) => `theme-${themeName}`
})
<html class='theme-dark'>
   ...
   <button class='theme-dark:rounded'>
      Click Me
   </button>
   ...
</html>

<samp>produceThemeVariant</samp>

default: same as produceThemeClass

Customize the variants

With the below configuration, the following variants will be created:

  • theme-light (instead of light)
  • theme-dark (instead of dark)
createThemes({
   'light': { 
      'primary': 'steelblue',
      'secondary': 'darkblue',
      'base': '#F3F3F3',
   },
   'dark': { 
      'primary': 'turquoise',
      'secondary': 'tomato',
      'base': '#4A4A4A',
   },
}, {
   produceThemeVariant: (themeName) => `theme-${themeName}`
})
<html data-theme='dark'>
   ...
   <button class='theme-dark:rounded'>
      Click Me
   </button>
   ...
</html>

From the same Author

up-fetch: Tiny 1kb configuration tool for the fetch API with sensible default

Please share

changelog

Changelog

3.3.0 - 2023-10-30

Added

  • Added support for prefers-color-scheme

3.2.0 - 2023-10-27

Fixed

  • [#30] - The colors defined via the plugin now properly override tailwind's default colors in case of name clash.

Breaking changes:

  • NativeWind 2 with Next.js is no longer supported

3.1.2 - 2023-10-23

Fixed

  • Fixed intellisense for the theme class

3.1.0 - 2023-10-23

Fixed

  • [#26] - NativeWind 2 with the Next.js setup is now supported.

3.0.3 - 2023-09-04

Added

  • Added support for group modifiers with theme variants

3.0.0 - 2023-09-04

Added

  • The variants can now be declared anywhere without having to redeclare the theme.
  • New option produceThemeVariant to customize the variant names. It will fallback to produceThemeClass if omitted

breaking changes:

  • Renamed the option getCssVariable to produceCssVariable.
  • Renamed the option getThemeClassName to produceThemeClass. The default return value is now the themeName instead of theme-${themeName}

2.2.0 - 2023-08-22

Added

  • strict option. If false (default) invalid colors are ignored, if true invalid colors throw an error

2.1.1 - 2023-08-21

Fixed

  • Fixed a typescript error when using a functional config and the defaultTheme option

2.1.0 - 2023-08-21

breaking changes:

  • resolveConfig was renamed to resolveTwcConfig
  • Only two types are exposed: TwcConfig and TwcOptions, corresponding to createThemes(TwcConfig, TwcOptions) and resolveTwcConfig(TwcConfig, TwcOptions). Previously exposed types are no longer valid.

Added

  • defaultTheme option

2.0.3 - 2023-08-06

breaking changes:

  • Dropped support for the cssVariablePrefix and cssVariableSuffix options. Check out the new getCssVariable Option

Added

  • getCssVariable option to customize the css variables.
  • getThemeClassName option to customize the generated theme classNames and variants.

1.2.6 - 2023-05-03

Fixed

  • [#11] - Removed the cross-var npm package

1.2.5 - 2023-04-26

Added

  • [#10] - added options to customize the CSS variables: cssVariablePrefix, cssVariableSuffix

1.2.4 - 2023-04-20

Fixed

  • [#9] - fixed missing support for '/' in color names

1.2.1 - 2023-04-13

Fixed

  • [#7] - fixed missing support for DEFAULT colors

1.2.0 - 2023-03-29

Added

  • support for esm

Fixed

  • [#6] - increased color conversion precision to 1 decimal