CSS class consolidation tool - compress multiple utility classes into single optimized classes
npm install classpresso> Make utility-first CSS render faster ā 50% faster style recalculation, 42% faster First Paint
Classpresso consolidates repeated utility class patterns at build time, dramatically reducing browser rendering work. Works with Tailwind, Bootstrap, Bulma, Tachyons, UnoCSS, and any utility-first CSS framework.
> š¦ Post-Build Tool ā Your Development Workflow is Unchanged
>
> Classpresso runs after your build (npm run build), not during development. Your source code is never modified ā only the compiled output in .next, dist, build, etc. You'll always see your normal Tailwind/utility classes while developing and debugging.
| Metric | Improvement |
|--------|-------------|
| Style Recalculation | 50% faster |
| First Paint | 42% faster |
| Memory Usage | 21% less |
| Runtime Overhead | 0ms |
Benchmarks run on 1000 complex components with Playwright + Chrome DevTools Protocol
Utility-first CSS means elements with 10-20+ classes:
``html`
Every class on every element is work for the browser:
- Parse the class string
- Look up each class in stylesheets
- Calculate specificity and resolve conflicts
- Compute final styles
With 15 classes Ć 500 elements = 7,500 class lookups per page load.
Classpresso finds repeated patterns and consolidates them:
Before:
`html`
After:
`html`
Generated CSS:
`css`
.cp-btn {
display: inline-flex;
align-items: center;
justify-content: center;
/ ... all consolidated utilities /
}
Result: Fewer classes = less browser work = faster rendering.
`bash`
npm install classpresso --save-dev
`bashBuild your project first
npm run build
Framework Compatibility
$3
| Framework | Classes per Element | Performance Gain |
|-----------|---------------------|------------------|
| Tailwind CSS | 10-20+ typical | Excellent |
| Bootstrap | 5-15 typical | Good |
| Bulma | 5-10 typical | Good |
| Tachyons | 15-25+ typical | Excellent |
| UnoCSS | 10-20+ typical | Excellent |
| Any utility CSS | Varies | Automatic |
$3
Classpresso works with 20+ frameworks out of the box:
| Framework | Build Directory | SSR Flag | Notes |
|-----------|-----------------|----------|-------|
| React Meta-Frameworks |
| Next.js |
.next (default) | --ssr for App Router | Pages Router usually doesn't need SSR flag |
| Remix | build | --ssr recommended | |
| Gatsby | public | Not needed | Static only |
| RedwoodJS | web/dist | --ssr if using SSR | |
| Vue Meta-Frameworks |
| Nuxt 3 | .output | --ssr recommended | |
| VitePress | .vitepress/dist | Not needed | Static docs |
| Gridsome | dist | Not needed | Static only |
| Svelte |
| SvelteKit | build | --ssr recommended | Or .svelte-kit |
| Other Frameworks |
| Astro | dist | --ssr for islands | Static doesn't need SSR |
| Solid Start | .output or dist | --ssr recommended | |
| Qwik | dist | --ssr recommended | |
| Angular | dist/[project-name] | Not needed | Angular 17+ uses browser/ subdir |
| Ember | dist | Not needed | |
| Preact | build or dist | Depends on setup | |
| Generic Bundlers |
| Vite | dist | Depends on framework | |
| Webpack | dist | Not needed | |
| Parcel | dist | Not needed | |
| Create React App | build | Not needed | |
| Static Site Generators |
| Eleventy (11ty) | _site | Not needed | |
| Hugo | public | Not needed | |
| Docusaurus | build | Not needed | |Zero code changes required. Classpresso runs on your build output. Your React, Vue, Svelte, Solid, Qwik, Astro, Angular, or vanilla HTML stays exactly the same.
How It Works
`
1. You run your normal build (next build, vite build, etc.)2. Classpresso scans the output:
ā Finds all class attributes
ā Identifies patterns that repeat
ā Calculates which are worth consolidating
3. Classpresso transforms:
ā Replaces repeated patterns with short hash classes
ā Generates CSS that maps hashes to original utilities
ā Updates HTML/JS with new class names
4. Result:
ā Same visual appearance
ā Dramatically fewer class lookups
ā Faster style recalculation on every interaction
`CLI Commands
$3
Analyze build output and show potential optimizations without modifying files.
`bash
classpresso analyze --dir .next
classpresso analyze --min-occurrences 3 --min-classes 3
classpresso analyze --json
`Options:
-
-d, --dir - Build directory (default: .next)
- --min-occurrences - Minimum times a pattern must appear (default: 2)
- --min-classes - Minimum classes in a pattern (default: 2)
- --ssr - Enable SSR-safe mode for hydration compatibility
- --json - Output as JSON
- -v, --verbose - Verbose output
- --debug - Generate detailed debug log file for troubleshooting
- --send-error-reports - Send error reports to configured webhook
- --error-report-url - Webhook URL for error reports$3
Apply optimizations to the build output.
`bash
classpresso optimize --dir .next
classpresso optimize --dry-run
classpresso optimize --backup
`Options:
-
-d, --dir - Build directory (default: .next)
- --min-occurrences - Minimum times a pattern must appear (default: 2)
- --min-classes - Minimum classes in a pattern (default: 2)
- --ssr - Enable SSR-safe mode for hydration compatibility
- --dry-run - Show what would be done without making changes
- --backup - Create backup files before modifying
- --no-manifest - Don't generate manifest file
- -v, --verbose - Verbose output
- --debug - Generate detailed debug log file for troubleshooting
- --send-error-reports - Send error reports to configured webhook
- --error-report-url - Webhook URL for error reports$3
Generate a report from an existing manifest.
`bash
classpresso report --dir .next
classpresso report --format json
classpresso report --format html > report.html
`Options:
-
-d, --dir - Build directory (default: .next)
- --format - Output format: text, json, html (default: text)Integration Examples
$3
`json
{
"scripts": {
"build": "next build && classpresso optimize",
"build:analyze": "next build && classpresso analyze"
}
}
`$3
`json
{
"scripts": {
"build": "vite build && classpresso optimize --dir dist"
}
}
`$3
`json
{
"scripts": {
"build": "react-scripts build && classpresso optimize --dir build"
}
}
`$3
Classpresso fully supports Astro static, SSR, and hybrid builds.
Static Build (default):
`json
{
"scripts": {
"build": "astro build && classpresso optimize --dir dist"
}
}
`SSR/Hybrid Build (with React/Vue/Svelte islands):
`json
{
"scripts": {
"build": "astro build && classpresso optimize --dir dist --ssr"
}
}
`Configuration file:
`javascript
// classpresso.config.js
module.exports = {
buildDir: 'dist',
// Use --ssr flag if you have interactive islands with client:* directives
ssr: false,
};
`Classpresso automatically detects Astro's build structure:
-
dist/*/.html - Static HTML pages
- dist/_astro/*/.js - Client-side JavaScript
- dist/_astro/*/.css - Compiled CSS
- dist/server/*/.mjs - Server code (SSR mode)
- dist/client/_astro/*/ - Client assets (SSR mode)$3
`json
{
"scripts": {
"build": "nuxt build && classpresso optimize --dir .output --ssr"
}
}
`$3
`json
{
"scripts": {
"build": "vite build && classpresso optimize --dir build --ssr"
}
}
`$3
`json
{
"scripts": {
"build": "remix build && classpresso optimize --dir build --ssr"
}
}
`$3
`json
{
"scripts": {
"build": "vinxi build && classpresso optimize --dir .output --ssr"
}
}
`$3
`json
{
"scripts": {
"build": "qwik build && classpresso optimize --dir dist --ssr"
}
}
`$3
`json
{
"scripts": {
"build": "ng build && classpresso optimize --dir dist/my-app"
}
}
`For Angular 17+, the output is in
dist/[project-name]/browser.$3
`json
{
"scripts": {
"build": "gatsby build && classpresso optimize --dir public"
}
}
`$3
`json
{
"scripts": {
"build": "eleventy && classpresso optimize --dir _site"
}
}
`$3
`bash
hugo && classpresso optimize --dir public
`$3
`json
{
"scripts": {
"build": "docusaurus build && classpresso optimize --dir build"
}
}
`$3
`json
{
"scripts": {
"build": "vitepress build && classpresso optimize --dir .vitepress/dist"
}
}
`SSR-Safe Mode
For Next.js App Router, Remix, or any SSR framework with hydration, use the
--ssr flag to prevent hydration mismatches:`bash
classpresso optimize --ssr
`$3
SSR-safe mode only consolidates patterns that appear in both server-rendered HTML and client-side JavaScript. This ensures the browser sees identical class names during hydration.
Without
--ssr: A pattern in HTML might get consolidated, but the JavaScript bundle still references the original classes ā hydration mismatch error.With
--ssr: Only patterns found in both places are consolidated ā perfect hydration.$3
Use
--ssr for these frameworks:
- Next.js App Router - Always recommended
- Nuxt 3 - Recommended
- SvelteKit - Recommended
- Remix - Recommended
- Solid Start - Recommended
- Qwik - Recommended
- Astro SSR/Hybrid - If using client:* directives with React/Vue/Svelte islands
- RedwoodJS - If using SSR featuresSSR flag NOT needed:
- Next.js Pages Router - Different hydration model
- Astro Static - No hydration
- Gatsby - Static generation
- Eleventy (11ty) - Static only
- Hugo - Static only
- VitePress - Static docs
- Docusaurus - Static docs
- Angular - Client-side rendering
- Ember - Client-side rendering
- Static sites (plain HTML) - No hydration
$3
`javascript
// classpresso.config.js
module.exports = {
ssr: true, // Enable SSR-safe mode
};
`Debug Mode
When troubleshooting issues, enable debug mode to generate a detailed log file:
`bash
classpresso optimize --debug
`This creates
classpresso-debug.log in your build directory containing:
- System info: Node version, OS, platform
- Config resolution: Final merged config values
- Operation trace: Every step with timestamps and timing
- Error details: Full stack traces if errors occurThe log file location is displayed when the command completes. Share this file when reporting issues.
Error Reporting
Opt-in to automatically send error reports to help improve classpresso:
`bash
classpresso optimize --send-error-reports --error-report-url https://your-webhook.com/errors
`Or configure in
classpresso.config.js:`javascript
module.exports = {
sendErrorReports: true,
errorReportUrl: 'https://your-webhook.com/errors',
};
`Privacy: Error reports only include:
- Classpresso version, Node version, OS
- Error message and stack trace
- Non-sensitive config values (thresholds, flags)
Excluded: Full file paths, project structure, class names
Configuration
Create a
classpresso.config.js file in your project root:`javascript
module.exports = {
// Build directory
buildDir: '.next', // Consolidation thresholds
minOccurrences: 2, // Pattern must appear at least 2 times
minClasses: 2, // Pattern must have at least 2 classes
minBytesSaved: 10, // Must save at least 10 bytes
// Hash configuration
hashPrefix: 'cp-', // Prefix for consolidated classes
hashLength: 5, // Hash length (5 = 1M+ unique combinations)
// Classes to exclude from consolidation (safelist)
exclude: {
prefixes: ['js-', 'data-', 'hook-', 'track-'],
suffixes: ['-handler', '-trigger'],
classes: ['no-consolidate'],
patterns: [/^qa-/, /^test-/, /^e2e-/],
},
// CSS output options
cssLayer: false, // Wrap in @layer (e.g., 'utilities') or false for none
// SSR & Hydration
ssr: false, // Enable SSR-safe mode for hydration compatibility
// Debug options
dataAttributes: false, // Add data-cp-original attribute with original classes
debug: false, // Generate detailed debug log file
// Error reporting (opt-in)
sendErrorReports: false, // Send error reports to webhook
errorReportUrl: undefined, // Webhook URL (HTTPS required)
// Output options
manifest: true, // Generate manifest.json
backup: false, // Create .bak files
};
`Benchmark Methodology
Tests run using Playwright with Chrome DevTools Protocol:
`
Test Environment
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Browser: Chromium (headless)
CPU: Throttled 4x (simulates mobile)
Metrics: Performance.getMetrics() APITest Pages
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
500 components: ~26,500 class instances
1000 components: ~53,000 class instances
Each component contains:
- Card container (8 classes)
- Header (12 classes)
- Button (15 classes)
- Badge (6 classes)
- Input (12 classes)
`Run benchmarks yourself:
`bash
npm run benchmark
`Example Output
`
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā CLASSPRESSO RESULTS ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā£
ā Files Scanned: 45 ā
ā Files Modified: 12 ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā£
ā CLASS CONSOLIDATION ā
ā Patterns found: 234 ā
ā Patterns consolidated: 67 ā
ā Total occurrences replaced: 834 ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā£
ā BROWSER PERFORMANCE IMPACT ā
ā Class lookups eliminated: 12,510 ā
ā Estimated style recalc improvement: ~50% ā
ā Estimated First Paint improvement: ~42% ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā£
ā BONUS: SIZE REDUCTION ā
ā HTML bytes saved: 12,450 B ā
ā CSS overhead: 3,200 B ā
ā Net reduction: 9,250 B ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
`API Usage
Use Classpresso programmatically:
`typescript
import {
loadConfig,
scanBuildOutput,
detectConsolidatablePatterns,
createClassMappings,
generateConsolidatedCSS,
transformBuildOutput,
} from 'classpresso';async function optimize() {
const config = await loadConfig('.next');
// Scan for patterns
const scanResult = await scanBuildOutput(config);
// Detect consolidation candidates
const candidates = detectConsolidatablePatterns(
scanResult.occurrences,
config
);
// Create mappings
const mappings = createClassMappings(candidates);
// Generate CSS
const css = await generateConsolidatedCSS(mappings, config.buildDir, config.cssLayer);
// Transform build
await transformBuildOutput(mappings, config);
}
`FAQ
Does this affect my development workflow?
No. Classpresso is a post-build tool that only runs after
npm run build. During development (npm run dev`), you see your normal Tailwind/utility classes exactly as you wrote them ā perfect for debugging and toggling classes in DevTools. Classpresso only transforms the compiled production output.Why does this make sites faster?
Every CSS class is work for the browser. With utility-first CSS, a button might have 15+ classes. Classpresso consolidates repeated patterns so there's less to parse, match, and calculate.
What about bundle size?
That's a bonus! HTML typically drops 50-60%. But the real win is browser performance ā style recalculation happens on every page load, every DOM change, every interaction. 50% faster there is huge.
Do I need to change my code?
No. Classpresso runs on your build output, not source code. Your components stay exactly the same.
Is there runtime overhead?
Zero. Classpresso is build-time only. No JavaScript added, no runtime processing.
MIT
- Website: https://classpresso.com
- GitHub: https://github.com/timclausendev-web/classpresso
- npm: https://www.npmjs.com/package/classpresso