tailwindlabs / tailwindcss

A utility-first CSS framework for rapid UI development.
https://tailwindcss.com/
MIT License
82.19k stars 4.16k forks source link

@tailwind directive doesnt work with purgecss ignore #1543

Closed pavelloz closed 4 years ago

pavelloz commented 4 years ago

I have app.css which looks like this:

/* purgecss start ignore */
@tailwind base;
@import './css/base';
@import './css/fonts';
@import 'docsearch.js/dist/cdn/docsearch.css';
/* purgecss end ignore */

Webpack spits out:

app.css   2.54 KiB       0  [emitted]              app

When i comment tailwind, so the code becomes this:

/* purgecss start ignore */
/* @tailwind base; */
@import './css/base';
@import './css/fonts';
@import 'docsearch.js/dist/cdn/docsearch.css';
/* purgecss end ignore */

It spits out much, much more.

app.css     19 KiB       0  [emitted]              app

I thought ignore means purgecss will ignore everything inside, so there should be no difference, or at least when i comment something out in there, it will become smaller, not 5x bigger and with completely different content.

It looks like no matter what i do in this ignore block, its either tailwind, or the rest.

I found a workaround (which leads me to believe that it is only tailwind issue). I changed how i import tailwind:

/* purgecss start ignore */
@import 'tailwindcss/base';
@import './css/base';
@import './css/fonts';
@import 'docsearch.js/dist/cdn/docsearch.css';
/* purgecss end ignore */

And now it works fine.

daniandl commented 4 years ago

Please attach your webpack config :)

pavelloz commented 4 years ago

Ouh! I just realized, i dont have tailwind.config.js - which might be unusual (i dont see a need ATM). This might be it?

pavelloz commented 4 years ago
Webpack config ```js const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const WebpackRequireFrom = require('webpack-require-from'); const TerserPlugin = require('terser-webpack-plugin'); const production = process.env.NODE_ENV === 'production'; const plugins = [ new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[name].css' }), new WebpackRequireFrom({ variableName: 'window.__CONTEXT__.cdnUrl' }) ]; const config = { entry: { app: './src/app' }, output: { filename: '[name].js', chunkFilename: '[name].[chunkhash:3].js', publicPath: '', path: path.resolve('app/assets') }, bail: true, stats: { modules: false, hash: false, assetsSort: '!size', children: false }, module: { rules: [ { test: /\.js$/, loader: 'babel-loader?retainLines=true&cacheDirectory' }, { test: /(\.css)$/, use: [ MiniCssExtractPlugin.loader, 'css-loader?url=false', 'postcss-loader' ] }, { test: /\.gif$/, use: ['file-loader?name=[name].gif'] }, { test: /\.(eot|ttf|otf|svg|woff(2))?$/, loader: 'file-loader?name=fonts/[name].[ext]' } ] }, optimization: { minimizer: [ new TerserPlugin({ parallel: true, cache: true }) ], splitChunks: { cacheGroups: { vendors: false // Do not emit vendors~* files that are almost empty in this setup } } }, plugins: plugins, mode: production ? 'production' : 'development' }; module.exports = config; ```
Postcss config ```js // Todo: Investigate DropCSS (maybe write postcss-plugin) // https://github.com/leeoniya/dropcss const purgecss = require('@fullhuman/postcss-purgecss')({ content: ['app/views/**/*.liquid', 'src/js/**/*.js'], css: ['app/assets/**/app.css'], defaultExtractor: content => content.match(/[\w-/:]+(? { const prod = process.env.NODE_ENV === 'production'; return { plugins: [ require('postcss-fixes'), require('postcss-import'), require('postcss-nested'), require('autoprefixer'), require('tailwindcss'), prod ? purgecss : undefined, prod ? csso : undefined // keep csso after purgecss, or it will break ] }; }; ```
adamwathan commented 4 years ago

@import statements must come first in a CSS file according to the spec and that is why you have to use @import 'tailwindcss/base.

This is explained here:

https://tailwindcss.com/docs/installation#2-add-tailwind-to-your-css

This behavior comes from the postcss-import plugin and is by design, and is not a bug in our code:

https://github.com/postcss/postcss-import

This plugin attempts to follow the CSS @import spec; @import statements must precede all other statements (besides @charset).

adamwathan commented 4 years ago

Here's another related discussion:

https://github.com/postcss/postcss-import/issues/257

sebastienbarre commented 3 years ago

@adamwathan I'm having the same issue, still.

/* purgecss start ignore */
@tailwind base;

@tailwind components;

/* Override react-toastify CSS styles https://fkhadra.github.io/react-toastify/how-to-style */
.Toastify__toast-container {
  width: 450px !important;
}

.Toastify__toast {
  font-family: inherit !important;
  min-height: 0px !important;
  padding: 0px !important;
  border-radius: 0px !important;
}

.Toastify__toast-body {
  width: 100% !important;
  height: 100% !important;
  padding: 0px !important;
  margin: 0px !important;
}

/* purgecss end ignore */

@tailwind utilities;

Yet the .Toastify classes are not making it to tailwind.output.css. My Tailwind config has purge.enabled: false because I'm using postcss.config.js:

const trimEnd = require('lodash.trimend');

const purgeContent = ['./public/**/*.html', './src/**/*.tsx', './src/**/*.jsx', './src/**/*.js'];

// https://tailwindcss.com/docs/controlling-file-size#setting-up-purge-css-manually
// https://github.com/tailwindlabs/tailwindcss/blob/master/src/lib/purgeUnusedStyles.js
const purgeExtractor = (content) => {
  // Capture as liberally as possible, including things like `h-(screen-1.5)`
  const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || [];
  const broadMatchesWithoutTrailingSlash = broadMatches.map((match) => trimEnd(match, '\\'));

  // Capture classes within other delimiters like .block(class="w-1/2") in Pug
  const innerMatches = content.match(/[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g) || [];

  return broadMatches.concat(broadMatchesWithoutTrailingSlash).concat(innerMatches);
};

const production = process.env.NODE_ENV === 'production';

module.exports = {
  plugins: [
    require('tailwindcss')('./tailwind.config.js'),
    production ? require('autoprefixer') : null,
    production ? require('cssnano')({ preset: 'default' }) : null,
    production
      ? require('@fullhuman/postcss-purgecss')({
          content: purgeContent,
          defaultExtractor: purgeExtractor,
        })
      : null,
  ],
};