Typed CSS with Tailwind

The Tailwind CSS framework is really nice to use. I recommend checking out some of their instructional videos to get a sense of what it’s like to work with.

If you’re like me, just writing strings for css styling also fills you with anxiety. So let’s use types for our css! A bonus feature is that we now get autocomplete:

(hmm, it seems like embedded webm in discourse broke. Link below)

Here’s a template (tailwind branch) where you can try this for yourself:

git clone --branch tailwind https://github.com/milesfrain/halogen.git myApp
cd myApp
npm install -g purescript spago parcel tailwindcss
# or if you prefer local installation:
# npm install
npm run gen-css
npm run build
npm run serve

How it works

  1. gen-css calls tailwindcss to generate the enormous tailwind.css file. (There are additional customization options at this phase not covered in this guide).
  2. It also calls a css/css2purs.py script which generates a src/Tailwind.purs file containing wrapped tailwind class strings. This is a pretty huge file (50k lines). It uses halogen-compatible ClassName wrappers, but you can tweak it as you wish. Symbols are stripped out of the class strings and some other transformations are applied to turn them into valid PS function names. For example:
hoverNegTranslateX1d2 = ClassName "hover:-translate-x-1/2"
  1. Now autocomplete works, and you’re protected against string typos with your classes. One downside is that rebuilds are more sluggish due to the massive Tailwind.purs file. This is about 4 seconds on my system (versus 1 second without tailwind), which is pretty noticeable with automatic rebuild and page refresh.
    Edit: A compiler fix for this slowness issue has been merged, but if you’d like this faster behavior today (while waiting for 0.14.0), you can make a custom build from this branch, which applies the fix to 0.13.8. Reach out if you’d like more detailed instructions. The following section shows a workaround for vanilla 0.13.8.

Faster rebuilds

If you’re not planning on tweaking CSS while working on other parts of your project, and would like faster rebuilds, you can shrink the size of src/Tailwind.purs to only contain the CSS classes that are actually being used in your project by running:

npm run lock-css

Remember to also “Restart/Reconnect purs IDE server” so it picks-up this smaller file.

Note that now autocomplete will no-longer be aware of all possible CSS classes, so re-run gen-css when you’re ready again for the full CSS pallette.


The initial tailwind.css file is 2MB, but we can throw away all the unused classes. Simply run:

npm run build-prod

It runs PurgeCSS to look at what classes are actually used in the bundle produced by purs (with dead-code-elimination applied), and removes everything it doesn’t see from prod/tailwind.css. This is the same approach used by lock-css, except the purged css file is then fed back into css2purs.py to generate the input Tailwind.purs file.

In this template example the resulting CSS is shrunk down to 12KB (4KB minified, 1.5KB compressed).


Tailwind is pretty, but how about tables and everything else, there is no predefined components as I see

I plan to port rmwc for example

Btw you can generate purescript modules with https://github.com/purescript-codegen/purescript-ps-cst


You are missing the point of tailwind, here is tables -> https://tailwindcss.com/docs/table-layout/#app

and if you want to use extensible components have a look at https://tailwindui.com/

Tailwind is about extensibility, and be able to tweak. It is like abstraction in correct level, you can abstract table+padding+color into .custome-table and use that everywhere. It takes small time to get going this way, but you will thank to yourself that you did it, that way. No more !important everywhere where you need to fight with framework.