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.
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!
