styleleak
v0.1.1
Published
Find and eliminate unused CSS classes in your TypeScript/React project
Maintainers
Readme
styleleak
Find and eliminate unused CSS classes in your TypeScript / React project.
The Problem
Over time, as components get deleted and features get removed, stylesheets accumulate orphaned classes that nobody uses but nobody dares delete.
/* card.module.css */
.card { ... } /* ✅ used in Card.tsx */
.cardV2 { ... } /* ❌ refactored 4 months ago */
.wrapperOld { ... } /* ❌ component was deleted */
.promoBadge { ... } /* ❌ feature was removed */styleleak tells you instantly what's safe to delete.
Installation
# Run without installing
npx styleleak ./src
# Install globally
npm install -g styleleak
# Install as a dev dependency
npm install -D styleleakQuick Start
# Scan your src folder
styleleak ./src
# See file sizes and potential savings
styleleak ./src --size
# Preview what would be removed (safe, no file changes)
styleleak ./src --dry-run
# Remove all unused classes
styleleak ./src --fixSample Output
styleleak — scanned 42 CSS files, 118 component files
src/styles/card.module.css [4.2 KB → 3.8 KB]
✗ .cardV2 (120 bytes)
✗ .wrapperOld (98 bytes)
src/styles/global.css [12.8 KB → 12.2 KB]
✗ .old-sidebar-item (380 bytes)
✗ .promo-badge (210 bytes)
src/components/Nav/Nav.module.css [2.1 KB → 2.0 KB]
✗ .navItemActive (64 bytes)
────────────────────────────────────────────────────
5 unused classes across 3 files
Potential savings: ~872 bytes
Run with --dry-run to preview, --fix to remove
────────────────────────────────────────────────────All Options
Scanning & Scope
[dir] — Directory to scan
Pass a directory as the first argument. Defaults to the current directory.
styleleak ./src
styleleak ./app
styleleak .--dir — Scan specific folders
Scan one or more specific folders. Can be repeated.
styleleak --dir ./src/components --dir ./src/pages--css-ext — CSS file types to scan
Comma-separated list of CSS extensions. Default: css,scss,module.css
# Only SCSS files
styleleak ./src --css-ext scss
# CSS and SCSS only (skip modules)
styleleak ./src --css-ext css,scss
# Include Less files
styleleak ./src --css-ext css,scss,less--js-ext — Component file types to scan
Comma-separated list of JS/TS extensions. Default: tsx,jsx,ts,js
# Only TSX and JSX
styleleak ./src --js-ext tsx,jsx
# Include Vue SFCs
styleleak ./src --js-ext tsx,jsx,vue--exclude — Exclude files or folders
Glob patterns to exclude. Can be repeated.
styleleak ./src --exclude "**/legacy/**"
styleleak ./src --exclude "**/legacy/**" --exclude "**/*.test.css"--only-modules — Only scan CSS Modules
Limits CSS scanning to .module.css and .module.scss files only.
styleleak ./src --only-modules--only-global — Only scan global stylesheets
Skips CSS Modules and only scans plain .css / .scss files.
styleleak ./src --only-global--git-staged — Only scan staged files
Scans only files currently staged in git. Ideal for pre-commit hooks.
styleleak --git-staged// package.json — use with lint-staged
"lint-staged": {
"*.{css,scss}": ["styleleak --git-staged --threshold 0"]
}Output & Reporting
--size — Show file sizes and savings
Displays current file size, estimated size after cleanup, and bytes saved per class.
styleleak ./src --size src/styles/card.module.css [4.2 KB → 3.8 KB]
✗ .cardV2 (120 bytes)
✗ .wrapperOld (98 bytes)
Potential savings: 218 bytes--format — Output format
Choose between table (default), json, or text.
# Default colored table
styleleak ./src --format table
# Machine-readable JSON (great for CI)
styleleak ./src --format json
# Plain text (great for log files)
styleleak ./src --format text# Save JSON report
styleleak ./src --format json --output styleleak-report.json--sort — Sort results
Sort unused classes by file (default), size (biggest savings first), or name (alphabetical).
# Biggest savings first
styleleak ./src --sort size
# Alphabetical by class name
styleleak ./src --sort name
# Grouped by file path (default)
styleleak ./src --sort file--summary-only — Print totals only
Prints a single summary line instead of the full per-class breakdown.
styleleak ./src --summary-only
# → 5 unused classes across 3 files (872 bytes)--output — Save report to a file
styleleak ./src --output report.txt
styleleak ./src --format json --output report.json--report-possibly-used — Show dynamic class warnings
Lists classes used in dynamic expressions (btn-${variant}, clsx(...)) with a ⚠ warning.
styleleak ./src --report-possibly-used ⚠ .btn-primary (possibly used — dynamic class, cannot verify)--verbose — Show scan progress
styleleak ./src --verbose
# Shows: dirs scanned, file counts, timing--silent — No output, exit code only
Produces no terminal output. Returns exit code 0 if clean, 1 if leaks are found.
styleleak ./src --silent
echo $? # 0 = clean, 1 = leaks foundFixing
--dry-run — Preview removals
Shows exactly what --fix would delete, without touching any files. Always run this first.
styleleak ./src --dry-run [dry-run] Would remove .cardV2 from src/styles/card.module.css
[dry-run] Would remove .wrapperOld from src/styles/card.module.css
[dry-run] 2 classes would be removed from src/styles/card.module.css--fix — Auto-remove unused classes
Removes all unused class blocks from your CSS files directly.
styleleak ./src --fixAlways run
--dry-runfirst to review what will be removed.
--backup — Back up files before fixing
Creates a .bak copy of each file before --fix modifies it.
styleleak ./src --fix --backup
# Creates: card.module.css.bak, global.css.bak, etc.--interactive — Confirm each removal
Steps through each unused class one by one and asks you to confirm before removing — like git add -p for CSS cleanup.
styleleak ./src --interactive ? Remove .cardV2 from card.module.css? (120 bytes) [y/n]
? Remove .wrapperOld from card.module.css? (98 bytes) [y/n]Safety & CI
--threshold — Fail above N unused classes
Returns exit code 1 when unused classes exceed the threshold. Use in CI to block merges.
# Fail on any unused class
styleleak ./src --threshold 0
# Allow up to 5 unused classes before failing
styleleak ./src --threshold 5# .github/workflows/styleleak.yml
- name: Check for unused CSS
run: npx styleleak ./src --threshold 0 --silent--min-age — Skip recently modified files
Uses git blame to skip CSS files modified within the last N days. Prevents flagging newly added classes that haven't been wired up yet.
# Skip files modified in the last 7 days
styleleak ./src --min-age 7--ignore — Ignore specific class names
Treat specific class names as used, even if they don't appear in source files. Useful for classes added at runtime via JavaScript.
styleleak ./src --ignore tooltipVisible --ignore js-modal-open--ignore-file — Skip specific CSS files
styleleak ./src --ignore-file src/styles/animations.css--allowlist — Point to an allowlist file
Load a file of class names to always treat as used.
styleleak ./src --allowlist .styleleak-allowlist# .styleleak-allowlist
# One class per line. Lines starting with # are comments.
tooltipVisible
js-modal-open
is-active
fade-enter-activeIf no --allowlist flag is passed, styleleak automatically reads .styleleak-allowlist from the current directory if it exists.
Configuration File
--config — Load options from a config file
Define all your options once in a config file instead of passing flags every time.
styleleak --config styleleak.config.js// styleleak.config.js
module.exports = {
dirs: ['./src/components', './src/pages'],
cssExt: ['css', 'scss'],
jsExt: ['tsx', 'jsx'],
exclude: ['**/legacy/**'],
size: true,
sort: 'size',
threshold: 0,
allowlist: '.styleleak-allowlist',
}If no --config flag is passed, styleleak automatically looks for styleleak.config.js or .styleleakrc.js in the current directory.
Safe Workflow (Recommended)
# 1. Audit — see what's unused and how big
styleleak ./src --size --sort size
# 2. Preview — see exactly what --fix will delete
styleleak ./src --dry-run
# 3. Remove with backup — just in case
styleleak ./src --fix --backup
# 4. Verify — make sure everything still compiles
npm run buildCI Integration
# Fail the build if any unused classes are found
styleleak ./src --threshold 0 --silent# .github/workflows/ci.yml
jobs:
styleleak:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npx styleleak ./src --threshold 0 --format json --output styleleak.json
- uses: actions/upload-artifact@v4
if: failure()
with:
name: styleleak-report
path: styleleak.jsonPre-commit Hook (lint-staged)
npm install -D lint-staged husky// package.json
"lint-staged": {
"*.{css,scss}": ["styleleak --git-staged --threshold 0"]
}Known Limitations
| Scenario | Behavior |
|---|---|
| className={`btn-${variant}`} | Cannot be statically detected — use --ignore or --allowlist |
| clsx('foo', isActive && 'bar') | Static string args detected; dynamic expressions skipped |
| Classes added via JS at runtime | Not detectable — add to .styleleak-allowlist |
| Tailwind utility classes | Not designed for Tailwind — works best with custom CSS / SCSS / CSS Modules |
Programmatic API
Use styleleak as a library in your own tools:
import { scan, defaultConfig } from 'styleleak'
const report = await scan({
...defaultConfig,
dirs: ['./src'],
size: true,
})
console.log(`Found ${report.totalUnused} unused classes`)
console.log(`Potential savings: ${report.totalSavingsBytes} bytes`)
for (const file of report.files) {
console.log(file.filePath, file.unusedClasses.map(c => c.name))
}License
MIT
