How to compress file size in tailwind using postcss? - postcss

I have the following postcss.config.js file:
// postcss.config.js
module.exports = {
plugins: [
require('postcss-import'),
require('tailwindcss'),
require('autoprefixer'),
]
}
and the following tailwind.config.js file:
// tailwind.config.js
module.exports = {
purge: [
'./src/cljs/foo/*.cljs',
'./target/cljs-runtime/*.js',
'./target/cljsbuild/public/js/*',
'./target/cljsbuild/public/js/cljs-runtime/*',
'./target/*'
],
theme: {},
variants: {},
plugins: [],
}
And my goal is to compress the css generated, for which I've added the purge key in tailwind.config.js.
To generate the css from the .src tailwind file, styles.src.css:
#tailwind base;
#tailwind components;
#tailwind utilities;
I'm running the command:
postcss ./resources/public/css/styles.src.css -o ./resources/public/css/styles.css
from the root directory of my project that contains both the tailwind.config.js and the postcss.config.js. Yet after running the command, the generated css is 1.2MB, as big as what I had without the purge key. Why isn't postcss purge working?

You don't need that command with postcss.
Just add enabled:true in purge in tailwind.config.json and wrap your path into list as stated in https://tailwindcss.com/docs/optimizing-for-production#enabling-manually:
purge: {
enabled: true,
content: [
'./src/cljs/foo/*.cljs',
'./target/cljs-runtime/*.js',
'./target/cljsbuild/public/js/*',
'./target/cljsbuild/public/js/cljs-runtime/*',
'./target/*'
],
},
There it is! Now you can run and see the results:
npm run build:css
That's the command I use in package.json:
"scripts": {
"build:css": "tailwind build static/css/tw.css -o static/css/tailwind.css"
},

Your PostCSS configuration is split between tailwind.config.js and postcss.config.js, when it should all be in postcss.config.js.
Why?
Tailwind uses PostCSS behind the scenes. But PostCSS itself doesn't know about your tailwind.config.js file. To use the postcss command, you need to specify the purge option in the postcss.config.js file, not tailwind.config.js. This page on the Tailwind website explains the difference between the two files in detail.
Here is my setup:
// postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
require('#fullhuman/postcss-purgecss')({
// Specify the paths to all of the template files in your project
content: [
'./src/cljs/foo/*.cljs',
'./target/cljs-runtime/*.js',
'./target/cljsbuild/public/js/*',
'./target/cljsbuild/public/js/cljs-runtime/*',
'./target/*'
],
// This extractor will tell PurgeCSS to ignore all CSS selectors and tags used in your files
defaultExtractor: content => Array.from(content.matchAll(/:?([A-Za-z0-9-_:]+)/g)).map(x => x[1]) || []
}),
]
}
Note my tailwind.config.js file is empty:
// tailwind.config.js
module.exports = {
purge: [],
theme: {
extend: {},
},
variants: {},
plugins: [],
}

Well you can also add purge key in postcss.config.js.
This is my config in
postcss.config.js
const purgecss = require('#fullhuman/postcss-purgecss')({
// Specify the paths to all of the template files in your project
content: ['./src/**/*.js', './public/index.html'],
// make sure css reset isnt removed on html and body
whitelist: ['html', 'body'],
// Include any special characters you're using in this regular expression
defaultExtractor: (content) => content.match(/[A-Za-z0-9-_:/]+/g) || [],
})
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
...(process.env.NODE_ENV === 'production' ? [purgecss] : []),
],
}
Important: The environment variable NODE_ENV is responsible for dev and prod environment. If you are using tailwindcss in dev mode, then you don't want to purge as you want to use all the available styles. By setting it for production mode will inform postcss and thus this will purge unused css.
Please take note that I haven't set any config for tailwindcss in webpack config.
At build time, make sure that you have your NODE_ENV set to specific value for production use case. You can use either 'production' or 'prod' doesn't matter. Same will reflect in postcss.config.js.

