bootstrap icon indicating copy to clipboard operation
bootstrap copied to clipboard

[DRAFT] Use postcss-drop-empty-css-vars to remove empty CSS variables

Open julien-deramond opened this issue 2 years ago • 3 comments

Warning Heavily draft

Linked to the discussion in https://github.com/twbs/bootstrap/pull/36597 and potentially addresses https://github.com/twbs/bootstrap/issues/36595. I've tried here to create a very simple PostCSS plugin to remove empty CSS vars.

Plugin repo: https://github.com/julien-deramond/postcss-drop-empty-css-vars Plugin code (very basic just to test the idea): https://github.com/julien-deramond/postcss-drop-empty-css-vars/blob/main/index.js

The plugin is deployed in 0.0.0 version

Running npm run css with and without this plugin gives this diff:

2801d2800
<   --bs-btn-font-family: ;
3506d3504
<   --bs-dropdown-box-shadow: ;
3620d3617
<   --bs-nav-link-font-weight: ;
3794d3790
<   --bs-nav-link-font-weight: ;
4168d4163
<   --bs-card-box-shadow: ;
4173,4175d4167
<   --bs-card-cap-color: ;
<   --bs-card-height: ;
<   --bs-card-color: ;
4482,4483d4473
<   --bs-breadcrumb-bg: ;
<   --bs-breadcrumb-border-radius: ;
5191d5180
<   --bs-toast-color: ;
5257d5245
<   --bs-modal-color: ;
5271d5258
<   --bs-modal-footer-bg: ;
5549d5535
<   --bs-tooltip-margin: ;
6094d6079
<   --bs-offcanvas-color: ;

/CC @mdo for thoughts

julien-deramond avatar Jul 14 '22 16:07 julien-deramond

A simple sidenote here: automating this would prevent us from using advanced usages based on the space toggle technique.

I don't think Bootstrap will ever use this, but that's to be mentionned on your PostCSS plugin's Doc at least 😉

ffoodd avatar Jul 15 '22 05:07 ffoodd

