Search code examples
csscomponentstailwind-cssextendsolid-js

Can props be passed into tailwindcss classes?


What I'm trying to do - create a reusable component that passes values into an extended background image class.

The issue - the background image is not appearing until after I first run the jsx with hardcoded classes of each background image

My tailwindcss config :

theme: {
    extend: {
      backgroundImage: {
        "race-1": "url('./src/assets/race1.png')",
        "race-2": "url('./src/assets/race2.png')",
        "race-3": "url('./src/assets/race3.png')",
        "class-1": "url('./src/assets/class1.png')",
        "class-2": "url('./src/assets/class2.png')",
        "class-3": "url('./src/assets/class3.png')",
      },
    },
  },

My component :

export const SelectionCard = (props) => {
  //passing the props in for 'class,race,etc' and '1,2,etc'
  const selectionCategory = props.category;
  const selectionName = props.name;
  
  //creates the custom class to apply the background image, doesn't work until images are already loaded
  const addSelectionClass = (passedSelectionCategory, passedSelectionName) => {
    const adjustedSelectionCategory = passedSelectionCategory.toLowerCase();
    const adjustedSelectionName = passedSelectionName.toLowerCase();

    return (
      "classBackground bg-" +
      adjustedSelectionCategory +
      "-" +
      adjustedSelectionName +
      " saturate-30 hover:saturate-100 flex justify-center w-full h-full"
    );
  };

  // I have to first load the classes by inserting them on first page load, then I can change them to the 'adjustedSelectionClass' in jsx and they load fine with the above function
  const loadedClassBackgrounds = "bg-class-1 bg-class-2 bg-class-3";
  const loadedRaceBackgrounds = "bg-race-1 bg-race-2 bg-race-3";

  // The variable I'm trying to use on first page load
  const adjustedSelectionClass = addSelectionClass(
    selectionCategory,
    selectionName,
  );

  return (
    <div className="baseLayoutStyles cardShrink">
      <div className={adjustedSelectionClass}>
        <div className="text-center text-white text-xl font-extrabold pt-4 bg-colorHeader rounded-t w-full h-1/6 text-shadow shadow-gray-800">
          {selectionName}
        </div>
      </div>
    </div>
  );
};
// I have to first load the classes by inserting them first, then I can change them to the 'adjustedSelectionClass' in jsx and they load fine
  const loadedClassBackgrounds = "bg-class-1 bg-class-2 bg-class-3";
  const loadedRaceBackgrounds = "bg-race-1 bg-race-2 bg-race-3";

Solution

  • 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 a dictionary for to map the variables to the Tailwind class name:
      const DICTIONARY = {
        race: {
          '1': 'bg-race-1',
          '2': 'bg-race-2',
          '3': 'bg-race-3',
        },
        class: {
          '1': 'bg-class-1',
          '2': 'bg-class-2',
          '3': 'bg-class-3',
        },
      };
      // …
      const addSelectionClass = (passedSelectionCategory, passedSelectionName) => {
        const adjustedSelectionCategory = passedSelectionCategory.toLowerCase();
        const adjustedSelectionName = passedSelectionName.toLowerCase();
      
        return (
          "classBackground " +
          DICTIONARY[adjustedSelectionCategory][adjustedSelectionName] +
          " saturate-30 hover:saturate-100 flex justify-center w-full h-full"
        );
      };
      
    • safelist the classes:
      module.exports = {
        safelist: [
          { pattern: /^bg-(race|class)-[1-3]$/ },
          // …
        ],
        // …
      ];
      
      You're kind of doing this already in a roundabout way with:
      // I have to first load the classes by inserting them first, then I can change them to the 'adjustedSelectionClass' in jsx and they load fine
      const loadedClassBackgrounds = "bg-class-1 bg-class-2 bg-class-3";
      const loadedRaceBackgrounds = "bg-race-1 bg-race-2 bg-race-3";