Tailwind will purge automatically - from their docs:
Now whenever you compile your CSS with NODE_ENV set to production, Tailwind will automatically purge unused styles from your CSS
https://tailwindcss.com/docs/controlling-file-size#basic-usage
You can run commands for your dev and production environments - development will keep all Tailwind's classes, production will run the purge.
package.json:
"dependencies": {
"autoprefixer": "^9.8.5",
"postcss-cli": "^7.1.1",
"tailwindcss": "^1.5.2"
},
"devDependencies": {
"cross-env": "^7.0.2"
},
"scripts": {
"watch": "cross-env NODE_ENV=development postcss static/css/tailwind.css -o style.css --watch",
"build": "cross-env NODE_ENV=production postcss static/css/tailwind.css -o style.css"
},
postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
]
}

Related

Local #import does not result in text substitution

I've followed this video and chrissainty/ondotnet-tailwindcss to configure Tailwind with the JIT and I'm pretty happy!
However, I wanted to take advantage of postcss-import to include multiple css files. Here's what my app.css looks like:
#import "tailwindcss/base";
#import "tailwindcss/components";
#import "tailwindcss/utilities";
#import "./other.css";
However, in the generated css I see generated code for Tailwind, but not for my other.css - it's still #import "./other.css". Here's my folder structure:
./Styles/app.css
./Styles/other.css
./package.json
./postcss.config.js
./tailwind.config.js
This is my postcss command:
cross-env TAILWIND_MODE=build postcss ./Styles/app.css -o ./wwwroot/css/app.css
I'm assuming that the problem is something to do with the Current Working Directory? I've tried the following variants for my #import statement:
#import ./Styles/other.css
#import ../Styles/other.css
#import Styles/other.css
But the text substitution doesn't happen. I've tried this with and without postcss-import in the devDependencies of the package.json.
I've included the contents of the config files, just in case.
package.json:
{
"scripts": {
"buildcss:dev": "cross-env TAILWIND_MODE=build postcss --verbose ./Styles/app.css -o ./wwwroot/css/app.css",
"buildcss:release": "cross-env NODE_ENV=production postcss ./Styles/app.css -o ./wwwroot/css/app.css"
},
"devDependencies": {
"autoprefixer": "10.3.1",
"cross-env": "7.0.3",
"cssnano": "^5.0.6",
"postcss": "8.3.6",
"postcss-cli": "8.3.1",
"postcss-import": "^14.0.2",
"tailwindcss": "2.2.7"
},
"dependencies": {
"#tailwindcss/forms": "^0.3.3"
}
}
postcss.config.js:
// postcss.config.js
const purgecss = require('#fullhuman/postcss-purgecss')({
// Specify the paths to all of the template files in your project
content: [
'./**/*.html',
'./**/*.razor'
],
// Include any special characters you're using in this regular expression
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
})
// postcss.config.js
module.exports = {
plugins: [
require('postcss-import'),
require('tailwindcss'),
require('autoprefixer'),
...process.env.NODE_ENV === 'production'
? [purgecss]
: []
]
}
tailwind.config.js:
module.exports = {
mode: 'jit',
purge: [
'./**/*.razor',
'./**/*.cshtml'
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {
zIndex: {
'1': '1'
},
},
},
variants: {
extend: {
ringWidth: ['focus'],
borderWidth: ['focus'],
},
},
plugins: [
require('#tailwindcss/forms'),
],
}
Aha! It was the postcss config. I was looking at the wrong file. The original config was this:
module.exports = ({ env }) => ({
plugins: {
tailwindcss: {},
autoprefixer: {},
cssnano: env === "production" ? { preset: "default" } : false
}
});
It was missing the postcss-import plugin:
module.exports = ({ env }) => ({
plugins: {
tailwindcss: {},
autoprefixer: {},
'postcss-import': {},
cssnano: env === "production" ? { preset: "default" } : false
}
});

React craco tailwind postcss integration

