PurgeCSS whitelist patterns with TailwindCSS - css

I am trying to preserve all TailwindCSS colour classes (i.e bg-green, bg-red, text-green, text-red) when it is processed via PurgeCSS. These colour classes are set in the CMS rather than code so we cannot search the code for them as they don't (all) exist here.
Therefore I want to use the whitelisting feature of PurgeCSS to retain all classes that beging with 'bg-' or 'text-'. However, the pattern I have below doesn't seem to be doing the trick? Any ideas how to tweak it?
whitelistPatterns: ['^bg\-', '^text\-'],

The issue appears to be simply to use regexp, not a string.
whitelistPatterns: [/^bg-/, /^text-/], // Retain all classes starting with...

If you run newer versions of tailwind: whitelist and whitelistPatterns merged into safelist. This info cost me a day of research.
purge: {
options: {
safelist: ["bg-red-50"],
},
// ... or even
options: {
safelist: [/^bg-/, /^text-/]
},
}

purge: {
options: {
safelist: ["whitelisted"],
},
// ...
}

In TailwindCSS version 3.0.24 it is integrated a bit differently.
// Example tailwind.config.js (see my config file)
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
safelist: [
'text-2xl',
'text-3xl',
{
pattern: /bg-(red|green|blue)-(100|200|300)/,
variants: ['lg', 'hover', 'focus', 'lg:hover'],
},
],
// ...
}
Source: Documentation

Im doing it based on official suggestion like this:
whitelistPatterns: [/\-blue\-/],
whitelistPatterns: [/\-pink\-/],
...etc
Selectors with ending or starting it does not fit your needs.
Think this one
.xl\:hover\:bg-pink-900:hover
or this one
.xl\:bg-cover

Based on this tailwind documentation, using something like this worked for a similar problem I had. The '+' works as wildcard.
module.exports = {
// ...
safelist: [
{
pattern: /bg-+/
},
{
pattern: /text-+/
},
],
// ...

Related

TailwindCSS doesn't purge plugin classes

I was trying to reduce the size of my CSS file using Tailwind's in-house compiler. This is my config file:
module.exports = {
content: ["./pages/*.html", "./components/**/*", "./src/*"],
mode: "jit",
plugins: [
require("tailwindcss"),
require("tw-elements/dist/plugin"),
require('tailwind-scrollbar')({ nocompatible: true }),
function ({ addVariant }) {
addVariant("child", "& > *");
addVariant("child-hover", "& > *:hover");
},
],
},
};
I have checked that the directories specified in content don't contain any hidden classes, however, it seems that Tailwind includes every class from the package tw-elements and doesn't purge them.
Is there any way how I can get a clean CSS file?
Many thanks for your help!
It is possible to reduce the size of your CSS file using Tailwind's in-house compiler. You can add the purge option to your Tailwind configuration file. Additionally, you can use the whitelist option to specify a list of classes that should not be purged. For example:
module.exports = {
content: ["./pages/*.html", "./components/**/*", "./src/*"],
mode: "jit",
purge: {
content: ['./pages/*.html', './components/**/*', './src/*'],
whitelist: ['tw-elements']
}
....
}

webpack: understanding source maps when working with CSS

