Developer
CSS Minifier
Minify CSS to shrink file size by stripping whitespace, comments, and unnecessary semicolons — or beautify with clean indentation and one property per line.
—
Original
—
Output
—
Saved
—
Rules
Output
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
function formatBytes(n) {
if (n < 1024) return n + ' B';
return (n / 1024).toFixed(1) + ' KB';
}
function updateStats(orig, out) {
const ob = new TextEncoder().encode(orig).length;
const outb = out !== null ? new TextEncoder().encode(out).length : null;
document.getElementById('statOrig').textContent = formatBytes(ob);
document.getElementById('statOut').textContent = outb !== null ? formatBytes(outb) : '—';
if (outb !== null && ob > 0) {
const pct = Math.round((1 - outb / ob) * 100);
document.getElementById('statSaved').textContent = (pct > 0 ? '-' : '+') + Math.abs(pct) + '%';
} else {
document.getElementById('statSaved').textContent = '—';
}
// Count rules (opening braces outside strings/comments)
const rules = (orig.match(/\{/g) || []).length;
document.getElementById('statRules').textContent = rules || '—';
}
function onInput() {
const input = document.getElementById('cssInput').value;
if (input.trim()) {
updateStats(input, null);
}
}
function stripComments(css) {
return css.replace(/\/\*[\s\S]*?\*\//g, '');
}
function minifyCSS() {
const input = document.getElementById('cssInput').value;
if (!input.trim()) return;
let css = stripComments(input);
css = css
.replace(/\s*\{\s*/g, '{')
.replace(/\s*\}\s*/g, '}')
.replace(/\s*:\s*/g, ':')
.replace(/\s*;\s*/g, ';')
.replace(/\s*,\s*/g, ',')
.replace(/;+\}/g, '}') // remove last semicolon before }
.replace(/\s+/g, ' ')
.trim();
document.getElementById('cssOutput').innerHTML = escapeHtml(css);
updateStats(input, css);
}
function beautifyCSS() {
const input = document.getElementById('cssInput').value;
if (!input.trim()) return;
// First minify to normalize, then reformat
let css = stripComments(input)
.replace(/\s+/g, ' ')
.trim();
let result = '';
let indent = 0;
let i = 0;
while (i < css.length) {
const c = css[i];
if (c === '{') {
result = result.trimEnd() + ' {\n';
indent++;
i++;
// Skip whitespace after {
while (i < css.length && css[i] === ' ') i++;
continue;
}
if (c === '}') {
indent = Math.max(0, indent - 1);
result = result.trimEnd() + '\n' + ' '.repeat(indent) + '}\n\n';
i++;
while (i < css.length && css[i] === ' ') i++;
continue;
}
if (c === ';') {
result = result.trimEnd() + ';\n';
i++;
// Skip space after ;
while (i < css.length && css[i] === ' ') i++;
if (i < css.length && css[i] !== '}') {
result += ' '.repeat(indent);
}
continue;
}
if (c === ' ' && result.endsWith('\n')) {
i++;
continue;
}
// Start of a new line inside a rule block
if (indent > 0 && (result.endsWith('\n') || result.endsWith('{\n'))) {
result += ' '.repeat(indent);
}
result += c;
i++;
}
result = result.replace(/\n{3,}/g, '\n\n').trim();
document.getElementById('cssOutput').innerHTML = highlightCSS(result);
updateStats(input, result);
}
function highlightCSS(css) {
const esc = s => s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
// Simple line-by-line highlight
return css.split('\n').map(line => {
const trimmed = line.trim();
// Comment
if (trimmed.startsWith('/*')) return '<span class="cc">' + esc(line) + '</span>';
// Selector line (ends with { or is just })
if (trimmed.endsWith('{') || trimmed === '}') return '<span class="cs">' + esc(line) + '</span>';
// Property: value;
const m = line.match(/^(\s*)([\w-]+)(\s*:\s*)(.+)$/);
if (m) {
return esc(m[1]) + '<span class="cp">' + esc(m[2]) + '</span>' + esc(m[3]) + '<span class="cv">' + esc(m[4]) + '</span>';
}
return esc(line);
}).join('\n');
}
function escapeHtml(s) {
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
}
function copyOutput(btn) {
const text = document.getElementById('cssOutput').textContent;
if (!text) return;
navigator.clipboard.writeText(text).catch(() => {});
btn.textContent = 'copied!';
btn.classList.add('copied');
setTimeout(() => { btn.textContent = 'copy'; btn.classList.remove('copied'); }, 2000);
}
function clearAll() {
document.getElementById('cssInput').value = '';
document.getElementById('cssOutput').innerHTML = '';
document.getElementById('statOrig').textContent = '—';
document.getElementById('statOut').textContent = '—';
document.getElementById('statSaved').textContent = '—';
document.getElementById('statRules').textContent = '—';
}
document.getElementById('cssInput').addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
e.preventDefault();
const s = this.selectionStart, end = this.selectionEnd;
this.value = this.value.substring(0, s) + ' ' + this.value.substring(end);
this.selectionStart = this.selectionEnd = s + 2;
}
});
// Sample CSS
document.getElementById('cssInput').value = `/* Main layout */
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
max-width: 1200px;
margin: 0 auto;
padding: 24px 16px;
}
/* Navigation */
.nav {
position: sticky;
top: 0;
z-index: 100;
background: rgba(10,10,10,0.9);
backdrop-filter: blur(16px);
border-bottom: 1px solid #1e1e1e;
height: 52px;
}
.nav a {
color: #e8e0d5;
text-decoration: none;
font-size: 14px;
transition: opacity 0.2s ease;
}
.nav a:hover { opacity: 0.8; }`;
onInput();