wyrihaximus/css-compress
A lightweight PHP library for minifying CSS, stripping comments, whitespace, and redundant characters to shrink stylesheets and speed up page loads. Easy to drop into any PHP or Laravel project, it produces smaller CSS output with minimal setup and no external dependencies.
This is a small, single-purpose runtime CSS minifier — not an asset pipeline. It takes a CSS string and returns a smaller CSS string. Treat it as a library you call, not a Laravel package you install and forget.
composer require wyrihaximus/css-compress
There is no service provider, no facade, no config file, and no auto-discovery. Open vendor/wyrihaximus/css-compress/src first — the entire public surface is one or two classes (a factory and a compressor with a compress(string $css): string-style method). The README is short; the source is the documentation.
Bind it once so you aren't constructing it per call:
// app/Providers/AppServiceProvider.php
public function register(): void
{
$this->app->singleton(\WyriHaximus\CssCompress\Factory::class, fn () => /* factory or compressor */);
}
First real use case: you already generate CSS somewhere at runtime (theme settings in the database, per-tenant branding, critical-CSS for emails) and you want it smaller before it hits disk or the response. That is the sweet spot. If your CSS comes from resources/css and a build step, stop here and use Vite — it already minifies.
$css = $compressor->compress($rawCss);
Storage::disk('public')->put('themes/acme.css', $css);
Cache aggressively, minify once. Minification is pure PHP string work — cheap per call, but pointless per request.
$css = Cache::remember(
'css.theme.' . $theme->id . '.' . $theme->updated_at->timestamp,
now()->addDay(),
fn () => $compressor->compress($theme->buildCss())
);
Key on a hash of the source (or the model's updated_at) so edits invalidate naturally. Never key on the theme id alone.
Serve it from a route with real cache headers. For per-tenant CSS you can't precompile:
Route::get('/t/{tenant}/theme.css', function (Tenant $tenant) {
$css = Cache::remember("css.{$tenant->id}." . md5($tenant->css), 86400,
fn () => app(Compressor::class)->compress($tenant->css));
return response($css, 200, [
'Content-Type' => 'text/css; charset=utf-8',
'Cache-Control' => 'public, max-age=86400',
'ETag' => '"' . md5($css) . '"',
]);
});
Skip minification outside production. Keep raw output in local so you can read stack traces against unminified CSS, and keep line numbers in dev:
$css = app()->isProduction() ? $compressor->compress($css) : $css;
Artisan command for precompiled output. If the CSS only changes on deploy or on save, write it to public/ from a command or an observer, then let nginx serve it statically. This is the pattern that scales — the route approach above is for genuinely dynamic cases.
Blade integration. Use a small component or directive for inline critical CSS rather than {!! !!} everywhere:
@php($critical = \Illuminate\Support\Facades\Cache::remember(
'critical.' . md5($criticalSource), 3600, fn () => app(Compressor::class)->compress($criticalSource)
))
<style>{!! $critical !!}</style>
If it's injected into a <style> block, you don't need to escape it as HTML — but do not reuse that same string in an attribute.
Pair with a test. Keep one fixture of "CSS that must survive" (custom properties, calc(), data URIs, content: " ") and assert on the output. This is your upgrade canary for a low-traffic dependency.
@import resolution, no autoprefixing, no source maps, no PostCSS/Tailwind. It is whitespace-and-comments level minification. Do not try to replace your build step with it.calc() and custom properties first. Aggressive whitespace stripping is where minifiers break: calc(100% - 10px) needs its spaces around -, and content: " " must keep the quoted space. Run your real CSS through it before trusting it in production, not after./*! ... */ license banners are preserved. If your theme CSS bundles third-party fonts or icons under a license requiring attribution, check the output — don't assume."wyrihaximus/css-compress": "~x.y"), commit the lock file, and keep the fixture test green when you bump.How can I help you explore Laravel packages today?