I’ve built a few Roda applications, but more recently been working with Rails (professionally and personally).

Whilst I enjoy working with Rails, the simplicity and speed of Roda still appeals to me and I was back tinkering last weekend. Since I last Roda, however, Tailwind has burst on to the scene and I would struggle to build anything without it

I couldn’t find much documented about getting Roda setup with Tailwind. I have an older application using this combo, but it hasn’t been brought up to date with Tailwind’s JIT mode. After getting a new application up and running with this super fast and powerful coombo, I hope others may benefit from a write up.

TL;DR you can grab a basic setup from this Github repo if you know what you're doing.

What you’ll need

I don’t know enough about the JS ecosystem to advise; I typically just yarn add and off we go….

  • Ruby 3.0.2 (or anything 2.7+ really, but the version doesn’t matter)
  • Yarn to install Tailwind (see here for more)

Roda Setup

If you’re new to Roda I’d advise you look at the Roda + Sequel Stack Skeleton to get a database enabled app skeleton.

To make things clearer here, though I’ve started with the absolute minimum to get Roda and Tailwind JIT setup.

We’ll end up with the following structure

.
├── assets
│   └── css
│       ├── tailwind.css
│       └── tailwind.out.css
├── views
│   ├── index.erb
│   └── layout.erb
├── Gemfile
├── Gemfile.lock
├── Procfile.dev
├── app.rb
├── config.ru
├── package.json
├── tailwind.config.js
└── yarn.lock

It might be helpful to set this structure up first (some of the files will be create automatically for you later on, so the command below should suffice)

mkdir -p assets/css views
touch assets/css/tailwind.css views/index.erb views/lyaout.erb Procfile.dev app.rb config.ru

To get started, create your Gemfile:

source 'https://rubygems.org'

gem 'erubi'
gem 'foreman'
gem 'puma'
gem 'rack-unreloader'
gem 'roda'
gem 'tilt'

Run bundle install to get setup. A usable CRUD app is likely to have far more gems than this, of course.

Tailwind Setup

Before we can join up Roda and Tailwind, we need to install Tailwind.

yarn add tailwindcss@latest autoprefixer@latest

If you like the clean, lightweight nature of Roda, you may not relish the idea of having a node_modules hanging around, but it is essential for this to work. The folder ends up a paltry 51M so it’s not too bad…

Once that’s finished you can generate your Tailwind config with:

npx tailwindcss init

This will generate a tailwind.config.js file. Open up this file and make it look like this:

module.exports = {
  mode: "jit",
  purge: ["./views/*.erb"],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};

You’ll find yourself addding the mode line and the purge field. This will enable JIT mode,, and purge is needed to watch your view files in Roda and automatically regenerate your CSS.

We need to make a quick change to the package.json file so we can generate CSS. Open this file and make it look like this (ignore the version numbers for the dependencies):

{
  "dependencies": {
    "autoprefixer": "^10.4.0",
    "tailwindcss": "^2.2.19"
  },
  "scripts": {
    "build:css": "npx tailwindcss -o assets/css/tailwind.out.css",
    "watch:css": "npx tailwindcss -o assets/css/tailwind.out.css --watch "
  }
}

These 2 script commands will allow us to watch and regenerate the output CSS.

Our (small) CSS File

Through the magic of Tailwind, we can just have our assets/css/tailwind.css file look like this:

@tailwind base;
@tailwind components;
@tailwind utilities;

You can now run yarn build:css and you will see assets/css/tailwind.out.css output!

Joining things up

We have the very basics of Roda (but no application) and Tailwind setup, so we need a few bits of plumbing. Normally using Roda in development we’d just expect our config.ru to do everything we need to do, but because we want to use Tailwind’s JIT feature, we need a parallel process to monitor and regenerate the CSS we’re consuming Roda. To do this we’re using foreman with a Procfile.dev - we won’t need the Procfile in production because we’ll want our build process to build a static CSS output.

Update your Procfile.dev to look like:

web: rackup
css: yarn watch:css --watch

We’ll then be able to run everything with bundle exec foreman start -f Procfile.dev - this will both serve your Roda app and regenerate CSS using the yarn script we added into our package file earlier.

Barebones Roda App

There are a few ways to structure a Roda app, and the below is the bare minimum I could come up with!

require 'bundler'
Bundler.require

class App < Roda
  plugin :assets, css: ['tailwind.out.css'], css_opts: {style: :compressed, cache: false}, timestamp_paths: true
  compile_assets if ENV['RACK_ENV'] == 'production'
  plugin :render, escape: true, layout: './layout'

  route do |r|
    r.assets
    r.root do
      view "index"
    end
  end
end

Here the assets plugin is looking at the OUTPUT of our Tailwind build script. The watch command in our Procfile will keep an eye on any changes in the purge: directive so as you make changes to files, you will see the JIT compiler running and will spit out a new CSS in milliseconds (true).

I’m not detailing the rest of the file setup here, so you can take a look at the github repo. Do make sure you’re using the assets plugin correctly and adding <%== assets(:css) %> to your layout file!

Assuming you’re running with Foreman, any changes you now make to your ERB files (and then save) will trigger a JIT refresh and you’ll see something like this:

20:19:13 css.1  | Rebuilding...
20:19:13 css.1  | Done in 26ms.

The time will obviously depend on how many ERB files it has to scan, but expect it to be pretty quick.

Next Steps

If you’ve got your dev setup working well,

Deployment

Think about your deployment script to run build:css so it generates the correct tailwind.out.css file when your application deploys. You can see above we’re going to compile_assets! in production. Roda can go even further and generate manifest files with hashes to further speed up the process.

Purge Settings for folder structure

This example is very basic. You’ll likely have nested view folders and perhaps some components in Ruby files, so you can update your tailwind.config.js to include more paths for analysis. If you do not include the correct path globs here, you will not get the CSS classes output!

purge: ["./views/**/*.erb", "./components/**/*.rb"]

Speed!

I’ve been using this setup on a production site (coupled with a same-server Alpine JS file) and out of the box you get a pretty favourable Pagespeed using Roda, Puma and Tailwind like this. A lot of it is down to Roda, but the speed of development and deployment from Tailwind + JIT makes this stack an absolute speed-demon!

Roda and Tailwind JIT
If you got this far and are looking for the easy option, you can grab a basic setup from this Github repo to get up and running.