Asger Skov Velling
Asger Skov Velling

Reputation: 393

React and Tailwind CSS: dynamically generated classes are not being applied

I'm just learning React and Tailwind CSS and had a strange experience with CSS grid using Tailwind classes. I've made the buttons for a calculator, with the last Button spanning two columns:

App.js:

export default function App() {
  return (
    <div className="flex min-h-screen items-center justify-center bg-blue-400">
      <Calculator />
    </div>
  );
}

Calculator.js

import { IoBackspaceOutline } from "react-icons/io5";

export const Calculator = () => {
  return (
    <div className="grid grid-cols-4 grid-rows-5 gap-2">
      <Button>AC</Button>
      <Button>
        <IoBackspaceOutline size={26} />
      </Button>
      <Button>%</Button>
      <Button>÷</Button>
      <Button>7</Button>
      <Button>8</Button>
      <Button>9</Button>
      <Button>x</Button>
      <Button>4</Button>
      <Button>5</Button>
      <Button>6</Button>
      <Button>-</Button>
      <Button>1</Button>
      <Button>2</Button>
      <Button>3</Button>
      <Button>+</Button>
      <Button>0</Button>
      <Button>.</Button>
      <Button colSpan={2}>=</Button>
    </div>
  );
};

const Button = ({ colSpan = 1, rowSpan = 1, children }) => {
  return (
    <div
      className={`col-span-${colSpan} row-span-${rowSpan} bg-white p-3 rounded`}
    >
      <div className="flex items-center justify-center">{children}</div>
    </div>
  );
};

This doesn't work (tested in Chrome): enter image description here

Now here comes the weird part. I replaced the returned JSX from the App component with HTML from a Tailwind tutorial and deleted it again.

<div className="bg-blue-400 text-blue-400 min-h-screen flex items-center justify-center">
  <div className="grid grid-cols-3 gap-2">
    <div className="col-span-2 bg-white p-10 rounded">1</div>
    <div className="bg-white p-10 rounded">2</div>
    <div className="row-span-3 bg-white p-10 rounded">3</div>
    <div className="bg-white p-10 rounded">4</div>
    <div className="bg-white p-10 rounded">5</div>
    <div className="bg-white p-10 rounded">6</div>
    <div className="col-span-2 bg-white p-10 rounded">7</div>
    <div className="bg-white p-10 rounded">8</div>
    <div className="bg-white p-10 rounded">9</div>
  </div>
</div>

After I Ctrl-Z'd a bunch of times, so I had only the previous code, my button suddenly spans two columns as intended:

enter image description here

I checked to make sure that there were no changes in the code:

enter image description here

My friend even cloned my repo, followed the same steps and got the same result. He suspects that it has something to do with the variable classNames in my Button component with regards to Tailwind's JIT compiler, but none of us can pinpoint the error.

Am I using variable CSS classes wrong?

This has been a WTF moment. What could be the reason for this?

Upvotes: 23

Views: 40565

Answers (5)

Daniel De Le&#243;n
Daniel De Le&#243;n

Reputation: 13679

I suffer the same issue...

And Is because a dare to move the functional component outside the @/app/ directory...

... Then I just return the files, and everything works fine again.

Upvotes: 0

dan-klasson
dan-klasson

Reputation: 14220

You can use inline styles. E.g style={{ paddingLeft: width }}

Upvotes: 1

Daher
Daher

Reputation: 1441

As Ed Lucas said: The CSS file generated by Tailwind will only include classes that it recognizes when it scans your code, which means that dynamically generated classes (e.g. col-span-${colSpan}) will not be included

But now could use safeListing

and tailwind-safelist-generator package to "pregenerate" our dynamics styles.

With tailwind-safelist-generator, you can generate a safelist.txt file for your theme based on a set of patterns.

Tailwind's JIT mode scans your codebase for class names, and generates CSS based on what it finds. If a class name is not listed explicitly, like text-${error ? 'red' : 'green'}-500, Tailwind won't discover it. To ensure these utilities are generated, you can maintain a file that lists them explicitly, like a safelist.txt file in the root of your project.

Upvotes: 15

Tkin R.
Tkin R.

Reputation: 171

Another tricky solution that worked for me is to use variable with forced type of the possible className values (in typescript) like :

export type TTextSizeClass =
  'text-xl'  |
  'text-2xl' |
  'text-3xl' |
  'text-4xl' |
  'text-5xl' |
  'text-6xl' |
  'text-7xl' |
  'text-8xl' |
  'text-9xl'
;
...
const type : number = 6 ;
const textSizeClass : TTextSizeClass = type != 1 ? `text-${type}xl` : 'text-xl';
...
<div className={`font-semibold ${textSizeClass} ${className}`}>text</div>

Upvotes: 7

Ed Lucas
Ed Lucas

Reputation: 7355

The CSS file generated by Tailwind will only include classes that it recognizes when it scans your code, which means that dynamically generated classes (e.g. col-span-${colSpan}) will not be included.

If you only need to span 2 columns, you could pass boolean values which will trigger the addition of a full col-span-2 or row-span-2 utility class to be added:

const Button = ({ colSpan = false, rowSpan = false, children }) => {
  return (
    <div
      className={`${colSpan ? 'col-span-2' : ''} ${rowSpan ? 'row-span-2' : ''} bg-white p-3 rounded`}
    >
      <div className="flex items-center justify-center">{children}</div>
    </div>
  );
};

Otherwise, you could pass the values as classes to the Button component:

<Button className='col-span-2 row-span-1'>=</Button>

const Button = ({ className, children }) => {
  return (
    <div
      className={`${className} bg-white p-3 rounded`}
    >
      <div className="flex items-center justify-center">{children}</div>
    </div>
  );
};

More information: https://tailwindcss.com/docs/content-configuration#dynamic-class-names

Upvotes: 37

Related Questions