npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tw-colors

v3.3.1

Published

Tailwind plugin for switching color theme with just one className

Downloads

81,560

Readme

Welcome to 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

  • 📦 Zero javascript added to the bundle size, it's just some CSS!
  • 🚀 Fully customizable, tailor it to your needs
  • 🎯 Fine-grained theming with variants
  • 🤩 Nested themes for complex layouts
  • All color formats are supported, including HEX, RGB, HSL, and named colors
  • 💫 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',
-           }
         },
      },
      plugins: [
+        createThemes({
+           light: { 
+              'primary': 'steelblue',
+              'secondary': 'darkblue',
+              'brand': '#F3F3F3',
+           }
+        })
      ],
   };

Apply class='light' or data-theme='light' anywhere in your app.

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, you 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',
+           },
         })
      ],
   };

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>

Nested themes

With data-theme

Just nest the themes...

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

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

With class

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='light'>
         ...
      </div>

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

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)', // avoid this
      '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>  

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

   <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>

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 prefers-color-scheme

See the default theme option

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 }) => ({
   'my-light-theme': light({ 
      'primary': 'steelblue',
      'secondary': 'darkblue',
      'brand': '#F3F3F3',
   }),
   'my-dark-theme': dark({ 
      'primary': 'turquoise',
      'secondary': 'tomato',
      'brand': '#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-secondary, --twc-brand will be created.

tailwind.config.js

   module.exports = {
      // ...your tailwind config
      plugins: [
         createThemes({
            'my-light-theme': { 
               'primary': 'steelblue',
               'secondary': 'darkblue',
               'brand': '#F3F3F3',
            },
            'my-dark-theme': { 
               'primary': 'turquoise',
               'secondary': 'tomato',
               'brand': '#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
})

defaultTheme

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

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

Example

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

...or simply set to one theme

Example

createThemes({
   'light': { 
      'primary': 'steelblue',
   },
   'dark': { 
      'primary': 'turquoise',
   },
}, {
   defaultTheme: 'light' // 'light' | 'dark'
})

strict

default: false

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

Example

createThemes({
   'light': { 
      // invalid color
      'primary': 'redish',
   },
   'dark': { 
      'primary': 'turquoise',
   },
}, {
   // an error will be thrown
   strict: true
})

produceCssVariable

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-brand-z (instead of twc-brand)
createThemes({
   'light': { 
      'primary': 'steelblue',
      'secondary': 'darkblue',
      'brand': '#F3F3F3',
   },
   'dark': { 
      'primary': 'turquoise',
      'secondary': 'tomato',
      'brand': '#4A4A4A',
   },
}, {
   produceCssVariable: (colorName) => `--a-${colorName}-z`
})

produceThemeClass

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',
      'brand': '#F3F3F3',
   },
   'dark': { 
      'primary': 'turquoise',
      'secondary': 'tomato',
      'brand': '#4A4A4A',
   },
}, {
   produceThemeClass: (themeName) => `theme-${themeName}`
})
<html class='theme-dark'>
   ...
   <button class='theme-dark:rounded'>
      Click Me
   </button>
   ...
</html>

produceThemeVariant

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',
      'brand': '#F3F3F3',
   },
   'dark': { 
      'primary': 'turquoise',
      'secondary': 'tomato',
      'brand': '#4A4A4A',
   },
}, {
   produceThemeVariant: (themeName) => `theme-${themeName}`
})
<html data-theme='dark'>
   ...
   <button class='theme-dark:rounded'>
      Click Me
   </button>
   ...
</html>

Please share