Clojure Hiccup with Tailwind CSS
Let's discover a production-ready way to use Tailwind CSS with Clojure backend and Hiccup HTML library
I’ve been using Tailwind for multiple years now and it’s my go-to way of styling web applications. Tiny, composable classes are great for both React components and for Clojure Hiccup components. The issue that in Clojure environment I was really lazy and just used CDN link to add Tailwind to my hobby projects. That’s worked but it’s not ideal, size of the file is large and it’s hard to configure.
I’ve recently explored a proper way of using Tailwind and was surprised, it’s not much harder to configure it to work with Clojure backend compared to React and Javascript.
First of all, as I usually don’t have any Node in my Clojure projects, I didn’t really want to install it just for Tailwind. Luckily, there is a standalone Tailwind CLI available: https://tailwindcss.com/blog/standalone-cli
I’ve used Homebrew to install:
brew install tailwindcss
The next step is to run init command in the root of the project:
tailwindcss init
The result of this command is a new file `tailwind.config.js` that will be used for additional customisations.
The idea of Tailwind build step is to search source files for known Tailwind CSS classes and it’s language agnostic, it will basically search for a string like literals that match class names.
Let’s configure CLI to search for classes in Clojure source files:
content: [
"./src/**/*.{clj,cljs,cljc}"
]
In addition to that we will need an `input.css` file with a couple of imports:
@tailwind base;
@tailwind components;
@tailwind utilities;
That’s basically all we need, let’s run the build:
tailwindcss -i input.css -o ./resources/public/css/output.css
It should discover all Tailwind classes and generate much smaller result CSS file.
I usually use Babashka as my task runner, so there is an example of adding tasks to build, minify or watch Tailwind, `bb.edn`:
{:tasks
{:init (do (defn sh [& args]
(binding [*out* *err*]
(apply println "+" args))
(apply shell args))
(defn tailwindcss
[cmd]
(sh (str "tailwindcss -i input.css -o ./resources/public/css/output.css " cmd))))
tailwind:watch {:doc "Build TailwindCSS in watch mode"
:task (tailwindcss "--watch")}
tailwind:build {:doc "Build TailwindCSS output"
:task (tailwindcss nil)}
tailwind:minify {:doc "Build & minify TailwindCSS"
:task (tailwindcss "--minify")}}}
Now, for example, in development we can just start watch mode:
bb tailwind:watch
Other customisations
I’ve tried a couple of things, as setting custom container width and adding other fonts.
For the font, the easiest is to add it as import into the `input.css` file, I’ve used one from Google Fonts.
module.exports = {
content: [
"./src/**/*.{clj,cljs,cljc}"
],
theme: {
extend: {
container: {
center: true,
padding: '1rem',
screens: {
DEFAULT: '600px',
},
},
fontFamily: {
sans: ['Titillium Web', 'sans-serif'],
},
},
},
plugins: [],
};
Dynamic classes
I think it’s best to avoid dynamic class names, but if you have one of those, for example some string concatenation to generate class name — one way to solve it is to use `safelist` configuration:
module.exports = {
safelist: ["bg-red-500"]
};
If you are looking for a full example, I’ve added this Tailwind integration into my Clojure service template: https://github.com/andfadeev/clojure-service-template