Tailwindcss: Include configuration variables as :root properties

Created on 29 Nov 2017  路  16Comments  路  Source: tailwindlabs/tailwindcss

Similar to what BS4 has started to do, it would be nice if some or all of the configuration variables were available for use as root properties.

Example:

:root {
  --purple-darkest: #1f133f;
  --purple-lightest: #f3ebff;
}

.card {
background-color: var(--purple-darkest)
}

This feature is gaining more coverage and would be a great feature for 1.0

https://caniuse.com/#feat=css-variables
https://www.w3.org/TR/css-variables/

For me, I may want to override a root property after I've compiled my css. As an example I might do this to override colors using something like the WordPress customizer or some custom admin options in Laravel.

While I'd love to help with this js isn't my strength. I'd love to get some help or direction.

Most helpful comment

All 16 comments

You can already do this. Set the css :root custom properties in your css/scss (import file or even in your app.css that imports tailwind).

In the tailwind config JS, you just point to the css variable

"primary" : var(--primary)",

@ar-stackrox I do realize I can do this, however I'm more looking to expose all the variables set in the config.js automatically as properties to :root.

Similar to how the responsive helper works now.

To do this, you'd have to define every single value in your Tailwind config as a CSS custom property, like:

{
  // ...
  margin: {
    '0': 'var(--margin-0)',
    '1': 'var(--margin-1)',
    '2': 'var(--margin-2)',
    '3': 'var(--margin-3)', 
  },

  // ...
  shadows: {
    default: 'var(--shadow-default)',
    'md': 'var(--shadow-md)',
    'lg': 'var(--shadow-lg)',
    'inner': 'var(--shadow-inset)',
  }
}

...and then define all those values in your root selector. There's not really anything we can reasonably do with Tailwind to make this easier save for adding a dedicated option (like convertToCustomProperties: true), which I don't think we really want to do at the moment.

Maybe one day but no actual plans to implement it at this time, so I'd recommend just referencing your own custom properties in your config for now and maintaining that list in your stylesheet.

There's not really anything we can reasonably do with Tailwind to make this easier save for adding a dedicated option (like convertToCustomProperties: true), which I don't think we really want to do at the moment.

@adamwathan I鈥檓 hoping you鈥檒l consider doing something similar to that.

In Craft CMS, we want to add a few accessibility-oriented user display preferences, e.g. Dark Mode, High Contrast Mode, Inverted Colors, and Grayscale鈥攁nd maybe even custom color controls.

Tricky thing is that Craft has plugins that provide their own UI components, which often require some amount of custom styling. We can鈥檛 expect every plugin to start accounting for all these different possibilities, so instead we want to move the Control Panel over to Tailwind, and use utility classes for everything that relates to color.

Worst case we could just dynamically generate the actual utility CSS and include it in each page. But that would add a ton of page weight to each response, so not ideal.

If Tailwind had a checkCustomProperties setting, it could generate utility classes like this:

