Cascade is a 30,000-line OCaml library that parses, generates, optimizes, and diffs modern CSS. It handles CSS Syntax Levels 3 through 5, covering @layer directives, container queries, nesting, and new color spaces like color-mix(). Developers created it to verify an OCaml port of Tailwind CSS’s generator, ditching Node.js entirely in a dune build pipeline.
This matters because Tailwind dominates utility-first CSS, but its JavaScript toolchain clashes with pure OCaml stacks like MirageOS unikernels. Existing CSS diff tools—meant to compare generated output against references—either gather dust on GitHub or choke on post-CSS3 features. Cascade’s cssdiff tool fixes that with structural comparisons, not just string diffs. Compile it to JavaScript via js_of_ocaml, and it runs in your browser. Test it at the project’s site with examples for layer reordering, nesting, or container queries.
Core Components
The library starts with a hand-written recursive descent parser. It turns CSS strings into a typed AST, preserving duplicates common in generated stylesheets. From there, a small eDSL lets you build the same AST in OCaml code, catching errors at compile time.
Consider this input CSS with duplicate rules:
.btn {
display: inline-block;
background-color: #3b82f6;
color: white;
padding: 0.5rem;
}
.btn {
background-color: #2563eb;
}
Parsing yields a typed AST. Rebuild it via eDSL:
open Cascade.Css
let btn = Selector.class_ "btn"
let rules = [
rule ~selector:btn [
display Inline_block;
background_color (hex "#3b82f6");
color (hex "#ffffff");
padding (Rem 0.5)
];
rule ~selector:btn [
background_color (hex "#2563eb")
]
]
Printing the AST spits out clean CSS, shortening #ffffff to #fff. Enable optimization, and it merges rules, keeping the last background-color per cascade order. It respects !important flags and preserves patterns like content fallbacks. Over 100 typed constructors cover layout (flexbox, grid), colors, transforms, and more.
Implications for Developers
Cascade enables end-to-end OCaml web pipelines: Markdown to HTML to Tailwind-styled CSS in one dune build. No npm, no Webpack. For unikernel fans, this means styling MirageOS apps without external deps, slashing attack surface and build times.
Type safety shines here. Try dsiplay instead of display? Compile error. Pass a color to padding? Type mismatch. Optimization deduplicates bloat from tools like Tailwind, which can balloon stylesheets to megabytes.
Skepticism check: 30k lines sounds bloated, but a full CSS parser+optimizer demands it. Hand-rolled recursive descent avoids lexer hacks in tools like PostCSS, and the typed AST justifies the heft. Maintenance risk exists—CSS evolves fast—but coverage hits v4 Tailwind output precisely.
Broader impact: CSS diffing gaps hurt verification in CSS-in-Rust/Go ports too. Cascade’s AST could inspire cross-language tooling. Pure OCaml+js_of_ocaml browser demos prove portability. If you generate CSS programmatically, grab it. For Tailwind unikernels, it’s a game-changer. Source on GitHub; binaries via opam.