Introduction
I have already setup bundling for my Javascript files with webpack in my project. Now I am in the process of adding CSS files to the webpack configuration. So far, I have been including the CSS files manually in the HTML header by adding <link> elements for every CSS file I depend on (e.g. bootstrap, my own css, etc.). Obviously this is not very elegant and using webpack would be much better, so I would like to replace the link elements and bundle them via webpack.
This should be easy, everything is pretty much documented in the webpack documentation. After reading the documentation and experimenting a bit with webpack I have arrived at the configuration below which already works.
Problem
The problem with my current setup is that I would like to have proper source map support and that does not seem to work. By proper, I mean that I expect that when I run a development build with webpack and I inspect some element in Chrome DevTools, that I will see from which file and which line in the file a certain CSS class originated and that I can click on the CSS rules and the browser jumps to that file.
I do not want to have inline styles in the head element, because then the browser will show something like .foobar { <style>..</style>, rather then .foobar { app.css:154.
With my current setup I have all CSS files combined (but not minified) into one app.css file. This means that if I inspect a bootstrap class such as .btn then it appears as .btn { app.css:3003. However, what I want to achieve is that the browser shows it as .btn { bootstrap.css:3003.
So now I am trying to understand how webpack and the different plugins such as css-loader and min-css-extract-plugin apply CSS source maps, and how I can configure them to achieve a proper debugging experience.
I am not sure how relevant this is, but when I navigate in DevTools under Sources to webpack://./bootstrap/dist/css/bootstrap.css I see that it only contains a single line:
// extracted by mini-css-extract-plugin.
Webpack Setup
index.js:
window.jQuery = require('jquery/dist/jquery');
require('bootstrap/dist/css/bootstrap.css');
require('bootstrap/dist/js/bootstrap');
/* other dependencies */
webpack.config.js:
const devMode = process.env.NODE_ENV !== 'production';
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module: {
rules: [
{ /* Javascript rules excluded */ },
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
plugins: [
new UglifyJSPlugin (),
new HtmlWebpackPlugin({
template: 'app/index.tpl.html'
}),
new MiniCssExtractPlugin({ filename: devMode ?
'[name].css' :
'[name].[hash].css'
})
],
Conclusion
It seems I just passed the rubber duck test. While I was writing this I arrived at a solution. I will still publish the question, maybe it can help others.
The problem was that I was also using the mini-css-extract-plugin for development and not just for production. I thought that I needed to do that, because when at first I was using the style-loaded I would get styles included in the header and the browser would show me all styles as .foobar { <style>..</style>.
However, the actual problem seemed to be, that I was not using devtools. So the solution was to add devtool: devMode ? 'cheap-module-eval-source-map' : 'source-map', to the webpack configuration to conditionally use the style-loader plugin during development builds and mini-css-extract-plugin during production builds.
webpack.config.js
{
test: /\.css$/,
use: [
{
- loader: MiniCssExtractPlugin.loader,
+ loader: devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
},
/* ... */
+ devtool: devMode ? 'cheap-module-eval-source-map' : 'source-map',

How do you configure Create React App to use CSS Blocks?

CSS blocks just went open source and I wanted to incorporate it into my React app while still using the boilerplate CRA webpack because I want to keep all the other functionality.
From what I understand the majority of the configuration is simply adding another babel-loader with the css-block plugin.
So instead of just:
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
},
},
You follow it with:
{
test: /\.[j|t]s(x?)$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve('babel-loader'),
options: {
presets: [require.resolve("babel-preset-react-app")],
cacheDirectory: true,
compact: true,
}
},
// Run the css-blocks plugin in its own dedicated loader because the react-app preset
// steps on our transforms' feet. This way, we are guaranteed a clean pass before any
// other transforms are done.
{
loader: require.resolve('babel-loader'),
options: {
plugins: [
require("#css-blocks/jsx/dist/src/transformer/babel").makePlugin({ rewriter: CssBlockRewriter }),
],
cacheDirectory: true,
compact: true,
parserOpts: {
plugins: [
"jsx",
"doExpressions",
"objectRestSpread",
"decorators",
"classProperties",
]
}
}
},
But, I cannot for the life of me get the second bit to parse anything. It's like it doesn't even exist and my CSS modules are just being referenced inside the class. Result ends up being like:
<div class="/src/test.css">
instead of
<div class="a b cD">
If anyone has any pointers of where I should try to look I would greatly appreciate it!
P.S. For reference I'll include links to the docs below since it's very new
http://css-blocks.com/
https://github.com/linkedin/css-blocks/blob/master/packages/%40css-blocks/website/config/webpack.config.dev.js
I get the example from Linkedin example /website and works fine with react-create-app scripts.
Fallow boilerplate extracted from CSS Blocks by me ralfting/boilerplate-css-blocks... Maybe this help you.

How to remove unused CSS using webpack 4?

I am having problem removing unused CSS in webpack 4. It seems that most of the CSS purification plugins are dependent on extract text plugin which is not updated for version 4.
Here's my commands:
node_modules/.bin/webpack --mode=development --env.operation=separateCSS
OR
node_modules/.bin/webpack --mode=development --env.operation=bundleCSS
Here's part of my webpack.config.js:
rules: [
// Loader for SASS files
{
test: /\.s[ac]ss$/,
use: [
// Use IIFE to choose how to emit the CSS: 1. Extract as separate file 2: Bundle with the JS file
(() => {
return env.operation == "separateCSS" ? MiniCssExtractPlugin.loader : 'style-loader';
})(),
{
loader: 'css-loader',
options: {
importLoaders: 1,
url: true
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
// Write future-proof CSS and forget old preprocessor specific syntax.
// Use the latest CSS syntax today with cssnext.
// It transforms CSS specs into more compatible CSS so you don’t need to wait for browser support.
// It parses CSS and add vendor prefixes to CSS rules using values from Can I Use.
// https://github.com/MoOx/postcss-cssnext
require('postcss-cssnext')()
]
}
},
'sass-loader'
]
}
],
plugins: [
new MiniCssExtractPlugin({
filename: "../css/[name].css"
})
]
Does anybody know how can I modify my config file so webpack can remove unused CSS?
Have you considered using something called uncss. There's a webpack plugin for it. It will look through all your CSS and compare it to your HTML, and remove anything that you're not using.
Take a look: WebPack UnCSS

bower concat CSS problems

Grunt novice here....what I am trying to do seems so simple, but I am at my wits end here. I am trying to concat the JS from a few separate bower components and then do the same with the CSS. Here is the relevant code from my grunt.file:
bower_concat: {
all: {
dest: 'builds/development/js/_bower.js',
cssDest: 'builds/development/css/_bower.css'
}
}
This is the last item in my config so does not need a comma after the final "}".
All the needed files are listed under "main" in their respective bower.json files. For example:
"main": [
"dist/owl.carousel.js",
"dist/assets/owl.carousel.css",
"dist/assets/owl.theme.css",
"dist/assets/owl.transitions.css"
],
I am positive these paths and file names are correct. The JS concats fine. The CSS does nothing. If I remove the "dest:..." line from my gruntfile (in an attempt to concat ONLY the CSS) terminal gives me the error ":Warning: You should specify "dest" and/or "cssDest" properties in your Gruntfile".
I clearly am specifying this. Help!
Finally got it to work with this:
bower_concat: {
all: {
dest: {
js: 'builds/development/js/_bower.js',
css: 'builds/development/css/_bower.css'
},
},
}
Essentially needed one more set of nested curly braces inside of "dest:". For the record you DO NOT need to specify mainFiles if they are designated in the bower_components json.
Ah, easy. You need to specify the component or library and then its mainFiles in your Gruntfile under grunt-bower-concat. Don't worry about messing with the individual components' files.
bower_concat: {
all: {
dest: 'builds/development/js/_bower.js',
cssDest: 'builds/development/css/_bower.css'
}
mainFiles: [
owlcarousel: [
"dist/owl.carousel.js",
"dist/assets/owl.carousel.css",
"dist/assets/owl.theme.css",
"dist/assets/owl.transitions.css"
],
],
}
FYI My current bower-concat for owlcarousel looks like this so double-check your bower_components folder tree structure.
bower_concat: {
all: {
dest: 'builds/development/js/_bower.js',
cssDest: 'builds/development/css/_bower.css'
}
mainFiles: [
owlcarousel: [
"owl-carousel/owl.carousel.js",
"owl-carousel/owl.carousel.css",
"owl-carousel/owl.theme.css",
"owl-carousel/owl.transitions.css"
], // (Version 1.3.2)
],
}

Resources