My project use craco to start/build, I wish to integrate tailwind postcss.
I have follow this tutorial https://dev.to/ryandunn/how-to-use-tailwind-with-create-react-app-and-postcss-with-no-hassle-2i09
But as a result, I am suffering with this error:
My config files looks like below:
craco.config.js (this one looks like where problem lies, as I feel like the style may overriding craco style underneath, so it caused above error, but not 100% sure how to resolve this puzzl.)
module.exports = {
style: {
postcss: {
plugins: [
require("tailwindcss")("./tailwind.config.js"),
require("postcss-nested"),
require("autoprefixer"),
],
},
},
postcss.config.js
const tailwindcss = require('tailwindcss');
module.exports = {
plugins: [
tailwindcss('./tailwind.js'),
require('autoprefixer')
],
};
tailwind.config.js
module.exports = {
purge: ["./src/**/*.html", "./src/**/*.jsx", "./src/**/*.js", "./src/**/*.tx", "./src/**/*.tsx"],
theme: {
extend: {
screens: {
xs: { max: "400px" },
},
},
},
};
I am running the app from
docker-compose up
Any suggestions to fix the error and config?
Please advise with some code example
Thanks in advance
You need to uninstall tailwindcss postcss and autoprefixer. Then re install them with these specific versions
npm install tailwindcss#npm:#tailwindcss/postcss7-compat postcss#^7 autoprefixer#^9
I had the same issue! There is more info in this link. https://tailwindcss.com/docs/installation#post-css-7-compatibility-build

Using tailwindcss with React, size not decreased after purging

I'm tried to set up tailwind with ejected create-react-app. I'm successful to make it works but failed to purge the size. here is my setup
./src/assets/styles/tailwind.css
#tailwind base;
#tailwind components;
#tailwind utilities;
postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer')
],
};
tailwind.config.js
module.exports = {
purge: ["./src/**/*.js"],
theme: {
extend: {}
},
variants: {},
plugins: []
};
package.json
"scripts": {
"start": "node scripts/start.js && postcss src/assets/styles/tailwind.css -o src/assets/styles/main.css -w",
"build": "node scripts/build.js && postcss src/assets/styles/tailwind.css -o src/assets/styles/main.css"
}
index.js
import "./assets/styles/main.css";
// ...
I tried to create a component like this and its work
<div className="w-64 h-64 bg-red-200">hai</div>
but when I build, even I have given a path to purge at the config, the size not decreasing. It constant 143kb whether I add the purge path or not. i also have tried manual purge like this at postcss.config.js but no work
// postcss.config.js
const purgecss = require("#fullhuman/postcss-purgecss")({
// Specify the paths to all of the template files in your project
content: [
"./src/**/*.js"
// etc.
],
// This is the function used to extract class names from your templates
defaultExtractor: (content) => {
// Capture as liberally as possible, including things like `h-(screen-1.5)`
const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || [];
// Capture classes within other delimiters like .block(class="w-1/2") in Pug
const innerMatches = content.match(/[^<>"'`\s.()]*[^<>"'`\s.():]/g) || [];
return broadMatches.concat(innerMatches);
}
});
module.exports = {
plugins: [
require("tailwindcss"),
require("autoprefixer"),
...(process.env.NODE_ENV === "production" ? [purgecss] : [])
]
};
whats wrong with my setup?
With the latest versions, starting at 1.4.0, you don't need to manually configure PurgeCSS.
// postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer')
]
}
//tailwind.config.js
module.exports = {
purge: [
'./src/**/*.js'
],
theme: {
extend: {},
},
variants: {},
plugins: [],
}
// package.json
"scripts": {
"start": "npm run build:css && react-scripts start",
"build": "NODE_ENV=production npm run build:css && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"build:css": "postcss tailwind.css -o src/main.css"
},
Note the NODE_ENV=production.
The documentation says:
Now whenever you compile your CSS with NODE_ENV set to production, Tailwind will automatically purge unused styles from your CSS.
I also have the same problem. This helped me:
https://tailwindcss.com/docs/optimizing-for-production#enabling-manually
I have to manually add this code in my tailwind.config.js file:
// tailwind.config.js
module.exports = {
purge: {
enabled: true,
content: ['./src/**/*.html'],
},
// ...
}
I hope it will be helpful.