Might be worth opening an issue upstream in clean-css (if there isn't any already). Maybe they could implement this there too.

XhmikosR avatar Jul 19 '22 17:07 XhmikosR

Might be worth opening an issue upstream in clean-css (if there isn't any already). Maybe they could implement this there too.

Yes good idea, I need to dig into their repo. https://github.com/clean-css/clean-css/issues/1180 might give some clues (+ https://github.com/clean-css/clean-css/commit/a6da53a8ada78678ec2502ed50a4977f4e668b9e)

julien-deramond avatar Jul 20 '22 05:07 julien-deramond

I love the idea of this. I attempted to implement in Webpack but after building (npm run build), I still see the empty variables in the outputted CSS. The empty variables I see are:

bs-btn-font-family bs-dropdown-box-shadow bs-nav-link-font-weight bs-nav-link-font-weight bs-card-box-shadow bs-card-cap-color bs-card-height bs-card-color bs-breadcrumb-bg bs-breadcrumb-border-radius bs-toast-color bs-modal-color bs-modal-footer-bg bs-tooltip-margin bs-popover-header-color bs-offcanvas-color

PyCharm shows red squigglies saying "Term expected".

My webpack.config.js:

const Path = require('path');
// const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

var dev_config = {
	cache: false,
	mode: 'development',
	devtool: 'source-map',
	devServer: {
		static: Path.resolve(__dirname, 'dist'),
		port: 8080,
		hot: true
	},
	entry: {
		app: Path.resolve(__dirname, 'src/app.js'),
	},
	output: {
		path: Path.join(__dirname, 'dist'),
		filename: 'js/[name].js',
		clean: true,
	},
	optimization: {
		splitChunks: {
			chunks: 'all',
			name: false,
		},
	},
	plugins: [
		new MiniCssExtractPlugin({filename: 'css/app.css'}),
		new CopyPlugin({
			patterns: [
				{from: "src/index.html", to: "index.html"},
			],
		}),
		// new HtmlWebpackPlugin({
		// 	title: "Generated by Webpack",
		// 	filename: 'index.html',
		// 	inject: 'body',
		// }),
	],
	module: {
		rules: [
			{
				test: /\.js$/,
				include: Path.resolve(__dirname, '../src'),
				loader: 'babel-loader',
			},
			{
				test: /\.(scss)$/,
				use: [{
					// inject CSS to page
					// loader: 'style-loader'
					loader: MiniCssExtractPlugin.loader
				},
				{
					// translates CSS into CommonJS modules
					loader: 'css-loader'
				},
				{
					// Run postcss actions
					loader: 'postcss-loader',
					options: {
						// `postcssOptions` is needed for postcss 8.x;
						// if you use postcss 7.x skip the key
						postcssOptions: {
							// postcss plugins, can be exported to postcss.config.js
							plugins: function () {
								return [
									require('postcss-drop-empty-css-vars'),
									require('autoprefixer'),
								];
							}
						}
					}
				},
				{
					// compiles Sass to CSS
					loader: 'sass-loader'
				}]
			},
			{
				test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
				type: 'asset'
			},
		],
	},
};

module.exports = dev_config;

// console.log(JSON.stringify(dev_config, null, 2));

My package.json

{
  "name": "bootstrap-missing-variables",
  "scripts": {
    "serve": "webpack serve --mode development",
    "build": "webpack --config webpack.config.js",
    "nodemon": "nodemon --watch webpack.config.js --delay 500ms --exec npm run build"
  },
  "devDependencies": {
    "autoprefixer": "^10.4.14",
    "babel-loader": "^9.1.2",
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^6.7.3",
    "html-webpack-plugin": "^5.5.1",
    "mini-css-extract-plugin": "^2.7.1",
    "nodemon": "^2.0.22",
    "postcss": "^8.4.23",
    "postcss-drop-empty-css-vars": "^0.0.0",
    "postcss-loader": "^7.3.0",
    "sass": "^1.62.1",
    "sass-loader": "^13.2.2",
    "style-loader": "^3.3.2",
    "webpack": "^5.81.0",
    "webpack-cli": "^5.0.2",
    "webpack-dev-server": "^4.13.3"
  },
  "dependencies": {
    "@popperjs/core": "^2.11.7",
    "bootstrap": "^5.2.3",
    "jquery": "^3.6.4"
  }
}

Wondering if I am doing something wrong. Apologies if this is something "a work in progress" and not yet ready to use. It seems to be exactly what I need.

jaradc avatar May 01 '23 17:05 jaradc

@jaradc Indeed it's something WIP, the postcss-drop-empty-css-vars package was released as a 0.0.0 as a test (IDK yet if we're going to use this package in Bootstrap, but if needed I can release a 0.1.0).

However, if you still want to test it out with Webpack and Bootstrap, the following seems to work: https://stackblitz.com/edit/github-t4mv5h.

To build it, I followed this path:

  1. Start from https://github.com/twbs/examples/tree/main/webpack/
  2. Add "postcss-drop-empty-css-vars": "^0.0.0", as a dep dependency in the package.json
  3. In webpack.config.js
  • Comment the const autoprefixer = require('autoprefixer'); import
  • Change the postcss-loader approach to have the following:
{
  // Loader for webpack to process CSS with PostCSS
  loader: 'postcss-loader',
  options: {
    postcssOptions: {
      plugins: [
        ['postcss-drop-empty-css-vars', {}],
        ['autoprefixer', {}],
      ],
    },
  },
},

When you run the project, click on "Toggle offcanvas" and use the dev tools to check the CSS of the offcanvas, there's no empty --bs-offcanvas-color.

julien-deramond avatar May 03 '23 06:05 julien-deramond

Thank you Julien-Deramond for such a clear response. I did everything you said and did a text compare before/after to confirm that all 16 empty CSS variables were in-fact removed from the Webpack build. Worked for me!

jaradc avatar May 22 '23 16:05 jaradc