Search code examples
cssnext.jstailwind-css

Why does my Tailwind styles not apply until I define them again?


I have a problem with my Tailwind classes.

I created a new component in the component directory and I placed my Button component there.

However, any time I restart the project in terminal the styles don't affect my button until I define them again manually. Here are my tailwind.config.css, Button.js and the Navbar.js files:

tailwind.config.css:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        primary: '#FFD700',
        secondary: '#00CED1',
        accent: '#FF6347',
        success: '#32CD32'
      },
    },
  },
  plugins: [],
};

Button.js:

'use client'
import React, { useState, useEffect } from 'react'

const Button = ({ children, color }) => {

    const [colorPicker, setColorPicker] = useState('')
    const [colorWeight, setColorWeight] = useState(0)
    
    useEffect(() => {
        switch (color) {
        case 'primary':
            setColorPicker('amber')
            setColorWeight(300)
            break;
        case 'secondary':
            setColorPicker('teal')
            setColorWeight(500)
            break;
        case 'seccess':
            setColorPicker('lime')
            setColorWeight(600)
            break;
        case 'accent':
            setColorPicker('red')
            setColorWeight(500)
            break;
    }
    }, [color])
    

  return (
    <div>
        <button className={`bg-${color} px-4 py-1 rounded-xl hover:bg-${colorPicker}-${colorWeight} focus:bg-${colorPicker}-${colorWeight + 100} focus:ring-4 focus:ring-${colorPicker}-${colorWeight - 200} duration-200 text-white`}>
            {children}
        </button>
    </div>
  )
}

export default Button

Navbar.js

import Link from 'next/link';
import Button from './Button';

const Navbar = () => {
  return (
    <div>
      <div className='flex justify-between items-center'>
        <div>
          <div className='flex'>
            <img src="" alt="Logo" />
            <p>Crossant</p>
          </div>
        </div>

        <div>
          <ul className='flex gap-3 items-center'>
            <Link href='/'>
              <li>Home</li>
            </Link>
            <Link href='/about'>
              <li>About me</li>
            </Link>
            <Link href='/posts'>
              <li>Posts</li>
            </Link>
            <Link href='/login'>
              <li>
              <Button color='primary'>Login</Button>
              </li>
            </Link>
          </ul>
        </div>
      </div>
    </div>
  )
}

export default Navbar

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 color to Tailwind class names:
      const Button = ({ children, color }) => {
        let classes = '';
      
        switch (color) {
          case 'primary':
            classes = 'bg-primary hover:bg-amber-300 focus:bg-amber-400 focus:ring-amber-100';
            break;
          case 'secondary':
            classes = 'bg-secondary hover:bg-teal-500 focus:bg-teal-600 focus:ring-teal-300';
            break;
          case 'seccess':
            classes = 'bg-seccess hover:bg-lime-600 focus:bg-lime-700 focus:ring-lime-400';
            break;
          case 'accent':
            classes = 'bg-accent hover:bg-red-500 focus:bg-red-600 focus:ring-red-300'
            break;
        }
      
        return (
          <div>
            <button className={`px-4 py-1 rounded-xl focus:ring-4 duration-200 text-white ${classes}`}>
              {children}
            </button>
          </div>
        )
      }
      
      Refactored to an object dictionary:
      const CLASSES = {
        primary: 'bg-primary hover:bg-amber-300 focus:bg-amber-400 focus:ring-amber-100',
        secondary: 'bg-secondary hover:bg-teal-500 focus:bg-teal-600 focus:ring-teal-300',
        seccess: 'bg-seccess hover:bg-lime-600 focus:bg-lime-700 focus:ring-lime-400',
        accent: 'bg-accent hover:bg-red-500 focus:bg-red-600 focus:ring-red-300,
      };
      
      const Button = ({ children, color }) => {
        return (
          <div>
            <button className={`px-4 py-1 rounded-xl focus:ring-4 duration-200 text-white ${CLASSES[color]}`}>
              {children}
            </button>
          </div>
        )
      }
      
      
    • safelist the classes, if you have a limited number of known colors:
      module.exports = {
        safelist: [
          { pattern: /^bg-(primary|secondary|seccess|accent)$/ },
          {
            pattern: /^bg-(amber|teal|lime|red)-300$/,
            variants: ['hover'],
          },
          // …
        ],
        // …
      ];