/ CSS Checker Fixes / CSS Bundle Size

How to Fix CSS Bundle Size

CSS bundle size affects everything downstream: First Contentful Paint, Largest Contentful Paint, mobile data usage, perceived speed. Sites shipping 200+ KB of CSS are throwing performance away. The fix combines four techniques: remove unused CSS, split per route, compress with Brotli, cache aggressively. Done together, typical sites cut CSS to 30-60 KB compressed per page.

1. Audit current bundle size

Step 1
Network tab measurements
DevTools → Network → reload. Filter by CSS. Note for each file:
  • Transfer size (compressed, what user actually downloads)
  • Resource size (decompressed)
  • Time to download
  • Whether served from cache
Step 2
Bundle analysis
For webpack: webpack-bundle-analyzer. For Vite: rollup-plugin-visualizer. For Next.js: ANALYZE=true next build. Shows what's inside each bundle, where the bytes go.

2. Remove unused CSS

Covered fully in the unused CSS guide. Most impactful step — typically 80% reduction. Don't move on to other techniques until you've done this.

3. Code-split CSS per route

Next.js

Automatic with CSS Modules and styled-jsx. Each page's CSS imports compile into per-route chunks. No config needed.

Vite

// vite.config.js
export default {
  build: {
    cssCodeSplit: true,  // default in Vite 4+
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor-css': ['react', 'react-dom'],
        }
      }
    }
  }
}

webpack

// webpack.config.js with MiniCssExtractPlugin
module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        styles: {
          name: 'styles',
          type: 'css/mini-extract',
          chunks: 'async',  /* async to enable splitting */
          enforce: true,
        }
      }
    }
  }
}

4. Tree-shake utility frameworks

Tailwind JIT (v3+)

// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{html,js,jsx,ts,tsx,vue}',
    './public/**/*.html',
    // EVERY file that uses Tailwind classes
  ],
}

Tailwind only generates classes it finds in content. Missing paths = missing classes. Don't use wildcards too broadly — they slow build but don't add to bundle.

Bootstrap modular imports

// scss/main.scss
@use 'bootstrap/scss/functions';
@use 'bootstrap/scss/variables';
@use 'bootstrap/scss/mixins';
@use 'bootstrap/scss/root';
@use 'bootstrap/scss/reboot';
@use 'bootstrap/scss/type';
@use 'bootstrap/scss/grid';
@use 'bootstrap/scss/buttons';
// Skip components you don't use: carousel, modal, etc.

Bulma sass partials

@use 'bulma/sass/utilities/all';
@use 'bulma/sass/base/minireset';
@use 'bulma/sass/elements/button';
@use 'bulma/sass/components/navbar';
// Cherry-pick only what you use

5. Minify aggressively

Most build tools minify CSS by default. Verify you're using cssnano or equivalent.

// postcss.config.js
module.exports = {
  plugins: [
    require('cssnano')({
      preset: ['default', {
        discardComments: { removeAll: true },
        minifyFontValues: true,
        normalizeWhitespace: true,
        colormin: true,
      }]
    })
  ]
}

6. Compress with Brotli

Brotli compresses CSS 15-25% smaller than gzip. All modern browsers support it.

nginx

http {
  brotli on;
  brotli_comp_level 6;
  brotli_types text/plain text/css application/json 
               application/javascript text/xml 
               application/xml application/xml+rss text/javascript;
  
  # Also enable gzip as fallback
  gzip on;
  gzip_types same list as above;
}

Apache

<IfModule mod_brotli.c>
  AddOutputFilterByType BROTLI_COMPRESS text/css application/javascript
</IfModule>
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/css application/javascript
</IfModule>

CDN-level (Cloudflare, Fastly, etc.)

Most CDNs apply Brotli automatically when origin doesn't. Check CDN dashboard to confirm.

Static pre-compression

// Build step: pre-compress CSS files
npm install --save-dev compression-webpack-plugin

new CompressionPlugin({
  algorithm: 'brotliCompress',
  test: /\.(css|js)$/,
  filename: '[path][base].br',
});

Then configure nginx to serve .br files when Accept-Encoding includes brotli:

brotli_static on;

7. Cache aggressively with hashed filenames

Production builds output filenames like main.abc123.css. The hash changes when content changes, so you can cache forever:

# nginx
location ~* \.(css|js)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

Repeat visits hit cache: zero download cost. First visit pays the full price. Hashed filenames mean cache busts automatically when CSS changes.

8. Measure the cumulative improvement

Step 1
Before/after Lighthouse
Capture Lighthouse Performance score, FCP, LCP, transfer sizes before any changes. After each change, re-measure. Document the gain per technique:
  • PurgeCSS: usually 60-80% bundle reduction
  • Code splitting: 30-50% reduction in per-page CSS
  • Brotli vs gzip: 15-25% further reduction
  • Caching: zero CSS download on repeat visits
Cumulative effect: a 250 KB initial CSS bundle becomes 30-50 KB compressed per page, with repeat visits at 0 KB.
💡 The CSS bundle size win cascades. Smaller CSS means faster First Paint, smaller HTML responses (less inlined critical CSS), better Core Web Vitals, lower mobile data costs. Few optimisations have this many downstream benefits.

🎨 Re-run the CSS Checker

Verify bundle size has dropped to target.

Run CSS Checker →
Related Guides: CSS Checker Fixes  ·  Fix Unused CSS  ·  Fix Render-Blocking CSS  ·  Page Speed Fixes
💬 Got a problem?