How to load environment variables in React

I've been trying to load environment variables in React and I can't seem to figure it out. I have tried multiple aproaches:
Load them using the dotenv-webpack package
webpack.config.dev.js
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const template = require('html-webpack-template');
const Dotenv = require('dotenv-webpack');
const baseConfig = require('./webpack.config.base');
module.exports = merge(baseConfig, {
mode: 'production',
plugins: [
new HtmlWebpackPlugin({
template,
inject: false,
appMountId: 'app',
mobile: true,
lang: 'es-ES',
title: 'My App',
meta: [
{
name: 'description',
content: 'My App',
},
],
}),
new Dotenv(),
],
});
.env
API_HOST=http://localhost:8000
REACT_APP_API_HOST=http://localhost:8000
Passing it directly on the package.json script:
"start": "webpack-dev-server --config ./webpack.config.dev.js"
Using .env on the webpack command
webpack --env.API_HOST=http://localhost:8000
Using webpack.environmentPlugin
const webpack = require('webpack');
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const template = require('html-webpack-template');
const baseConfig = require('./webpack.config.base');
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'cheap-module-source-map',
devServer: {
publicPath: '/',
contentBase: './dist',
compress: true,
stats: 'minimal',
overlay: true,
historyApiFallback: true,
port: 8081,
hot: true,
},
plugins: [
new HtmlWebpackPlugin({
template,
devServer: 'http://localhost:8081',
inject: false,
appMountId: 'app',
mobile: true,
lang: 'es-ES',
title: 'My App',
meta: [
{
name: 'description',
content: 'React template.',
},
],
}),
new webpack.EnvironmentPlugin({
API_HOST: 'http://localhost:8000',
}),
],
});
None of this approaches work and when I try to access process.env variables in my React code I get undefined
Any ideas of what I could be doing wrong?
I've been fighting with environment variables myself for some time, when I wanted to provide settings to the Firebase project but not load them into the public repository.
As far as I know, you need to name you environment variables should always start with the prefix REACT_APP_. You can define them whereever you like, but if you created your app with create-react-app tool then you can put your variables in .env file, or a few other files - (https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables)
The pain for me started when I wanted to make my own files, because I had two different Firebase projects - one for staging and one for production.
I end up with using react-app-env module which helped with:
- defining my own files - staging.env and production.env
- auto prefix my variables with REACT_APP_
For example:
I have defined Firebase ApiKey in staging.env file:
API_KEY=xxxxxxxxxxxxxxxxxxx
when I use it in my firebase.js file, I use it as:
const config = {
apiKey: process.env.REACT_APP_API_KEY,
}
And to make sure that I develop against staging environment (Firebase project) I've changed my package.json to:
"scripts": {
"start": "react-app-env --env-file=staging.env start",
},
Hope that helps!
You need to specify the webpack config file correct. You will need to create a separate config for dev. (webpack.config.dev.js)
Example here.
scripts: {
"dev": "webpack --env.API_HOST=http://localhost:8000 --config webpack.config.dev.js"
}
Also, you need to use Webpack.DefinePlugin.
plugins: [
...
new webpack.DefinePlugin({ `process.env.API_HOST`: JSON.stringify(${env.API_HOST}) })
]
or you can use reduce to make it more comprehensive.
const envKeys = Object.keys(env).reduce((prev, next) => {
prev[`process.env.${next}`] = JSON.stringify(env[next]);
return prev;
}, {});
return {
plugins: [
...
new webpack.DefinePlugin(envKeys)
]
};
Agree with #Philip's answer, this is how I structure my dev.config.js
...
plugins: [
new webpack.DefinePlugin({
// process.env
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
WHY_DID_YOU_UPDATE: process.env.WHY_DID_YOU_UPDATE,
},
// my other global flags
__CLIENT__: true,
__SERVER__: false,
__DEVELOPMENT__: true,
__DEVTOOLS__: true
}),
]
I also use better-npm-run to manage my package.json, where you can easily manage the env variables
"betterScripts": {
"dev": {
"command": "concurrently --kill-others \"better-npm-run watch-client\" \"better-npm-run start-dev\" \"gulp watch --gulpfile semantic/gulpfile.js\""
},
"why-did-you-update": {
"command": "better-npm-run dev",
"env": {
"WHY_DID_YOU_UPDATE": true
}
},
"watch-client": {
"command": "node webpack/webpack-dev-server.js",
"env": {
"UV_THREADPOOL_SIZE": 100,
"NODE_ENV": "development",
"NODE_PATH": "./src",
"PORT": 3000
}
},
"build": {
"command": "webpack --verbose --colors --display-error-details --config webpack/prod.config.js"
}
},
Hope this information helps you too!
I find a simple solution for this. You need to install 'dotenv-webpack', then add this configuration to your webpack config:
const Dotenv = require('dotenv-webpack');
...
plugins: [
new Dotenv(),
],
...
.env
DB_HOST=127.0.0.1
DB_PASS=foobar
S3_API=mysecretkey
Finally you can access your env variables in your app
console.log(process.env.DB_PASS);
From the docs: the .env values for DB_HOST and S3_API are NOT present in our bundle, as they were never referenced (as process.env.[VAR_NAME]) in the code.
Hope it helps!
You can specify environment variables in the package.json scripts section:
{
...
"scripts": {
"start": NODE_ENV=development webpack-dev-server
},
...
}