.bg-blue-500 {
  /* fallback for older browsers */
  background-color: #4299e1 !important;
  background-color: var(--blue-500, #4299e1) !important;
}

Then if someone changes their display preferences, all we have to do is set the :root CSS properties in the HTML, and everything on the page that鈥檚 using the utility classes will automatically get the updated color values.


EDIT: I just noticed the mention of custom properties on the Customizing Colors doc. That won鈥檛 work in browsers that don鈥檛 support CSS custom properties, which includes all versions of IE, Opera Mini, and others, but probably good enough for our use case anyway.

Currently, I am using CSS variables to define my colors across different projects that use the same theme. Is it possible to generate variations of those colors by the same pattern that tailwind uses?

For example:

primary: {
  default: 'var(--color-primary)',
  800: 'lightness(var(--color-primary), 80%)',
  ...
},

Currently, I am using CSS variables to define my colors across different projects that use the same theme. Is it possible to generate variations of those colors by the same pattern that tailwind uses?

For example:

primary: {
  default: 'var(--color-primary)',
  800: 'lightness(var(--color-primary), 80%)',
  ...
},

Yep, this is going to work.

but keep in mind to put Tailwind before postcss-preset-env in plugins list

Wonderful! But, oops, now I got lost. :(

I am using it with Sage, and tailwind is being configured inside postcss.config.js:

const cssnanoConfig = {
  preset: ['default', { discardComments: { removeAll: true } }]
};

module.exports = ({ file, options }) => {
  return {
    parser: options.enabled.optimize ? 'postcss-safe-parser' : undefined,
    plugins: {
      tailwindcss: `${options.paths.assets}/styles/tailwind.config.js`,
      autoprefixer: true,
      cssnano: options.enabled.optimize ? cssnanoConfig : false,
    },
  };
};

Is there a way to set what you said within my situation?

Wonderful! But, oops, now I got lost. :(

I am using it with Sage, and tailwind is being configured inside postcss.config.js:

const cssnanoConfig = {
  preset: ['default', { discardComments: { removeAll: true } }]
};

module.exports = ({ file, options }) => {
  return {
    parser: options.enabled.optimize ? 'postcss-safe-parser' : undefined,
    plugins: {
      tailwindcss: `${options.paths.assets}/styles/tailwind.config.js`,
      autoprefixer: true,
      cssnano: options.enabled.optimize ? cssnanoConfig : false,
    },
  };
};

Is there a way to set what you said within my situation?

no, should works also.

It's actually not working. I am using the class "bg-primary-800" to test it. What do you think I could be doing wrong?

It's actually not working. I am using the class "bg-primary-800" to test it. What do you think I could be doing wrong?

is bg-primary working ?

Yes, it is. I think the problem is that sass can't process a color that is not known before the runtime, isn't it?

Yes, it is. I think the problem is that sass can't process a color that is not known before the runtime, isn't it?

correct
I think you should define css variable for this like

--color-primary-800: lightness(var(--color-primary), 80%);

This will probably never work because CSS variables are handled at runtime by the browser, so until there's a browser version of the lightness function it's basically impossible for this to work. Better to just actually define all of your colors by hand, simply changing the lightness isn't a great way to build a color palette anyways, color is very complex and for things to actually look good you need to change saturation and hue as well.

For example, here is the blue color palette from Tailwind in HSL:

100: 'hsl(201, 100%, 96%)',
200: 'hsl(202, 81%, 86%)',
300: 'hsl(203, 82%, 76%)',
400: 'hsl(205, 79%, 66%)',
500: 'hsl(207, 73%, 57%)',
600: 'hsl(209, 62%, 50%)',
700: 'hsl(211, 61%, 43%)',
800: 'hsl(213, 49%, 34%)',
900: 'hsl(215, 41%, 28%)',

Notice how all three parameters change for each color to actually create a smooth scale, not just the lightness.

Yeah, you are right. It is better to create every CSS variable by hand. Thank you @adamwathan and @omarkhatibco.

I've implemented a variable fallback plugin for PostCSS. This met my needs, but wouldn't work with !important. However, you could update this to use a parser instead of a regex for more general usage.

const postcss = require('postcss');

module.exports = postcss.plugin('postcss-var-fallbacks', (options = {}) => {
  return (root) => {
    root.walkRules((rule) => {
      rule.walkDecls((decl) => {
        // Detect variable fallback values such as `var(--blue, #0000ff)`
        const match = decl.value.match(
          /^var\(\s*(--[A-Za-z][A-Za-z0-9-]*),\s*(.*)\)/
        );
        if (!match) {
          // Either no CSS variable, or no fallback value provided
          return;
        }
        // Insert the fallback by itself prior to the declaration using the
        // CSS variable; that way, older browsers will gracefully degrade to
        // an acceptable value.
        decl.parent.insertBefore(
          decl,
          new postcss.decl({ prop: decl.prop, value: match[2].trim() })
        );
      });
    });
  };
});

Was this page helpful?
0 / 5 - 0 ratings

Related issues

divdax picture divdax  路  3Comments

afuno picture afuno  路  3Comments

lamberttraccard picture lamberttraccard  路  3Comments

chasegiunta picture chasegiunta  路  3Comments

paulhuisman picture paulhuisman  路  3Comments