Search code examples
cssreactjstypescriptstylestailwind-css

How to format tailwind classes in a cleaner way for re-usable styles dynamically?


I am making this reusable badge using Tailwind CSS where the colour of the badge is supposed to change depending on the status. The result is very unclean and I am looking for a way to optimise this.

I have tried using string literals to try make the styles cleaner as following;

bg-${color}-100 and same fo other classNames. Apparently this does not work because tailwind expects you to pass a full class name like this bg-green-100. This means I have to conditionally check for each colour as this color === "green"? "bg-green-100" : "bg-red-100" this forces you to write so many classes if you have a lot of colour props that you are checking.

This is how to whole component looks like

import ctl from "@netlify/classnames-template-literals";
interface BadgeProps {
  color: string;
  status: string | boolean;
}

function Badge({ color, status }: BadgeProps) {
  return <span className={badgeStyle(color)}>{status}</span>;
}


const badgeStyle = (color: string) =>
  ctl(
   `
    ${
      color === "green"
        ? `bg-green-100 text-green-800  ${containerStyle} dark:bg-green-900 dark:text-green-300`
        : color === "red"
        ? `bg-red-100 text-red-800  ${containerStyle} dark:bg-red-900 dark:text-red-300`
        : color === "yellow"
        ? `bg-yellow-100 text-yellow-800  ${containerStyle} dark:bg-yellow-900 dark:text-yellow-300`
        : color === "blue"
        ? "bg-blue-100 text-blue-800  ${containerStyle} dark:bg-blue-900 dark:text-blue-300"
        : color === "indigo"
        ? `bg-indigo-100 text-indigo-800  ${containerStyle} dark:bg-indigo-900 dark:text-indigo-300`
        : color === "purple"
        ? `bg-purple-100 text-purple-800  ${containerStyle} dark:bg-purple-900 dark:text-purple-300`
        : color === "pink"
        ? `bg-pink-100 text-pink-800  ${containerStyle} dark:bg-pink-900 dark:text-pink-300`
        : color === "orange"
        ? `bg-orange-100 text-orange-800  ${containerStyle} dark:bg-orange-900 dark:text-orange-300`
        : color === "gray"
        ? `bg-gray-100 text-gray-800  ${containerStyle} dark:bg-gray-900 dark:text-gray-300`
        : color === "gray"
        ? `bg-gray-100 text-gray-800  ${containerStyle} dark:bg-gray-900 dark:text-gray-300`
        : ""
    }
   
    `
  );

const containerStyle = ctl(`
 font-medium mr-2 px-2.5 py-0.5 rounded  text-sm text-sm
  `);

export default Badge;

Solution

  • You could create a separate file that maps the different color props to the corresponding Tailwind classes.

    Create file called colorClasses.js that exports an object with the different color props as keys, and the corresponding Tailwind classes as values.

    colorClasses.js

    export default {
      green: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300',
      red: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300',
      yellow: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300',
      blue: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300',
      indigo: 'bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-300',
      purple: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300',
      pink: 'bg-pink-100 text-pink-800 dark:bg-pink-900 dark:text-pink-300',
      orange: 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-300',
      gray: 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300'
    }
    

    Then in your component file, you can import the colorClasses object and use it to dynamically set the class names based on the color prop.

    import colorClasses from './colorClasses';
    
    const Badge = ({ color }) => {
      const containerStyle = 'font-medium mr-2 px-2.5 py-0.5 rounded text-sm';
      const colorClass = colorClasses[color];
      return (
        <div className={`${colorClass} ${containerStyle}`}>
          {/* Your content here */}
        </div>
      );
    };