Search code examples
csstailwind-css

Opacity Issues with Dynamic Colors in TailwindCSS


TL;DR

text-primary-500/50 not working as expected.

Declare Dynamic Color

I have a color named primary. This color doesn't have any specific settings; instead, I assign existing colors based on a template. If the parent element has the class theme-red, the primary color should essentially match the red color. If the parent element has the class theme-blue, the primary color should match the blue color. By default, the primary color is red.

I have implemented this in the following example.

Example

tailwind.config = {
  theme: {
    extend: {
      colors: {
        primary: {
          500: 'var(--color-primary-500)',
        },
      },
    },
  },
}
<script src="https://cdn.tailwindcss.com/3.4.5"></script>
<style type="text/tailwindcss">
  @layer utilities {
    .theme-red {
      --color-primary-500: theme('colors.red.500');
    }

    .theme-blue {
      --color-primary-500: theme('colors.blue.500');
    }
  }
</style>

<div>
  <div class="text-green-500">
    Default Green Color
  </div>
  <div class="text-green-500/50">
    Default Green Color With Opacity
  </div>
</div>

<!-- Not working examples with dynamic primary color -->
<div class="theme-red">
  <div class="text-primary-500">
    Example Red Theme
  </div>
  <div class="text-primary-500/50">
    Example Red Theme With Opacity
  </div>
</div>

<div class="theme-blue">
  <div class="text-primary-500">
    Example Blue Theme
  </div>
  <div class="text-primary-500/50">
    Example Blue Theme With Opacity
  </div>
</div>

Result

opacity primary color not working

Opacity Dynamic Colors Not Working

The issue arises with transparent colors, such as text-primary-500/50. That is, it should be text-primary-500 with 0.5 opacity. It doesn't work because text-primary-500 is essentially a hex color, but in CSS, we expected an RGB color code, which we could then blend with opacity on a 0-1 scale. How can I make it work so that text-primary-500/50 functions similarly to my written logic?

Expected Behavior

I expected primary color to work the same way as the base colors, as shown with the green color in the previous result. Here’s an example of what I would like.

expected result


Solution

  • You could consider using color-mix() in your color definition. This allows the use of the Tailwind <alpha-value> token to affect the transparency of a CSS variable that is not in HSL or RGB component format:

    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: {
              500: 'color-mix(in srgb, var(--color-primary-500) calc(100% * <alpha-value>), transparent)',
            },
          },
        },
      },
    }
    <script src="https://cdn.tailwindcss.com/3.4.5"></script>
    <style type="text/tailwindcss">
      @layer utilities {
        .theme-red {
          --color-primary-500: theme('colors.red.500');
        }
    
        .theme-blue {
          --color-primary-500: theme('colors.blue.500');
        }
      }
    </style>
    
    <div class="theme-red">
      <div class="text-primary-500">
        Example Red Theme
      </div>
      <div class="text-primary-500/50">
        Example Red Theme With Opacity
      </div>
    </div>
    
    <div class="theme-blue">
      <div class="text-primary-500">
        Example Blue Theme
      </div>
      <div class="text-primary-500/50">
        Example Blue Theme With Opacity
      </div>
    </div>

    Alternatively, you could consider revising the CSS variable definition to be inside a Tailwind plugin. This allows the values to be manipulated to HSL or RGB component format. Then, the <alpha-value> token can be used more "natually" in a rgb() or hsl() CSS function:

    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: {
              500: 'rgb(var(--color-primary-500) / <alpha-value>)',
            },
          },
        },
      },
      plugins: [
        tailwind.plugin(({ addBase, theme }) => {
          const toRgb = (hex) => {
            const r = parseInt(hex.substring(1, 3), 16);
            const g = parseInt(hex.substring(3, 5), 16);
            const b = parseInt(hex.substring(5, 7), 16);
            return `${r} ${g} ${b}`;
          };
    
          addBase({
            '.theme-red': {
              '--color-primary-500': toRgb(theme('colors.red.500')),
            },
            '.theme-blue': {
              '--color-primary-500': toRgb(theme('colors.blue.500')),
            },
          });
        }),
      ],
    }
    <script src="https://cdn.tailwindcss.com/3.4.5"></script>
    
    <div class="theme-red">
      <div class="text-primary-500">
        Example Red Theme
      </div>
      <div class="text-primary-500/50">
        Example Red Theme With Opacity
      </div>
    </div>
    
    <div class="theme-blue">
      <div class="text-primary-500">
        Example Blue Theme
      </div>
      <div class="text-primary-500/50">
        Example Blue Theme With Opacity
      </div>
    </div>