Getting Tailwind to Work with Elm Book
Tags: elm, tailwindcss, css
Published: 2022-11-28 14:27:43


---
title: Getting Tailwind to Work with Elm Book 
published: true
description: Getting Tailwind to work with Elm Book was harder than I thought it'd be. Hopefully this setup will help you save time if you're trying to get Tailwind, or any CSS framework, to work with Elm Book.
tags: elm,tailwind,css
canonical_url: https://jessewarden.com/2022/11/getting-tailwind-to-work-with-elm-book.html
cover_image: 
---

Trying to help build a design system at work in my spare time; no clue if it will go anywhere but it’s fun regardless. I asked the [Elm Slack](http://elmlang.slack.com/) group what the equivalent of [React Storybook](https://storybook.js.org/) is in Elm land. Specifically, I wanted a way to build a documentation website like [Vuepress](https://vuepress.vuejs.org/) with the ability to host native [Elm code](https://elm-lang.org/) to showcase components. They pointed me to [Elm Book](https://package.elm-lang.org/packages/dtwrks/elm-book/latest/). While Elm Book has built-in theming capabilities, I needed CSS control over my components. While they support [elm-css](https://github.com/rtfeldman/elm-css), I wanted the ability to use [TailwindCSS](https://tailwindcss.com/). The Elm libraries haven’t kept up with Tailwind’s changes, which is fine; writing raw Tailwind CSS on Elm HTML functions is easy and co-located with the component you’re styling.

However, getting it to work in [elm-live](https://github.com/wking-io/elm-live), which [elm-book](https://github.com/dtwrks/elm-book) wraps, was a bit challenging. I wanted to layout how to get this to work in case you’d like to use Tailwind or your own CSS framework.

Why Elm-Live?
-------------

Almost all web projects nowadays require 3 important things:

1. Run a local web server so you can see your website as you write code
2. Refresh the browser window when you change code so you immediately see changes
3. Compile, lint, and/or bundle your code changes

Most front-end frameworks nowadays include these features as part of their CLI’s such as [Create React App](https://create-react-app.dev/), [Angular’s ng-cli](https://angular.io/), etc. For those that don’t, many will use a combination of some type of bundler like [Parcel](https://parceljs.org/), [Rollup](https://rollupjs.org/), or [Webpack](https://webpack.js.org/) and a browser refresher like [livereload](https://www.npmjs.com/package/livereload). This enables you to write code, save it, and immediately see the results. This in turn leads to fast feedback as you iterate all day in this build loop. The native Elm Reactor doesn’t offer this ability and elm-live fits the bill as a small Node.js library to enable this.

Two Terminals And package.json
------------------------------

For good or ill, I’m using the 2 Terminal approach, where you start your webserver in one Terminal, and your TailwindCSS CLI watcher in another. This allows elm-live to detect you’ve changed Elm, HTML, JavaScript, and CSS files and have it recompile your code and refresh the browser. The Tailwind CLI will detect you’ve changed HTML in CSS and will recompile your CSS. Both the Elm compiler and Tailwind’s version 3 CLI are fast enough that you don’t often get race conditions; meaning, your CSS is compiled first, then Elm, then the browser refreshes. If the Elm compiles first, then the CSS, the browser refresh is usually slow enough that the application still loads all the latest code.

The proper thing to do would have Tailwind finish first, then allow elm-live to run the elm compiler, but getting that to work requires a lot of engineering effort. The Elm Slack gave some great recommendations, but it seemed tricky since the elm-live library would have to “know” to wait. A challenge for another time.

Configuring Tailwind CLI
------------------------

Modifying your package.json to support Tailwind is basically a copy pasta from the documentation. In your package.json scripts, you just add:

```bash
"css:watch": "npx tailwindcss -i ./src/tailwind.css -o static/tailwind.build.css --watch"
```


Then, you run `npm run css:watch` and it’ll start recompiling your tailwind.build.css every time your CSS changes. The `tailwind.config.js` just needs to include Elm files in it, not just html and js which is the default:

```javascript
module.exports = {
  content: ['./src/**/*.{html,elm,js{']
}
```


Now you can write Tailwind CSS in your Elm functions, and it’ll trigger Tailwind to recompile the `tailwind.build.css` with the Tailwind CSS styles you’ve used.

Configuring Elm-Live
--------------------

Elm Book *comes* with elm-live built-in, but you don’t have to use that if you want more control. Instead, we’ll add another script to your `package.json`‘s scripts that starts elm-live to watch your Elm code, and recompile + refresh the browser.

```bash
"start": "elm-live src/Main.elm --pushstate --startpage=./static/index.html --dir=./static -- --output=static/elm.js
```


Now when you run `npm start`, it’ll do a bunch of good things:

- recompile your Elm code
- if successful, it’ll refresh the browser, else show a compiler error both in Terminal and in the browser
- utilize our own `index.html` file vs an elm-book generated one
- compile our Elm code to the `static` directory
- read all files from the `static` directory
- handle deep linking when you refresh the browser

That last one is why I wrote this blog post. That took me hours to get the right combination of directories, file names, and configs correct.

Configuring index.html
----------------------

Your `index.html` belongs in the static directory, not `src`. In it, we’ll put some important tags, which I’ll highlight below the code:

```html
<html>
<head>
  <link href="/tailwind.build.css" rel="stylesheet" type="text/css"></link>
<body>
  <main></main>
  <script src="./elm.js"></script>
  <script>
    var app = Elm.Main.init({ node: document.querySelector('main') })
  </script>
<body>
</html>
```


Note both `tailwind.build.css` and `elm.js` have a forward slash in front. This tells the webpage “look in the root/top level directory”. Since both files are next to index.html, it finds them. Sometimes you can get away with a dot slash like `./` or simply just the file names with no proceeding slash, but the web server gets confused that it’s running in root, but serving files from `static`. This combination makes it work.

Your Elm code and `tailwind.css` files can reside in `src`. The only caveat is I added `static/elm.js` and `static/tailwind.build.css` to `.gitignore` as I don’t want compiled assets checked into source control as they change often and that’s sometime a Continuous Integration server should build as an artifact, store, and deploy.

Conclusion
----------

Now when writing elm-book chapters, you can use Tailwind CSS styles in your Elm html functions and components:

```elm
h1 [ class "text-3xl font-bold underline" ] [ text "Sup" ]
```