React Dev Tools Thinks My Site is in Dev Mode

I want to see how much of a speed boost I get from using the non dev version of everything so I built my site using my "production" webconfig. but dev tools still is telling me it is in "development" mode
This page is using the development build of React.
const merge = require("webpack-merge");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const common = require("./webpack.common.js");
module.exports = merge(common, {
// Provides process.env.NODE_ENV with value production.
// Enables FlagDependencyUsagePlugin, FlagIncludedChunksPlugin,
// ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin,
// SideEffectsFlagPlugin and UglifyJsPlugin.
mode: "production",
// see https://webpack.js.org/configuration/optimization/
optimization: {
// minimize default is true
minimizer: [
// Optimize/minimize CSS assets.
// Solves extract-text-webpack-plugin CSS duplication problem
// By default it uses cssnano but a custom CSS processor can be specified
new OptimizeCSSAssetsPlugin({})
]
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
// only use MiniCssExtractPlugin in production and without style-loader
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"]
}
]
},
plugins: [
// Mini CSS Extract plugin extracts CSS into separate files.
// It creates a CSS file per JS file which contains CSS.
// It supports On-Demand-Loading of CSS and SourceMaps.
// It requires webpack 4 to work.
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
new BundleAnalyzerPlugin()
]
});
in my package.json
"scripts": {
"dev": "cross-env NODE_ENV=dev webpack-serve --config webpack.dev.js --open",
"prod": "cross-env NODE_ENV=prod webpack -p --config webpack.prod.js",
"qa": "cross-env NODE_ENV=QA webpack --config webpack.prod.js"
},
My confidence is growing that your problem stems from setting NODE_ENV to prod in your package.json. I think you should instead set it to production, or allow webpack to set it internally with the mode option in your webpack config.
The react-scripts package explicitly sets this value to production for building and searching for NODE_ENV in the facebook/react project in github project shows tons of checks for production as the value instead of prod.
Here's the index.js file for react-is, many other projects in the react source follow the same pattern:
'use strict';
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react-is.production.min.js');
} else {
module.exports = require('./cjs/react-is.development.js');
}
I would try changing your build script to:
"prod": "cross-env NODE_ENV=production webpack -p --config webpack.prod.js",
or even let webpack -p set it automatically:
"prod": "cross-env webpack -p --config webpack.prod.js",

Resources