How to dynamically pass tailwind styles from props

85 Views Asked by At

I am sending a tailwind color green-500 from the prop bgColor and then use it inside the after:border-b-${bgColor} in the positionStyles object.

But tailwind is unable to dynamically parse after:border-b-${bgColor} as a tailwind class.

How to solve this issue? I am open to new suggestions or ways to rewrite my code.

What I want is to pass just a color like green-500 and then let tailwind render it as a class.

Following is my code:

const TOOLTIP_POSITIONS = {
  TOP: "TOP",
  RIGHT: "RIGHT",
  BOTTOM: "BOTTOM",
  LEFT: "LEFT",
};

const ToolTip = ({
  title,
  position = TOOLTIP_POSITIONS.BOTTOM,
  isOverflowX = false,
  noTooltip = false,
  zindex,
  bgColor = "green-500",
  keys = [],
  children,
}) => {

  const positionStyles = {
    [TOOLTIP_POSITIONS.BOTTOM]: `top-[100%] mt-2
    after:content-[""] 
    after:absolute
    after:top-[-5px]
    after:left-2/4
    after:-translate-x-2/4
    after:w-0
    after:h-0
    after:border-l-[6px] after:border-l-transparent
    after:border-b-[6px] after:border-b-${bgColor}
    after:border-r-[6px] after:border-r-transparent`,
  };

  return (
    <div
      className={`tooltip-wrapper group relative flex w-full items-center justify-center font-medium`}
    >
      {children}
      <div
        className={`tooltip absolute hidden w-max rounded-lg bg-gray950 p-2 text-center text-white group-hover:flex group-hover:items-center ${positionStyles[position]}`}
      >
        {title}
      </div>
    </div>
  );
};
2

There are 2 best solutions below

0
On

As per the documentation:

The most important implication of how Tailwind extracts class names is that it will only find classes that exist as complete unbroken strings in your source files.

If you use string interpolation or concatenate partial class names together, Tailwind will not find them and therefore will not generate the corresponding CSS:

Don’t construct class names dynamically

<div class="text-{{ error ? 'red' : 'green' }}-600"></div>

In the example above, the strings text-red-600 and text-green-600 do not exist, so Tailwind will not generate those classes. Instead, make sure any class names you’re using exist in full:

Always use complete class names

<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>

You could:

  • Have the entire class in the bgColor prop like

    const ToolTip = ({
      …
      bgColor = "after:border-b-green-500",
      …
    }) => {
      const positionStyles = {
        [TOOLTIP_POSITIONS.BOTTOM]: `
        …
        after:border-b-[6px] ${bgColor}
        …`,
      };
    
  • Have a dictionary for color to Tailwind class names:

    const ToolTip = ({
      …
      bgColor = "green-500",
      …
    }) => {
      const DICTIONARY = {
        'green-500': 'after:border-b-green-500',
        // …
      };
      const positionStyles = {
        [TOOLTIP_POSITIONS.BOTTOM]: `
        …
        after:border-b-[6px] ${DICTIONARY[bgColor]}
        …`,
      };
    
  • Use the style attribute for truly dynamic colors, if theme can be converted to a valid CSS color value (or you could get the color from Tailwind):

    import resolveConfig from 'tailwindcss/resolveConfig';
    import tailwindConfig from './tailwind.config.js';
    
    const fullConfig = resolveConfig(tailwindConfig);
    
    // …
    
    const ToolTip = ({
      …
      bgColor = "green-500",
      …
    }) => {
      const positionStyles = {
        [TOOLTIP_POSITIONS.BOTTOM]: `
        …
        after:border-b-[6px] after:border-b-[color:--color]
        …`,
      };
    
      return (
        <div …>
          {children}
          <div
            className={`… ${positionStyles[position]}`}
            style={{
              '--color': fullConfig.theme.borderColor[bgColor] || fullConfig.theme.borderColor[bgColor.split('-')[0]][bgColor.split('-')[1]],
            }}
          >
    
  • safelist the classes, if you have a limited number of known colors:

    module.exports = {
      safelist: [
        {
          pattern: /^border-b-(red|green|blue)-500$/,
          variants: ['after'],
        },
        // …
      ],
      // …
    ];
    
0
On

Instead of using after:border-b-${bgColor} the whole class name should be variable meaning

let bgColor = "border-b-red-200" // whole className should be static

const positionStyles = {
   // ..other classes
   after:${bgColor}
   `,
};