Note: As of 08/16/2021, tailwind-ppx is now in archive-mode. I have been out of the Reason/ReScript community for some time now, but if anyone wants to take over as owner please let me know. Thank you to all involved for making this project successful.
A Reason/OCaml Pre-Processor eXtension (PPX) that validates your Tailwind classes at compile-time.
- Features (Current and Upcoming)
 - Usage
 - Configuration
 - Installation
 - Frequently Asked Questions (FAQ)
 - Developing
 - Contributors
 - Examples and Related Projects
 
Current
- Checks for invalid class names (and suggestions for valid ones!)
 - Checks for duplicate class names
 - Always in-sync with your 
tailwind.cssfile (just make sure to re-build!) - Automatic purging of unused class names (with PurgeCSS and 
tailwind-ppx's custom extractor function) - Ships with an integration script that converts all your
existing 
className="..."toclassName=[%tw "..."] 
Upcoming
- Better integration with PostCSS
 - Checks for redundant class names (like having both 
flex-rowandflex-col) - Checks for class name dependencies (like having 
flex-rowwithoutflex) 
If you have ideas for new features, please open an issue!
tailwind_ppx implements a ppx (%tw) that validates your Tailwind CSS classes at compile time.
For example, for the following (condensed) tailwind.css file:
.flex {
  display: flex;
}
.flex-row {
  flex-direction: row;
}tailwind-ppx will provide validation for your desired class names. See these
examples:
// Example 1
<Component className=[%tw "flex flex-row"] /> // This is ok!
// Example 2
<Component className=[%tw "flex flex-ro"] /> // ERROR: Class name not found: flex-ro. Did you mean flex-row?
// Example 3
<Component className=[%tw "flex flex-row flex"] /> // ERROR: Duplicate class name: flexFinally, tailwind-ppx requires your generated tailwind.css file to exist somewhere in the
project hierarchy. Though not required, it's recommended that you configure the
path to your tailwind.css file (relative to your project root).
As outlined in the Tailwind docs, when preparing for production you'll want to make sure that the only CSS from Tailwind that ends up in your bundle is CSS that you actually use in your code.
First, take a second to read the section on setting up Purgecss from the Tailwind docs. In order to help with the process outlined in the docs, this package ships with a default extractor function that'll take care of ensuring that any CSS from Tailwind that you aren't using with this PPX can be purged from your production CSS bundle. You enable it by slightly modifying the official example of how to set up your postcss.config.js:
// postcss.config.js
const purgecss = require("@fullhuman/postcss-purgecss")({
  // Specify the paths to all ReasonML code where you're using this PPX.
  content: ["./src/**/*.re"],
  // Include the extractor from this package
  defaultExtractor: require("@dylanirlbeck/tailwind-ppx").extractor
});
module.exports = {
  plugins: [
    require("tailwindcss"),
    require("autoprefixer"),
    ...(process.env.NODE_ENV === "production" ? [purgecss] : [])
  ]
};Doing this will ensure that you only ship CSS from Tailwind to production that you're actually using with this PPX.
If your tailwind.css file changes (or you move it) you'll need to rebuild your
project - for example, bsb -clean-world and bsb -make-world if in BuckleScript.
At this time, tailwind-ppx does not automatically watch for changes, though this is on
the roadmap.
Alternatively, you can add the following rules to you bsconfig.json to re-trigger builds
{
  "sources": [
    {
      "dir": "src",
      "subdirs": true,
      "generators": [
        {
          "name": "gen-tailwind",
          "edge": ["tailwind.css", ":", "styles.css"]
        }
      ]
    }
  ],
  "generators": [
    {
      "name": "gen-tailwind",
      "command": "tailwindcss build $in -o $out"
    }
  ]
}If you have a custom tailwind config file, you'll need to pass it to the tailwindcss command:
{
  "name": "gen-tailwind",
  "command": "tailwindcss build $in -o $out -c ../../tailwind.config.js"
}You might have to specify the path to tailwind.css.
If you're a Neovim user, you can download the coc-tailwindcss extension to get class name autocompletion while using tailwind-ppx - just make sure to define a tailwind.config.js file. See the example below!
tailwind-ppx will generate a .tailwind_ppx_cache folder in your project root
to optimize the validation performance. If you're using a version control
system, you don't need to check it in.
By default, tailwind-ppx looks for your tailwind.css file in the root
directory. If tailwind.css lives elsewhere (or the name of your generated CSS file is different), you'll need to specify the file path in your bsconfig.json.
"ppx-flags": [
  ["tailwind-ppx", "-path ../path/to/tailwind.css",]
],The most likely use case for tailwind-ppx is inside ReasonReact projects
(using BuckleScript). To get started, we recommend cloning our demo
project.
Install the PPX with yarn or npm
yarn add --dev @dylanirlbeck/tailwind-ppx
# Or
npm install --dev @dylanirlbeck/tailwind-ppxAnd add the PPX in your bsconfig.json file:
{
  "ppx-flags": ["tailwind-ppx"]
}The @dylanirlbeck/tailwind-ppx NPM package ships with an executable that, when
run in a BuckleScript project, turns all instances of className="..." into
className=[%tw "..."]. The script is designed to make it easy to immediately
introduce tailwind-ppx into an existing codebase.
You can use this script by running the following command from the root of your project (just make sure you've installed the NPM package).
yarn use-tailwind-ppx
# Or
npx use-tailwind-ppx
Note that you'll need both a
bsconfig.jsonto exist in the project hierarchy and compiled project withbsb -make-world(so thelib/directory exists in the project root) for the script to work properly.
- 
How can I conditionally add classes?
This feature is out of scope for
tailwind-ppx; instead, we recommend you usere-classnamesin combination withtailwind-ppx. See the example below:module SomeComponent = { [@react.component] let make = (~someBool) => { let className = Cn.( [%tw "text-blue-500"]->on(someBool) + [%tw "text-gray-500"]->on(!someBool) ); <div className />; }; };
 - 
How can I use custom CSS classes?
tailwind-ppxdirectly parses your generatedtailwind.cssfile, which means that all CSS classes will be validated by the PPX, including custom class names defined in your baseindex.css/styles.cssfile. In short, if the class is in yourtailwind.cssfile, it will be validated correctly by the ppx.Example:
<Component className=[%tw "flex flex-row customLayou"] /> // ERROR: Class name not found: customLayou. Did you mean customLayout?
 
After cloning the repository, you should run esy to install the project dependencies. After that, you should be good to start developing!
esy build-> Builds the projectesy format-> Formats the entire project withocamlformatandrefmtesy watch-> Watches for changes to Reason/OCaml files in the entire project, including in the/testdirectoryesy test_native-> Runs the native tests (intest/native)cd test/bucklescript && yarn test-> Runs the BuckleScript tests (intest/bucklescript)
Note that if you pull requests are not formatted properly, or the
esy.lockis out-of-date, GitHub actions will automatically format your code by pushing up a new commit.
- Bump the version of the ppx in 
esy.jsononmaster(we use semantic versioning) - Create and push a new tag
 
$ git checkout master
$ git tag vx.y.z
$ git push origin vx.y.z
- Create detailed release notes for the new version, following the 
Added/Changed/Fixed/Removedformat. Note that the new version of the PPX will automatically be pushed to NPM and a release will be created on GitHub. - Make sure that for any merged pull requests/closed issues were noticed by the 
all-contributorsbot -- see this PR for an example of adding a new contributor who's PR was merged. 
Thanks goes to these wonderful people:
This project follows the all-contributors specification. Contributions of any kind welcome!
These projects are using tailwind-ppx throughout the code base:
The following amazing projects provided a lot of inspiration; I recommend you check them out!

