How to use tailwind in a rails engine? According to the documentation supplying a css argument to the Rails generator should work
Rails 7.0.2.2 engine generated using
rails plugin new tailtest --mountable --full -d postgresql --css tailwind
This generates the engine with Postgresql but does nothing with tailwind at all, and following manual installation instructions fail too.
Running, as per documentation, bundle add tailwindcss-rails adds tailwind to the gemfile rather than the engines tailtest.gemspec
So after adding the dependency to the gemspec
spec.add_dependency "tailwindcss-rails", "~> 2.0"
and running bundle install does install the engine however the rest of the manual installation fails
then adding the require to lib/engine.rb
require "tailwindcss-rails"
module Tailtest
class Engine < ::Rails::Engine
isolate_namespace Tailtest
end
end
then running the install process fails
rails tailwindcss:install
Resolving dependencies...
rails aborted!
Don't know how to build task 'tailwindcss:install' (See the list of available tasks with `rails --tasks`)
Did you mean? app:tailwindcss:install
Obviously the app:tailwindcss:install command fails too.
So I am probably missing an initializer of some sort in the engine.rb file but no idea on what it should be.
It is the same idea as How to set up importmap-rails in Rails 7 engine?. We don't need to use the install task. Even if you're able to run it, it's not helpful in the engine (see the end of the answer for explanation).
Also
rails plugin newdoesn't have a--cssoption. To see available options:rails plugin new -h.Update engine's gemspec file:
Update engine.rb:
Update assets manifest:
Update engine's layout:
bundle showcommand will give us the path where the gem is installed, so we can copy a few files:Copy tailwind.config.js file from
tailwindcss-rails:Copy application.tailwind.css file into any directory to fit your setup:
Because
tailwindcss-railsuses standalone executable, we don't need node or rails to compile the stylesheets. We just need to get to the executable itself.Executable is located here https://github.com/rails/tailwindcss-rails/tree/v2.0.8/exe/. Instead of running the build task https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/tasks/build.rake we can just call the executable directly.
Use
-woption to start watch mode.The output file should match the name in
stylesheet_link_tag "my_engine".Now that you have a plain my_engine.css file, do with it what you want. Use it in the layout, require it from the main app application.css. The usual rails asset pipeline rules apply.
If you want to put all that into a task, use
Engine.rootto get the paths.From the engine directory:
Make your own install task if you have a lot of engines to set up:
Install task reference:
https://github.com/rails/rails/blob/v7.0.2.4/railties/lib/rails/tasks/framework.rake#L8
https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/tasks/install.rake
Watch task reference:
https://github.com/rails/tailwindcss-rails/blob/v2.0.8/lib/tasks/build.rake#L10
Update How to merge two tailwinds.
Above setup assumes the engine is its own separate thing, like admin backend, it has its own routes, templates, and styles. If an engine functionality is meant to be mixed with the main app, like a view_component collection, then tailwind styles will override each other. In this case isolating engine styles with a prefix could work:
https://tailwindcss.com/docs/configuration#prefix
The reason that tailwind styles don't mix is because most of the selectors have the same specificity and the order is very important.
So here is an example. Main app with an engine, both using tailwind, both compile styles separately, tailwind configs are only watching one file from the engine and one from the main app, only using @tailwind utilities; directive:
Engine template, that we want to use in the main app, should work fine:
But when rendered in the main app it never turns blue. Here is the demonstration set up:
And it looks like this:
^ you can hit run and click "full page". Main app
bg-red-500selector is last so it overrides enginessm:bg-blue-500selector, media queries don't add to specificity score. It's the same reason you can't override, say,mt-1withm-2, margin top comes later in the stylesheet. This is why@layerdirectives are important.The only way around this is to watch the engine directory when running tailwind in the main app, so that styles are compiled together and in the correct order. Which means you don't really need tailwind in the engine:
Other ways I tried, like running 6 tailwind commands for each layer for main app and engine, so that I can put them in order, better but was still out of order a bit and duplicated. Or doing an @import and somehow letting postcss-import know where to look for engine styles (I don't know, I just symlinked it into node_modules to test), but this still required tailwind to watch engine files.
I did some more digging, tailwind cli has a
--contentoption, which will overridecontentfrom tailwind.config.js. We can use it to setup a new task:https://github.com/rails/tailwindcss-rails/blob/v2.0.21/lib/tasks/build.rake#L11