Download instructions


For years i've wanted a "minifier" for CSS (Cascading Style Sheets) but all of them i've found are either tens of megabytes huge, with insane amounts of dependencies, and/or are based on node.js (against which i have a strong allergy). Well, i'm not getting any younger, and my desire for a CSS minifier is starting to get acute, so i finally sat down for a few hours to Get This Done...

cssminc is a tiny CSS minifier. It's made up of only a small amount of dependency-free, portable, C89-compliant source code. It has only two small files, one C and one header, and can be built as either a library or a standalone binary. (Building the binary also requires one additional C/header pair with app-level utility code.) The library-only bits compile down to a mere 9kb of binary code.

cssminc is intended to be pronounced "see-ess-ess mincy," "see-ess-ess min-see," "see-ess-ess mink," "see-ess-ess mince," "see-smink," or however else one prefers.




License: dual Public Domain/MIT

Feedback and patches are happily received: https://wanderinghorse.net/home/stephan/


The Library API:

cssminc_state cc = cssminc_state_empty; /* similar to memset() but uses some non-0 values */
int rc;
cc.in = cssminc_input_f_FILE; /* input source */
cc.inState = stdin; /* state for cc.in */
cc.out = cssminc_output_f_FILE; /* output destination */
cc.outState = stdout; /* State for cc.out */
rc = cssminc_process(&cc)
  /* 0 on success, non-0 on error, noting that "error" only
     means that it choked on some of the input or one of
     the I/O routines failed. */;

The in and out members can be arbitrary implementations of the I/O abstraction functions, and the inState/outState members are opaque state pointers for use by those functions. e.g. they could be pointers to an app-specific memory buffer type, or the output routine might send its contents directly to a UI widget (nothing that it might do so a byte at a time, so the target needs to be prepared to accept, at least briefly, partial multi-byte characters).

The CLI App

~> time ./cssminc --metrics < big.css > foo.css
./cssminc metrics:
  Input bytes = 260530, output = 137715, = 47.14% reduction
  Output spaces = 4188, non-spaces = 133527

real	0m0.038s
user	0m0.031s
sys	0m0.001s

(That's from a Raspberry Pi 4B system running on an external USB 2.0 hard drive, not a fast workstation.)

Output samples:

Sample (admittedly trivial) input:

$ cat foo.css 
/* This comment is normally retained. */
bläüp, bläüp.x bläüp.y-z,
bläüp.zzz > x
{x:"üö" , yy  :  zz }/* these non-ASCII characters must survive the ccssmin process. */
  /*/ <--- this is a comment, despite
    weird placement of that /. */

/* MOAR comments */

foo { y:z }

         /* Not retained */

bar  { foo:  "barre baz"  }
cssminc -f foo.css -bn

The -bn tells it to add a newline after each closing brace (}):

/* This comment is normally retained. */
bläüp,bläüp.x bläüp.y-z,bläüp.zzz > x{x:"üö",yy:zz}
bar{foo:"barre baz"}
cssminc -f foo.css -bs

-bs tells it to add a space after each closing brace:

/* This comment is normally retained. */
bläüp,bläüp.x bläüp.y-z,bläüp.zzz > x{x:"üö",yy:zz} foo{y:z} bar{foo:"barre baz"}
cssminc -f foo.css -b0 -c

-b0 tells it to add nothing after closing braces and -c tells it to strip the initial comment:

bläüp,bläüp.x bläüp.y-z,bläüp.zzz > x{x:"üö",yy:zz}foo{y:z}bar{foo:"barre baz"}

Normally the initial comment of a CSS file contains a license or attribution, and should not be stripped. cssminc only supports keeping the first comment if it is the first non-space content in the input. Comments appearing after non-space content are unconditionally stripped.