Gatsby Dynamic styling not working in production build

Viewed 2280

I am new in Gatsby and I am using tailwind css with postcss. Some of the color configurations I defined in theme object of tailwind.config.js is working in dev environment but not in production. I have tried cleaning cache and deleting public folder and re-building it. That did not solve the problem. My theme object in tailwind.config.js is like this:

theme: {
    colors: {
      transparent: 'transparent',
      current: 'currentColor',
      primary: {
        DEFAULT: '#4F9C3A',
        900: '#25441c',
      },
      secondary: {
        0: '#ff9563',
        DEFAULT: '#E66437',
        9: '#ae3409',
      },
      footer: {
        light: '#e66437',
        DEFAULT: '#383e42',
        dark: '#26292c',
      },
      neutral: {
        0: '#ffffff',
        DEFAULT: '#ffffff',
        1: '#fafafa',
        9: '#000000',
      },
      accent: {
        1: '#388ac5',
        DEFAULT: '#293842',
      },
      brown: {
        DEFAULT: '#C9AC75',
        2: '#44261c',
      },
      black: '#000000',
    }
}

UPDATE: I have been able to pinpoint source of the problem. I am fetching which class names to apply from a json file using gatsby-transformer-json. I have something like the following code segment to set background color which is working in development environment but not in production.

<div className={`bg-${color}>
The development build shows proper background color for this segment but production build does not. 
</div>
2 Answers

TLDR: Don't use string concatenation in your class names when you use the purge option in tailwind.config.js. Instead, provide full class names.

Source: https://tailwindcss.com/docs/optimizing-for-production


UPDATE March 1st, 2021

Thanks @robotu for bringing another great option to the table: you can add a safelist option to your tailwind.config.js file, like this:

module.exports = {
  // ...
  purge: {
    content: ['./src/**/*.html'],
    safelist: ['bg-primary', 'bg-secondary']
  }
}



Original OP Code/Intent

You've done this: <div className={`bg-${color}`>

But TailwindCSS prefers something like this: <div className={ color === "red" ? "bg-red" : "bg-blue" }>

My guess is that you could probably use a function/hook that returns the full class name in the case that you have many possible colors, but I haven't tested that yet.




The longer version:

You aren't showing us your full tailwind.config.js file, but I'll assume you're using the purge option somewhere in there.

Continuing with Ferran's answer: I'd say the real issue is how PurgeCSS, which Tailwind uses under the hood during the build process when you include the purge option in your config, determines what classes to purge during the build process.

It finds any strings that match the regular expression:

/[^<>"'`\s]*[^<>"'`\s:]/g

Which will naively find almost everything, only stopping at specific syntax symbols. It will then try to preserve every match, and purge the rest. So it will find bg- and color, preserve them, but will purge bg-color as that was not found by PurgeCSS's regex.

From TailwindCSS docs:

That means that it is important to avoid dynamically creating class strings in your templates with string concatenation, otherwise PurgeCSS won't know to preserve those classes.

According to the Tailwind + Gatsby docs, there are two important statements to consider:

In gatsby-browser.js add an import rule for your Tailwind directives and custom CSS so that they are accounted for in build.

And:

Note: By default, PurgeCSS only runs on the build command as it is a relatively slow process. The development server will include all Tailwind classes, so it’s highly recommended you test on a build server before deploying.

In your case, the issue may come from the PurgeCSS directive because it's not present so it may be purging all the styling. Fix it by:

  // tailwind.config.js
  module.exports = {
   purge: ['./src/**/*.{js,jsx,ts,tsx}'],
    darkMode: false, // or 'media' or 'class'
    theme: {
      extend: {},
    },
    variants: {
      extend: {},
    },
    plugins: [],
  }

Source: https://tailwindcss.com/docs/guides/gatsby

Or:

module.exports = {
  purge: ["./src/**/*.js", "./src/**/*.jsx", "./src/**/*.ts", "./src/**/*.tsx"],
  theme: {},
  variants: {},
  plugins: [],
}

Source: https://www.gatsbyjs.com/docs/how-to/styling/tailwind-css/

One other thing you can try is to move your styles to global styles in your gatsby-browser.js:

import "tailwindcss/dist/base.min.css"

I'm assuming that in your gatsby-config.js you have already declared the proper instances:

plugins: [
  {
    resolve: `gatsby-plugin-sass`,
    options: {
      postCssPlugins: [
        require("tailwindcss"),
        require("./tailwind.config.js"), // Optional: Load custom Tailwind CSS configuration
      ],
    },
  },
],

Note: Optionally you can add a corresponding configuration file (by default it will be tailwind.config.js). If you are adding a custom configuration, you will need to load it after tailwindcss.

Related