tw is an OCaml implementation of Tailwind CSS v4. You assemble utilities as ordinary OCaml values and compile them to CSS through the same typed variable-and-layer pipeline Tailwind uses, producing byte-for-byte identical output. Unknown utilities are caught at compile time, and nothing depends on Node.js, PostCSS, or the Tailwind CLI at run time.
open Tw
(* A set of utilities is a value of type [Tw.t list]. *)
let card =
[
flex; flex_col; gap 4; p 6;
bg white; rounded_lg; shadow_sm;
border; border_color ~shade:200 gray;
hover [ shadow_md ];
dark [ bg ~shade:800 gray; text ~shade:100 gray ];
]@tailwindcss/typography (prose) and @tailwindcss/forms are fully implemented.Tw.str "flex p-4 bg-blue-500" turns class strings into typed utilities for dynamic class lists.Requires OCaml >= 5.2.
opam install twopen Tw
(* Spacing scales: the integer is multiplied by 0.25rem, so [p 4] is 1rem. *)
let layout = [ flex; items_center; gap 4; p 6; mx_auto; max_w_4xl ]
(* Colors default to shade 500; [~shade] picks another, [~opacity] adds alpha. *)
let colors = [ bg blue; text white; border_color ~shade:300 gray; bg ~opacity:50 white ]
(* Variants nest as functions: responsive breakpoints, state, and dark mode. *)
let responsive =
[ p 4; md [ p 8 ]; lg [ p 12 ]; bg blue; hover [ bg ~shade:600 blue ]; dark [ text ~shade:300 gray ] ]
(* The typography plugin, including per-element variants. *)
let article =
[ prose; prose_lg; mx_auto; prose_headings [ text ~shade:600 blue ]; prose_code [ bg ~shade:100 gray ] ]open Tw
(* [to_classes] returns the value for an HTML [class] attribute. *)
let class_attr = to_classes [ flex; p 4; bg blue ]
(* [to_css] builds a stylesheet; [Css.to_string] renders it to text. *)
let stylesheet = to_css [ flex; p 4; bg blue; hover [ bg ~shade:600 blue ] ]
let css = Css.to_string ~minify:true stylesheet(* [Tw.str] parses a class string into utilities (raises on unknown classes). *)
let styles = Tw.str "flex items-center gap-4 p-6 bg-white rounded-lg shadow-sm"
(* [Tw.of_string] parses a single class and returns a result. *)
let parsed = Tw.of_string "hover:bg-blue-600" (* Ok _ *)
let rejected = Tw.of_string "not-a-class" (* Error (`Msg _) *)# Generate CSS for specific classes
tw -s "flex p-4 bg-blue-500 hover:bg-blue-600"
# Scan files for classes
tw src/ > styles.css
# Compare with real Tailwind CSS
tw -s "prose mb-4 dark:text-gray-300" --diffAll Tailwind CSS v4 core utilities and both official plugins:
Category | Examples |
|---|---|
Layout |
|
Spacing |
|
Sizing |
|
Typography |
|
Colors |
|
Opacity |
|
Borders |
|
Effects |
|
Transforms |
|
Transitions |
|
Interactivity |
|
Filters |
|
Tables |
|
Prose |
|
Forms |
|
Variant | Example |
|---|---|
Responsive |
|
State |
|
Dark mode |
|
Group/Peer |
|
Pseudo-elements |
|
Prose elements |
|
Stacked |
|
The --diff and --tailwind CLI modes require the tailwindcss standalone binary for comparison testing:
# macOS (Apple Silicon)
curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-macos-arm64
chmod +x tailwindcss-macos-arm64
sudo mv tailwindcss-macos-arm64 /usr/local/bin/tailwindcss
# Linux (x64)
curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64
chmod +x tailwindcss-linux-x64
sudo mv tailwindcss-linux-x64 /usr/local/bin/tailwindcssdune build # build the library and CLI
dune runtest # run all tests, including README examples
# Compare a single utility with Tailwind
dune exec -- tw -s "bg-blue-500 hover:bg-blue-600" --diffISC — see LICENSE.md.