Using modern css tooling with Obelisk.
This repository is an example of how one can use Obelisk with a css processor/compiler. We're specifically going to be setting up the tailwindcss CLI tool and Tailwind CSS.
We'll be putting together a nix expression that runs the tailwindcss compiler with the tailwind plugin on our input css file. This nix expression will be supplied to our Obelisk application as the generator for our application's static files. As we modify our application (code or stylesheets), Obelisk's ob run command will automatically regenerate the static assets when needed.
We'll set up a simple Tailwind-CLI project, as described here.
First, let's create a folder static/src in our Obelisk project:
mkdir -p static/src
cd static/srcHere's a package.json specifying our dependencies, including tailwindcss, the tailwind ui plugin, and the autoprefixer postcss plugin:
{
"name": "styles",
"version": "0.0.0",
"author": "Obsidian Systems, LLC",
"dependencies": {
"tailwindcss": "4.1.13"
}
}Next, we'll write a basic tailwind configuration file (tailwind.config.js):
module.exports = {
purge: {
enabled: true,
content: ['./frontend/**/*.hs']
},
darkMode: false,
theme: {
extend: {},
},
variants: {},
plugins: [],
}Note that the tailwind file refers to ./frontend/**/*.hs: tailwind will read the our frontend haskell source files looking for references to tailwind classes so that it can purge unneeded classes from the generated CSS.
Finally, we'll write a basic tailwind stylesheet:
@import "tailwindcss";We'll use the node2nix command to generate nix expressions for our node project. This will lock down our dependencies and make it possible to compile our css files in a nix build.
cd static/src
$(nix-build node2nix.nix)/bin/node2nix -18 -d --include-peer-dependenciesnode2nix will generate a few different nix files. You won't have to re-run node2nix unless you change the contents of package.json.
Now that we've nixified the Tailwind, PostCSS and their dependencies, we can write a nix expression that will actually build the css files (and other static assets) for our project using the node project as an input.
We'll put this code in static/default.nix:
{ pkgs }:
let
# The nixified node project was generated from a package.json file in src using node2nix
# See https://github.com/svanderburg/node2nix#using-the-nodejs-environment-in-other-nix-derivations
nodePkgs = (pkgs.callPackage ./src {
inherit pkgs;
nodejs = pkgs.nodejs-18_x;
}).shell.nodeDependencies;
nixpkgs = import ./src/nixpkgs.nix {};
# The frontend source files have to be passed in so that tailwind's purge option works
# See https://tailwindcss.com/docs/optimizing-for-production#removing-unused-css
frontendSrcFiles = ../frontend;
in pkgs.stdenv.mkDerivation {
name = "static";
src = ./src;
buildInputs = [pkgs.nodejs nixpkgs.tailwindcss_4];
installPhase = ''
mkdir -p $out/css
mkdir -p $out/images
# Setting up the node environment:
ln -s ${nodePkgs}/lib/node_modules ./node_modules
export PATH="${nodePkgs}/bin:$PATH"
# We make the frontend haskell source files available here:
# This corresponds to the path specified in tailwind.config.js
ln -s ${frontendSrcFiles} frontend
# Run the tailwindcss compiler:
tailwindcss -i css/styles.css -o $out/styles.css
# We can write other commands to produce more static files as well:
cp -r images/* $out/images/
'';
}In your Obelisk project's default.nix, set the staticFiles option to the nix derivation you created above:
{ system ? builtins.currentSystem
, obelisk ? import ./.obelisk/impl {
inherit system;
iosSdkVersion = "13.2";
}
}:
with obelisk;
project ./. ({ pkgs, ... }: {
staticFiles = import ./static { inherit pkgs; };
android.applicationId = "systems.obsidian.obelisk.examples.tailwind";
android.displayName = "Obelisk + Tailwind CSS";
ios.bundleIdentifier = "systems.obsidian.obelisk.examples.tailwind";
ios.bundleName = "Obelisk + Tailwind CSS";
})Whenever you change a frontend file or modify something in static/ that would have an impact on the static files derivation, you'll see the following messages in the ob run console:
Static assets being built...
Static assets built and symlinked to static.out
That's also where you'll see errors related to the compilation of static files.
Note that changing frontend files doesn't have to, in principle, cause a rebuild of the static files. In our case it does because we're using the Tailwind CSS purge functionality.
Thanks to Emre Can for the photo used in this example.

