CMS Development

Destnel
Participant

CLI upload CSS file size limitations when integrating Tailwind

SOLVE

I'm in the process of integrating Tailwind into my project and am running into the following error when uploading to my staging/dev environment:

[ERROR] Error validating template.
[ERROR] line 0: Coded files must be smaller than 1.5 MiB

 

Tailwind, minified, is ~3 MiB (which is large, but I've worked in larger files). When building to  my production environment I use PurgeCSS, and the file size drops to below 10 KiB, but in order to develop my site effectively I need the entire tailwind library so I don't have to rebuild, purge, and upload the Tailwind CSS library every time I add a net-new class to an element.

 

I've tried Gzipping the Tailwind CSS file, but Hubspot doesn't allow .gz filetypes, so that seems to be off the table. I thought about using Tailwind's CDN, but that doesn't help much because the CDN doesn't have my important customizations.

Has anyone else had trouble with this limitation, and is there a work-around?

0 Upvotes
1 Accepted solution
Destnel
Solution
Participant

CLI upload CSS file size limitations when integrating Tailwind

SOLVE

After some work, I came up with a solution that works for our team.

Just to reiterate/clarify the goals here, I was trying to do the following:

 

  1. Integrate the Tailwind library into our hubspot website with Webpack
  2. Be able to use Webpack's --watch command to recompile our CSS on save so we can use CSS nesting and Tailwind's @apply rule (among other PostCSS goodies)
  3. Be able to use the Hubspot CLI watch command and a Hubspot sandbox environment to semi-hot-reload our work, on save
  4. Integrate our project with git versioning but on Bitbucket, rather than Github

 

Our primary issue was the Tailwind library uncompressed and unpurged was way too large to upload to the Hubspot sandbox since there is an upload file limit of 1.5 MiB and the full Tailwind library is ~3 MiB, minified. To get around this issue, we used Tailwind's built in purgeCSS which brought our file size down to below 20 KiB—easily handled by Hubspot's watch.

 

The problem at this point was that Webpack was not recompiling the CSS when we added net-new Tailwind classes to our elements. In order to get those classes, we had to restart the Webpack --watch or rebuild, which was just annoying, especially when trying to iterate classes quickly.

 

To solve this, we added a the extra-watch-webpack-plugin and dropped the Tailwind purgeCSS in favor of the purgecss-webpack-plugin which can live outside postCSS—where we were doing our other CSS optimizations. This method (while a bit slower than we were hoping for due to the purging) recomiled our CSS and added new Tailwind classes on save, then successfully uploaded those files through the CMS CLI to our development sandbox.

 

Our Webpack setup, for those who are curious:

 

webpack.common.js

 

const path = require('path')
const glob = require('glob')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const entry = require('webpack-glob-entry')
const ExtraWatchWebpackPlugin = require('extra-watch-webpack-plugin')
const PurgeCSSPlugin = require('purgecss-webpack-plugin')

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

module.exports = {
	entry: entry('./assets/js/main.js'),
	output: {
		path: path.resolve(__dirname, 'dist'),
		filename: '[name].js',
	},
	plugins: [
		new ExtraWatchWebpackPlugin({
			dirs: ['./templates'],
		}),
		new PurgeCSSPlugin({
			defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || [],
			paths: glob.sync(`${PATHS.src}/*.html`,  { nodir: true }),
		}),
		new MiniCssExtractPlugin({
			filename: "[name].min.css"
		})
	]
}

 

 

webpack.dev.js

 

const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = merge(common, {
	mode: 'development',
	devtool: 'source-map',
	watch: true,
	module: {
		rules: [
			{
				test: /\.css$/i,
				use: [
					'style-loader',
					MiniCssExtractPlugin.loader,
					{
						loader: 'css-loader',
						options: {
							sourceMap: true,
							importLoaders: true
						}
					},
					{
						loader: 'postcss-loader',
						options: {
							postcssOptions: {
								map: true,
								plugins: [
									require('postcss-import'),
									require('tailwindcss'),
									require('postcss-nested')
								],
							},
							sourceMap: true
						}
					},
				],
			},
		],
	}
})

 

 

 webpack.prod.js

 

const path = require('path')
const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = merge(common, {
	mode: 'production',
	module: {
		rules: [
			{
				test: /\.css$/,
				exclude: /node_modules/,
				use: [
					'style-loader',
					MiniCssExtractPlugin.loader,
					{
						loader: 'css-loader',
						options: {
							importLoaders: true
						}
					},
					{
						loader: 'postcss-loader',
						options: {
							postcssOptions: {
								map: false,
								plugins: [
									require('postcss-import'),
									require('tailwindcss'),
									require('postcss-nested'), // or require('postcss-nesting')
									require('autoprefixer'),
									require('cssnano')
								],
							},
						}
					},
				],
			},
			{
				test: /\.(mjs|js|jsx)$/,
				use: [
					{
						loader: 'babel-loader',
						options: {
							presets: [
								'@babel/preset-env',
								{
									'plugins': [
										'@babel/plugin-proposal-class-properties'
									]
								}
							]
						}
					}
				],
				exclude: path.resolve(__dirname, "node_modules")
			},
		]
	}
})

 

 

package.json scripts

 

  "scripts": {
    "watch": "webpack --mode=development --config=webpack.dev.js --progress",
    "build": "rm -rf dist && webpack --mode=production --config=webpack.prod.js"
  },

 

 

And for those who are curious about our Bitbucket Pipelines integration:

 

bitbucket-pipelines.yml

 

image: node:12.16.1

pipelines:
  branches:
    master:
      - step:
          name: Generate hubspot.config.yml and deploy
          caches:
            - node
          script:
              - npm install
              - npm run build
              - ./generate-config.js
              - ./deploy.sh

 

 

deploy.sh

 

#!/bin/bash
npx hs upload ../build your-site-name

 

 

And, finally, our Tailwind config:

 

tailwind.config.js

 

const colors = require('tailwindcss/colors')

module.exports = {
  purge: ['./**/*.html'],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {
      colors: {
        gray: colors.blueGray
      }
    },
  },
  variants: {
    backgroundColor: ['responsive', 'hover', 'focus', 'active'],
    color: ['responsive', 'hover', 'focus', 'active'],
    borderColor: ['responsive', 'hover', 'focus', 'active'],
  },
  plugins: [
    require('@tailwindcss/typography'),
    require('@tailwindcss/forms')
  ],
}

 

 

 

View solution in original post

4 Replies 4
Destnel
Solution
Participant

CLI upload CSS file size limitations when integrating Tailwind

SOLVE

After some work, I came up with a solution that works for our team.

Just to reiterate/clarify the goals here, I was trying to do the following:

 

  1. Integrate the Tailwind library into our hubspot website with Webpack
  2. Be able to use Webpack's --watch command to recompile our CSS on save so we can use CSS nesting and Tailwind's @apply rule (among other PostCSS goodies)
  3. Be able to use the Hubspot CLI watch command and a Hubspot sandbox environment to semi-hot-reload our work, on save
  4. Integrate our project with git versioning but on Bitbucket, rather than Github

 

Our primary issue was the Tailwind library uncompressed and unpurged was way too large to upload to the Hubspot sandbox since there is an upload file limit of 1.5 MiB and the full Tailwind library is ~3 MiB, minified. To get around this issue, we used Tailwind's built in purgeCSS which brought our file size down to below 20 KiB—easily handled by Hubspot's watch.

 

The problem at this point was that Webpack was not recompiling the CSS when we added net-new Tailwind classes to our elements. In order to get those classes, we had to restart the Webpack --watch or rebuild, which was just annoying, especially when trying to iterate classes quickly.

 

To solve this, we added a the extra-watch-webpack-plugin and dropped the Tailwind purgeCSS in favor of the purgecss-webpack-plugin which can live outside postCSS—where we were doing our other CSS optimizations. This method (while a bit slower than we were hoping for due to the purging) recomiled our CSS and added new Tailwind classes on save, then successfully uploaded those files through the CMS CLI to our development sandbox.

 

Our Webpack setup, for those who are curious:

 

webpack.common.js

 

const path = require('path')
const glob = require('glob')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const entry = require('webpack-glob-entry')
const ExtraWatchWebpackPlugin = require('extra-watch-webpack-plugin')
const PurgeCSSPlugin = require('purgecss-webpack-plugin')

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

module.exports = {
	entry: entry('./assets/js/main.js'),
	output: {
		path: path.resolve(__dirname, 'dist'),
		filename: '[name].js',
	},
	plugins: [
		new ExtraWatchWebpackPlugin({
			dirs: ['./templates'],
		}),
		new PurgeCSSPlugin({
			defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || [],
			paths: glob.sync(`${PATHS.src}/*.html`,  { nodir: true }),
		}),
		new MiniCssExtractPlugin({
			filename: "[name].min.css"
		})
	]
}

 

 

webpack.dev.js

 

const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = merge(common, {
	mode: 'development',
	devtool: 'source-map',
	watch: true,
	module: {
		rules: [
			{
				test: /\.css$/i,
				use: [
					'style-loader',
					MiniCssExtractPlugin.loader,
					{
						loader: 'css-loader',
						options: {
							sourceMap: true,
							importLoaders: true
						}
					},
					{
						loader: 'postcss-loader',
						options: {
							postcssOptions: {
								map: true,
								plugins: [
									require('postcss-import'),
									require('tailwindcss'),
									require('postcss-nested')
								],
							},
							sourceMap: true
						}
					},
				],
			},
		],
	}
})

 

 

 webpack.prod.js

 

const path = require('path')
const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = merge(common, {
	mode: 'production',
	module: {
		rules: [
			{
				test: /\.css$/,
				exclude: /node_modules/,
				use: [
					'style-loader',
					MiniCssExtractPlugin.loader,
					{
						loader: 'css-loader',
						options: {
							importLoaders: true
						}
					},
					{
						loader: 'postcss-loader',
						options: {
							postcssOptions: {
								map: false,
								plugins: [
									require('postcss-import'),
									require('tailwindcss'),
									require('postcss-nested'), // or require('postcss-nesting')
									require('autoprefixer'),
									require('cssnano')
								],
							},
						}
					},
				],
			},
			{
				test: /\.(mjs|js|jsx)$/,
				use: [
					{
						loader: 'babel-loader',
						options: {
							presets: [
								'@babel/preset-env',
								{
									'plugins': [
										'@babel/plugin-proposal-class-properties'
									]
								}
							]
						}
					}
				],
				exclude: path.resolve(__dirname, "node_modules")
			},
		]
	}
})

 

 

package.json scripts

 

  "scripts": {
    "watch": "webpack --mode=development --config=webpack.dev.js --progress",
    "build": "rm -rf dist && webpack --mode=production --config=webpack.prod.js"
  },

 

 

And for those who are curious about our Bitbucket Pipelines integration:

 

bitbucket-pipelines.yml

 

image: node:12.16.1

pipelines:
  branches:
    master:
      - step:
          name: Generate hubspot.config.yml and deploy
          caches:
            - node
          script:
              - npm install
              - npm run build
              - ./generate-config.js
              - ./deploy.sh

 

 

deploy.sh

 

#!/bin/bash
npx hs upload ../build your-site-name

 

 

And, finally, our Tailwind config:

 

tailwind.config.js

 

const colors = require('tailwindcss/colors')

module.exports = {
  purge: ['./**/*.html'],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {
      colors: {
        gray: colors.blueGray
      }
    },
  },
  variants: {
    backgroundColor: ['responsive', 'hover', 'focus', 'active'],
    color: ['responsive', 'hover', 'focus', 'active'],
    borderColor: ['responsive', 'hover', 'focus', 'active'],
  },
  plugins: [
    require('@tailwindcss/typography'),
    require('@tailwindcss/forms')
  ],
}

 

 

 

DKR91
Participant

CLI upload CSS file size limitations when integrating Tailwind

SOLVE

Looking great, but one question.

 

How did you manage the seperate Hubspot modules?  They all have there own scss and css files and I do like that seperation between the modules and main css file.

 

Have you found a way to actually process each module.scss file to a .css file and then convert those @apply tailwind classes inside that module.css file?

0 Upvotes
dennisedson
HubSpot Product Team
HubSpot Product Team

CLI upload CSS file size limitations when integrating Tailwind

SOLVE

@Jake_Lett, @John , @stefen 

Any chance you have some ideas for our new Community member, @Destnel  😀

0 Upvotes
stefen
Key Advisor | Partner
Key Advisor | Partner

CLI upload CSS file size limitations when integrating Tailwind

SOLVE

@dennisedson @Destnel I highly recommend not using the non-optimized files. Instead, you can use the HubSpot CLI to watch and automatically upload your compiled files pretty easily.

Stefen Phelps, Community Champion, Kelp Web Developer