Skip to content
This repository has been archived by the owner on Mar 6, 2020. It is now read-only.

How to get Tailwind working with Gatsby? #100

Closed
ooloth opened this issue Jan 16, 2018 · 21 comments
Closed

How to get Tailwind working with Gatsby? #100

ooloth opened this issue Jan 16, 2018 · 21 comments

Comments

@ooloth
Copy link

ooloth commented Jan 16, 2018

Has anyone managed to get Tailwind working with Gatsby.js?

Configuring postCSS plugins with Gatsby is a bit tricky... If anyone has managed to get Tailwind up and running with Gatsby, I'd love to know how!

@01ivr3
Copy link

01ivr3 commented Jan 21, 2018

Gatsby's current implementation of PostCSS is cobbled together. I posted a roundup issue about this about a month ago:

Feature Request: make Postcss plugins configurable, update dependancies and add to documentation #3284

Short version: it looks like Postcss dependancies included with Gatsby v1 are quite out of date.

ref: Update postcss related plugins #2462

If attempting to include the tailwindcss v0.4.0 plugin in Gatsby via the current method of using its gatsby-plugin-postcss-sass plugin (which I believe overwrites the default postcss plugins included by Gatsby, which aren't exposed in its config) then you'll get the typical Module build failed: TypeError: atRule.before is not a function error that's routinely posted in Tailwind related issues, due to Tailwind requiring a fairly recent major version of PostCSS

I've been debating with a colleague about taking a closer look at postcss support in Gatsby, but at the moment, we mostly stopped looking into the platform since some postcss plugins are used in our workflow/tooling.

@ooloth
Copy link
Author

ooloth commented Jan 22, 2018

Thanks for the thorough reply!

I'm a fan of Gatsby, so I've decided it's worth using Tailwind with it and manually building changes via the Tailwind CLI for now.

It's a bit of extra effort and I'd certainly rather use postCSS to automate this step, but I created an npm run tailwind shortcut that makes it not too bad for now.

I hope you find a good solution for your postCSS needs!

@ooloth
Copy link
Author

ooloth commented Feb 9, 2018

In case anyone finds it helpful, I posted my current CLI workaround for running Sass + Tailwinds + PurgeCSS with Gatsby here on StackOverflow.

If anyone can translate it into a working Webpack version for Gatsby, I'd be thrilled!

@fardarter
Copy link

@ooloth Is this current best practice? Is it possible to achieve this with the following plugin? https://www.npmjs.com/package/@jesses/gatsby-plugin-postcss

@ooloth
Copy link
Author

ooloth commented Jul 24, 2018

@fardarter Yes, you can now run postcss on your stylesheets with Gatsby using gatsby-plugin-postcss (the CLI tool is no longer necessary).

The plugin is working for me as long as I list the postcss plugins in a postcss.config.js file in the root of the project folder. (I wasn't able to get it working by adding the plugin settings in gatsby-config.js, but that doesn't really matter.)

To simplify the setup, I switched from Sass to CSS + a few postcss plugins that simulate Sass-like features. This made it easier to guarantee the "Sass"-style processing runs before the Tailwind processing (using the order of the plugins in postcss.config.js).

If you prefer to keep using Sass, you may be able to simply use gatsby-plugin-sass along with gatsby-plugin-postcss. I haven't tested this approach, though, so I'm not sure if the Sass plugin runs first (as you would need it to when using Tailwinds).

In case it helps, here's my updated setup (CSS + PostCSS + PurgeCSS) that is working with Gatsby v2 and allows me to use Sass-like features with Tailwind:

npm i gatsby-plugin-postcss precss tailwindcss autoprefixer purgecss-webpack-plugin
// gatsby-config.js

...
plugins: [
  `gatsby-plugin-postcss`,
  ...
]
...
// postcss.config.js

/*

PreCSS run the following plugins (in this order):

1. postcss-extend-rule
2. postcss-advanced-variables
3. postcss-preset-env
4. postcss-atroot
5. postcss-property-lookup
6. postcss-nested

*/

module.exports = {
  plugins: [
    require(`precss`),
    require(`tailwindcss`)(`./src/styles/tailwind.js`),
    require(`autoprefixer`)()
  ]
}
// components/Base.js

import '../styles/index.css' // file that imports all other stylesheets
// gatsby-node.js

// To run PurgeCSS in production, add the following:

const PurgeCssPlugin = require(`purgecss-webpack-plugin`)
const path = require(`path`)
const glob = require(`glob`)

const PATHS = {
  src: path.join(__dirname, `src`)
}

const purgeCssConfig = {
  paths: glob.sync(`${PATHS.src}/**/*.js`, { nodir: true }),
  extractors: [
    {
      // Custom extractor to allow special characters (like ":") in class names
      // See: https://tailwindcss.com/docs/controlling-file-size/#removing-unused-css-with-purgecss
      extractor: class {
        static extract(content) {
          return content.match(/[A-Za-z0-9-_:/]+/g) || []
        }
      },
      extensions: [`js`]
    }
  ],
  whitelist: [`class-to-whitelist`], // adjust for each project
  whitelistPatterns: [/body/, /headroom/, /ReactModal/, /ril/] // adjust for each project
}

exports.onCreateWebpackConfig = ({ actions, stage }) => {
  if (stage.includes(`develop`)) return

  // Add PurgeCSS in production
  // See: https://github.com/gatsbyjs/gatsby/issues/5778#issuecomment-402481270
  if (stage.includes(`build`)) {
    actions.setWebpackConfig({
      plugins: [new PurgeCssPlugin(purgeCssConfig)]
    })
  }
}

Hopefully that helps!

If anyone can suggest ways to streamline this further (or incorporate Sass), that would be great too.

@ooloth ooloth closed this as completed Jul 24, 2018
@magicspon
Copy link

Nice one...

Any thoughts on minifying the output?

ta

@ooloth
Copy link
Author

ooloth commented Jul 25, 2018

@magicspon Good one! I assumed the output was minified, but you're right that it isn't.

Any Gatsby or webpack people know what needs to be added here? (Gatsby v2 used Webpack v4.)

@magicspon
Copy link

I tried adding:

new MiniCssExtractPlugin({
      filename: "[name].css"
    }),

no change

@magicspon
Copy link

@ooloth the @apply syntax seems to be broken still as well

./src/components/Panel/Panel.css (./node_modules/css-loader??ref--9-oneOf-1-1!./node_modules/postcss-loader/lib??ref--9-oneOf-1-2!./src/components/Panel/Panel.css)
Module build failed (from ./node_modules/postcss-loader/lib/index.js):
Syntax Error 

(4:2) `@apply` cannot be used with `.block` because `.block` either cannot be found, or it's actual definition includes a pseudo-selector like :hover, :active, etc. If you're sure that `.block` exists, make sure that any `@import` statements are being properly processed *before* Tailwind CSS sees your CSS, as `@apply` can only be used for classes in the same CSS tree.

  2 | 
  3 | .test {
> 4 | 	@apply .block;
    | 	^
  5 | }
  6 | 

 @ ./src/components/Panel/Panel.css 4:14-166 18:2-22:4 19:20-172
 @ ./src/components/Panel/Panel.js
 @ ./src/pages/index.js
 @ ./.cache/sync-requires.js
 @ ./.cache/root.js
 @ ./.cache/app.js
 @ multi ./node_modules/react-hot-loader/patch.js (webpack)-hot-middleware/client.js?path=https://localhost:8000/__webpack_hmr&reload=true&overlay=false ./.cache/app

babel-plugin-tailwind + emotion isn't a bad compromise

@ooloth
Copy link
Author

ooloth commented Jul 25, 2018

@magicspon The @apply syntax should work just fine (it does for me with the setup above).

That @apply error (which I run into as well) means that the “block” class doesn’t exist or hasn’t been imported properly.

@magicspon
Copy link

that was my first thought... but .block is definitely there, as is text-blue et al... is it working for you as expected?

@magicspon
Copy link

magicspon commented Jul 25, 2018

this works:

@tailwind utilities;

.test {
	@apply .text-blue;
}
import React, { Component } from 'react'
import './Panel.css'

export default class Panel extends Component {
	render() {
		return <div className="test">hello world</div>
	}
}

but then i have multiple style tags with duplicate code... i'm possibly going terribly wrong somewhere!!

@ooloth
Copy link
Author

ooloth commented Jul 25, 2018

What works for me is to have all of my CSS files imported by a single index.css file...

/* index.css */

@import './base/index';
@import './plugins/index';
@import './components/index';
@import './utilities/index';
@import './new';

...where each index file imported by index.css also imports its own subfiles (so I can maintain a growing utility CSS library while keeping each file small and easy to read). You may have fewer files and imports, depending on your setup:

/* base/index.css, for example */

@import './font-face';
@import './tailwind-preflight';
@import './reset';
@import './a11y';

Make sure the @tailwind directives are each imported from a separate file (rather than being placed beside other imports):

/* base/_tailwind-preflight.css, for example */

@tailwind preflight; /* the only line in the file */

And then just import the main index.css file once in your top level component:

// components/Layout.js

import '../styles/index.css' /* the only CSS import in the entire project */

As long as your CSS imports are set up correctly (it took me some time to fix problems with mine) and you are only importing one CSS file into your JS, you should end up with a single style tag that includes all of your CSS in the desired order.

With this setup, I'm able to use @apply and the other Tailwinds directives in any of my CSS files with no issues (unless the class doesn't exist, hasn't been imported, or I'm erroneously using a responsive version of the class).

