CSS Minify in CI — Where It Fits in Your Pipeline
A site ships beautiful CSS in development and a 400KB unminified sheet in production because the Vite plugin was disabled “temporarily.” Minification is not a mystery — it is a pipeline slot teams forget when frameworks already tree-shake. Hand-pasting into a random website mid-incident is how license banners and rare hacks disappear without a git trail.
What minify does (and does not)
Does: remove whitespace/comments, shorten where safe, sometimes merge rules (depending on tool).
Does not: remove unused selectors by itself (that is Purge/content/Tailwind/UnCSS), fix specificity wars, or replace a critical-CSS strategy.
Author CSS → (preprocess) → bundle → unused removal → minify → hash filename → CDN
Transport compression (Brotli/Gzip) is a separate layer. You want minified bytes and compressed transfer — neither substitutes for the other.
Correct placement in CI
- Compile SCSS/PostCSS/Tailwind
- Let the bundler emit one or more CSS files
- Run minifier as part of
vite build/cssnano/ Lightning CSS - Upload artifacts with content hashes
- Fail the build if CSS budget exceeds the limit
Wrong placement: minify in middleware per request, or rely on humans pasting into a website before release. Dev can skip heavy minify for speed; staging must mirror prod.
For a quick local compare of before/after size, a CSS minify tool is fine. Wire the same idea into CI for anything customers download repeatedly.
Tailwind and “purge”
Tailwind’s content scanning dramatically cuts utility volume. You still want minification:
| Step | Removes |
|---|---|
| Content / purge | Unused class rules |
| Minify | Bytes inside remaining rules |
Skipping minify after purge leaves readable but heavier CSS on the wire. Wrong content globs are a bigger size regression than forgetting minify — fix paths first, then minify.
Source maps and debugging
Production-only repros need maps or you will hate life. Options:
- Public maps (simple; reveals source structure)
- Private map store keyed by version
- Keep unminified artifacts in CI for a week
Never treat “pretty-print in DevTools” as the only strategy for a minified 200KB sheet. If your security policy forbids public maps, document how on-call retrieves them.
What aggressive minify can break
Most modern defaults are safe. Still watch for:
- Old CSS hacks that depended on specific comment markers
- Exotic
@supports/ browser-specific syntax your optimizer misunderstands - Design-system packages that already ship minified — double-processing rarely helps
Visual regression on staging after enabling a new optimizer setting is cheaper than a Friday night rollback.
Budgets and verification
main.css budget 80KB gzip
Measure gzip/brotli size — the number users feel — not only raw minify output. Track the budget in CI so a dependency’s CSS import cannot silently balloon the sheet. Chart the trend weekly; sudden jumps usually mean a new import or a broken purge path.
Emergency hotfix path
If production is broken and CI is red:
- Reproduce locally
- Minify the patched fragment with a local tool if needed
- Still land the proper pipeline fix in the next PR
Hotfix pastes without automation become mythology configs that nobody can reproduce.
Checklist for the next build PR
- Minify enabled in production mode
- Unused CSS removal configured (Tailwind content paths correct)
- Filenames hashed; cache forever
- HTML references the hashed CSS
- Budget alert on size regression
- Maps policy documented
- Staging uses the same minify settings as production
CSS minify belongs in CI beside JS minify — once per build, not once per browser session. Purge unused rules, minify what remains, hash the file, and keep a budget so “temporary” unminified deploys cannot linger into next quarter.