CSShighoptimization
Minify all CSS files
rule · css-minification
CSS minification removes unnecessary characters like whitespace, comments, and redundant code to reduce file sizes and improve loading performance.
Code Examples
Unminified CSS (Development)
CSS
/* Main navigation styles */
.navigation {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #ffffff;
padding: 1rem 2rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.navigation ul {
display: flex;
list-style: none;
margin: 0;
padding: 0;
gap: 2rem;
}
.navigation li a {
color: #333333;
text-decoration: none;
font-weight: 500;
transition: color 0.3s ease;
}
.navigation li a:hover {
color: #007bff;
}
/* Responsive design */
@media (max-width: 768px) {
.navigation {
flex-direction: column;
padding: 1rem;
}
.navigation ul {
margin-top: 1rem;
}
}Minified CSS (Production)
CSS
.navigation{display:flex;justify-content:space-between;align-items:center;background-color:#fff;padding:1rem 2rem;box-shadow:0 2px 4px rgba(0,0,0,.1)}.navigation ul{display:flex;list-style:none;margin:0;padding:0;gap:2rem}.navigation li a{color:#333;text-decoration:none;font-weight:500;transition:color .3s ease}.navigation li a:hover{color:#007bff}@media (max-width:768px){.navigation{flex-direction:column;padding:1rem}.navigation ul{margin-top:1rem}}Size Reduction: ~65% smaller (from ~600 bytes to ~215 bytes)
Why It Matters
Unminified CSS wastes bandwidth with whitespace, comments, and verbose syntax—minification delivers the same styles in significantly fewer bytes.
Build Tool Integration
Webpack with CSS Minification
JavaScript
// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
mode: 'production',
module: {
rules: [
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
chunkFilename: '[id].[contenthash].css'
})
],
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin({
minimizerOptions: {
preset: [
'default',
{
discardComments: { removeAll: true },
normalizeWhitespace: true,
colormin: true,
convertValues: true,
discardDuplicates: true
}
]
}
})
]
}
}Vite CSS Minification
JavaScript
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
build: {
cssMinify: 'lightningcss', // or 'esbuild'
rollupOptions: {
output: {
assetFileNames: (assetInfo) => {
const info = assetInfo.name.split('.')
const ext = info[info.length - 1]
if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(ext)) {
return `images/[name]-[hash][extname]`
}
if (/css/i.test(ext)) {
return `css/[name]-[hash][extname]`
}
return `assets/[name]-[hash][extname]`
}
}
}
},
css: {
postcss: {
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: 'default'
})
]
}
}
})PostCSS with cssnano
JavaScript
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer'),
...(process.env.NODE_ENV === 'production' ? [
require('cssnano')({
preset: [
'advanced',
{
discardComments: {
removeAll: true
},
normalizeWhitespace: true,
colormin: true,
convertValues: true,
discardDuplicates: true,
discardEmpty: true,
mergeRules: true,
minifySelectors: true,
reduceInitial: true,
reduceTransforms: true
}
]
})
] : [])
]
}Framework Examples
Next.js Automatic Minification
JavaScript
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// Next.js automatically minifies CSS in production
experimental: {
optimizeCss: true, // Enhanced CSS optimization
},
compiler: {
// Remove console logs in production
removeConsole: process.env.NODE_ENV === 'production',
},
webpack: (config, { dev, isServer }) => {
if (!dev && !isServer) {
// Additional CSS optimization for production
config.optimization.splitChunks.cacheGroups.styles = {
name: 'styles',
test: /\.(css|scss|sass)$/,
chunks: 'all',
enforce: true
}
}
return config
}
}
module.exports = nextConfigCreate React App Minification
JavaScript
// craco.config.js (for customizing CRA)
const CracoLessPlugin = require('craco-less')
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: {
'@primary-color': '#1DA57A',
},
javascriptEnabled: true,
}
},
miniCssExtractPluginOptions: {
ignoreOrder: true,
},
postcssLoaderOptions: {
postcssOptions: {
plugins: [
require('autoprefixer'),
...(process.env.NODE_ENV === 'production' ? [
require('cssnano')({
preset: 'default'
})
] : [])
]
}
}
}
}
]
}Vue.js CSS Minification
JavaScript
// vue.config.js
module.exports = {
css: {
extract: process.env.NODE_ENV === 'production' ? {
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css'
} : false,
sourceMap: process.env.NODE_ENV !== 'production'
},
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
// CSS optimization for production
config.optimization.splitChunks.cacheGroups.styles = {
name: 'styles',
test: /\.(css|vue)$/,
chunks: 'all',
enforce: true
}
}
},
chainWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.plugin('extract-css')
.use(require('mini-css-extract-plugin'), [{
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css'
}])
}
}
}CSS-in-JS Minification
Styled Components with Minification
JSX
import styled from 'styled-components'
// The styled-components babel plugin automatically minifies in production
const Navigation = styled.nav`
display: flex;
justify-content: space-between;
align-items: center;
background-color: white;
padding: 1rem 2rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
ul {
display: flex;
list-style: none;
margin: 0;
padding: 0;
gap: 2rem;
}
li a {
color: #333;
text-decoration: none;
font-weight: 500;
transition: color 0.3s ease;
&:hover {
color: #007bff;
}
}
@media (max-width: 768px) {
flex-direction: column;
padding: 1rem;
ul {
margin-top: 1rem;
}
}
`
// babel-plugin-styled-components config in .babelrc
{
"plugins": [
["babel-plugin-styled-components", {
"minify": true,
"displayName": false,
"fileName": false
}]
]
}Emotion with Production Optimization
JSX
import { css } from '@emotion/react'
import styled from '@emotion/styled'
// Emotion automatically optimizes in production
const navigationStyles = css`
display: flex;
justify-content: space-between;
align-items: center;
background-color: white;
padding: 1rem 2rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
`
const NavigationList = styled.ul`
display: flex;
list-style: none;
margin: 0;
padding: 0;
gap: 2rem;
`
// webpack.config.js for Emotion optimization
module.exports = {
mode: 'production',
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
}Manual Minification Tools
Command Line Tools
Shell
# Using cssnano CLI
npm install -g cssnano-cli
cssnano src/styles.css dist/styles.min.css
# Using clean-css CLI
npm install -g clean-css-cli
cleancss -o dist/styles.min.css src/styles.css
# Using PostCSS CLI with cssnano
npm install -g postcss-cli cssnano
postcss src/styles.css --use cssnano --output dist/styles.min.css
# Batch processing multiple files
find src/css -name "*.css" -exec cleancss {} \; > dist/bundle.min.cssNode.js Scripts
JavaScript
// scripts/minify-css.js
const fs = require('fs')
const path = require('path')
const postcss = require('postcss')
const cssnano = require('cssnano')
async function minifyCSS(inputPath, outputPath) {
try {
const css = fs.readFileSync(inputPath, 'utf8')
const result = await postcss([
cssnano({
preset: [
'advanced',
{
discardComments: { removeAll: true },
normalizeWhitespace: true,
colormin: true,
convertValues: true,
discardDuplicates: true,
mergeRules: true,
minifySelectors: true
}
]
})
]).process(css, { from: inputPath, to: outputPath })
fs.writeFileSync(outputPath, result.css)
const originalSize = Buffer.byteLength(css, 'utf8')
const minifiedSize = Buffer.byteLength(result.css, 'utf8')
const savings = ((originalSize - minifiedSize) / originalSize * 100).toFixed(1)
console.log(`✅ ${inputPath} → ${outputPath}`)
console.log(` ${originalSize} bytes → ${minifiedSize} bytes (${savings}% smaller)`)
} catch (error) {
console.error(`❌ Error minifying ${inputPath}:`, error)
}
}
// Process all CSS files in src directory
async function minifyAllCSS() {
const srcDir = 'src/css'
const distDir = 'dist/css'
if (!fs.existsSync(distDir)) {
fs.mkdirSync(distDir, { recursive: true })
}
const cssFiles = fs.readdirSync(srcDir)
.filter(file => file.endsWith('.css'))
for (const file of cssFiles) {
const inputPath = path.join(srcDir, file)
const outputPath = path.join(distDir, file.replace('.css', '.min.css'))
await minifyCSS(inputPath, outputPath)
}
}
minifyAllCSS()Advanced Optimization Techniques
Critical CSS Extraction and Minification
JavaScript
// scripts/optimize-critical-css.js
const critical = require('critical')
const postcss = require('postcss')
const cssnano = require('cssnano')
async function optimizeCriticalCSS() {
// Extract critical CSS
const { css } = await critical.generate({
inline: false,
base: 'dist/',
src: 'index.html',
width: 1200,
height: 800,
minify: false // We'll minify separately for better control
})
// Minify the critical CSS
const result = await postcss([
cssnano({
preset: [
'advanced',
{
discardComments: { removeAll: true },
normalizeWhitespace: true,
colormin: true
}
]
})
]).process(css, { from: undefined })
// Save minified critical CSS
fs.writeFileSync('dist/css/critical.min.css', result.css)
console.log('✅ Critical CSS extracted and minified')
}CSS Splitting and Minification
JavaScript
// webpack.config.js - Advanced CSS splitting
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
// Critical CSS
critical: {
name: 'critical',
test: /critical\.(css|scss)$/,
chunks: 'all',
enforce: true,
priority: 30
},
// Vendor CSS
vendorStyles: {
name: 'vendor',
test: /[\\/]node_modules[\\/].*\.(css|scss)$/,
chunks: 'all',
enforce: true,
priority: 20
},
// Component CSS
componentStyles: {
name: 'components',
test: /components.*\.(css|scss)$/,
chunks: 'all',
enforce: true,
priority: 10
}
}
},
minimizer: [
new CssMinimizerPlugin({
parallel: true,
minimizerOptions: {
preset: [
'advanced',
{
autoprefixer: false, // Disable if you're using autoprefixer separately
discardComments: { removeAll: true },
normalizeWhitespace: true,
colormin: true,
convertValues: true,
discardDuplicates: true,
mergeRules: true,
minifySelectors: true,
reduceInitial: true,
svgo: {
plugins: [
{ name: 'removeViewBox', active: false },
{ name: 'removeDimensions', active: true }
]
}
}
]
}
})
]
}
}Performance Monitoring
Measuring CSS Optimization Impact
JavaScript
// Performance monitoring script
function measureCSSPerformance() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name.includes('.css')) {
console.log('CSS File:', entry.name)
console.log('Load Time:', entry.duration + 'ms')
console.log('Transfer Size:', entry.transferSize + ' bytes')
console.log('Encoded Size:', entry.encodedBodySize + ' bytes')
console.log('Compression Ratio:',
((entry.transferSize / entry.encodedBodySize) * 100).toFixed(1) + '%')
}
}
})
observer.observe({ entryTypes: ['resource'] })
}
// Run after page load
window.addEventListener('load', measureCSSPerformance)Lighthouse CSS Auditing
JavaScript
// lighthouse-css-audit.js
const lighthouse = require('lighthouse')
const chromeLauncher = require('chrome-launcher')
async function auditCSS(url) {
const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] })
const options = {
logLevel: 'info',
output: 'json',
onlyCategories: ['performance'],
port: chrome.port
}
const runnerResult = await lighthouse(url, options)
// Extract CSS-related audits
const cssAudits = {
'css-minification': runnerResult.lhr.audits['css-minification'],
'unused-css-rules': runnerResult.lhr.audits['unused-css-rules'],
'render-blocking-resources': runnerResult.lhr.audits['render-blocking-resources']
}
console.log('CSS Performance Audits:', cssAudits)
await chrome.kill()
return cssAudits
}
auditCSS('http://localhost:3000')Best Practices
- Automate in Build Process: Never manually minify CSS
- Source Maps: Generate source maps for debugging minified CSS
- Environment-Specific: Only minify in production builds
- Test After Minification: Ensure styles work correctly after minification
- Monitor File Sizes: Track CSS bundle sizes over time
- Critical CSS: Inline and minify critical CSS separately
- Cache Busting: Use content hashes in filenames for cache invalidation
- Compression: Combine minification with gzip/brotli compression
- Dead Code Elimination: Remove unused CSS before minification
- Performance Budget: Set and monitor CSS size budgets
Common Issues and Solutions
Broken Selectors After Minification
CSS
/* Problem: Over-aggressive selector minification */
/* Before: */
.my-component .nested-element { }
/* After (broken): */
.a .b { }
/* Solution: Configure cssnano to preserve class names */
cssnano({
preset: ['default', {
discardComments: { removeAll: true },
minifySelectors: false // Disable if breaking selectors
}]
})Missing Vendor Prefixes
JavaScript
// Solution: Use autoprefixer before minification
postcss([
require('autoprefixer'),
require('cssnano')
])Source Map Issues
JavaScript
// webpack.config.js
module.exports = {
devtool: process.env.NODE_ENV === 'production'
? 'source-map' // Separate source map file
: 'eval-source-map', // Inline for development
}Verification
Automated Checks
- Confirm the computed styles match the intended fix in DevTools.
- If the rule affects motion, contrast, or layout stability, verify those user-facing outcomes directly.
Manual Checks
- Inspect the rendered UI at the breakpoints and interaction states affected by the rule.
- Test at least one mobile and one desktop viewport before shipping.