The PurgeCSS additions to gatsby-node.js above will then help eliminate any unused CSS in production.

Hope that helps!

@magicspon
Copy link

Aces...

And then just import the main index.css file once in your top level component:

This is where I was going wrong.

Thanks

@carlcs
Copy link

carlcs commented Sep 10, 2018

For minifying css add the optimize-css-assets-webpack-plugin plugin to the Webpack config optimization key. I’m a bit surprised Gatsby 2 doesn’t include it by default and if there’s another recommended way?

const OptimizeCSSAssetsPlugin = require(`optimize-css-assets-webpack-plugin`)

exports.onCreateWebpackConfig = ({ actions, stage }) => {
  if (stage.includes(`build`)) {
    actions.setWebpackConfig({
      plugins: [
        new PurgeCssPlugin(purgeCssConfig),
      ],
      optimization: {
        minimizer: [
          new OptimizeCSSAssetsPlugin(),
        ],
      },
    })
  }
}

@magicspon
Copy link

I believe there is a pull request to add this to the core.

@carlcs
Copy link

carlcs commented Sep 10, 2018

@Devin-Chaves
Copy link

Surprised this isn't a documented workflow yet. Has anyone got a tailwind/purge/sass workflow going yet?

@magicspon
Copy link

@Devin-Chaves

example of using tailwind/purge here:
https://github.com/magicspon/gatsby-starter-craftcms

credit to @ooloth

@Devin-Chaves
Copy link

@magicspon Thanks for having a look. I actually started tinkering with your spon-postcss plugin. As far as the gatsby-starter you just posted - where is the sass flavor here?

@magicspon
Copy link

@Devin-Chaves alas... i didn't end up using scss with the craftcms starter.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants