Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's the 2.0 strategy for including styles seen in engines? #108

Closed
ianterrell opened this issue Dec 21, 2021 · 8 comments
Closed

What's the 2.0 strategy for including styles seen in engines? #108

ianterrell opened this issue Dec 21, 2021 · 8 comments

Comments

@ianterrell
Copy link

ianterrell commented Dec 21, 2021

The changes with Tailwind 3 mean that its server needs to know where all styles are to be found. How do we communicate that dynamically in the case of engine code?

Engine gems may be installed in multiple places, and in Ruby we can identify the filesystem location with MyEngine.root. In the prior release all we needed to do was not have them be purged which could be accomplished like this in production.rb:

  config.assets.css_compressor = Tailwindcss::Compressor.new(files_with_class_names:
    Rails.root.glob("app/views/**/*.*") +
    MyEngine::Engine.root.glob("app/views/**/*.*")
  )

Is there a supported strategy to communicate that in the tailwind.config.js file, which doesn't speak Ruby?

Or do we just need to generate that file with all locations added?

@cpjmcquillan
Copy link

I'm building a component library built with Tailwind at the moment and have run into this.

Initially I tried inserting something like MyEngine::Engine.root.join("app/components/**/*.*") into tailwind.config.js as part of an install task. Given the engine root will differ between environments I'm not sure how well this approach could work in practice, though.

How about if your engine builds an engine.css file and you add that as a separate stylesheet_link_tag in the application? I haven't tried this approach yet, but I think it might work reasonably well for now. One problem with this approach is the duplication of styles, but I'm not sure how to avoid that right now.

@ianterrell
Copy link
Author

I'm doing something similar with a rake task. I'm ignoring the config/tailwind.config.js file and generating it on relevant tasks with this (app is named "rice"):

namespace :rice do
  namespace :tailwind do
    desc "Generate a component-aware config/tailwind.config.js file"
    task :generate do
      components = [
        Foo::Engine,
        Bar::Engine,
      ]
      paths = components.map do |c|
        [
          "'#{c.root}/app/views/**/*'",
          "'#{c.root}/app/assets/javascripts/**/*'",
        ]
      end.flatten

      File.open(File.join(Rails.root, "config", "tailwind.config.js"), "w") do |f|
        f.write <<-JS
        const defaultTheme = require('tailwindcss/defaultTheme')

        module.exports = {
          content: [
            './app/helpers/**/*.rb',
            './app/javascript/**/*.js',
            #{paths.join(",\n            ")},
          ],
          theme: {
            extend: {
              fontFamily: {
                sans: ['Inter var', ...defaultTheme.fontFamily.sans],
              },
            },
          },
          plugins: [
            require('@tailwindcss/forms'),
            require('@tailwindcss/aspect-ratio'),
            require('@tailwindcss/typography'),
          ]
        }
        JS
      end
    end
  end
end

Rake::Task["tailwindcss:build"].enhance(["rice:tailwind:generate"])
Rake::Task["tailwindcss:watch"].enhance(["rice:tailwind:generate"])

So far so good, but we're in heavy development mode and don't have it in production yet.

@javierjulio
Copy link

javierjulio commented Jul 22, 2023

I've come across this need as well. I found a Stackoverflow answer that used Bundler for providing the path. An example:

const execSync = require('child_process').execSync;
const gemPath = execSync('bundle show gem-name', { encoding: 'utf-8' });
module.exports = {
  content: [
    gemPath.trim() + '/app/assets/stylesheets/**/*.css',
    gemPath.trim() + '/app/views/**/*.{erb,haml,html,rb}',
    // ...
  ]
}

Would either of you have happened to ship CSS in a Rails Engine that was written with Tailwind CSS but meant to be included and precompiled with the Rails app that is using the engine?

Is there a way to get Postcss imports in a Rails app to find and import Rails Engine based stylesheets? I figure it's a similar path problem to this.

Only way I've found around that is having to build an NPM package of stylesheets in the engine for the app then to reference via @import in its own Tailwind stylesheet so its included in the right layers (e.g. base, components, etc.).

@Paul-Bob
Copy link

Hello!

Would either of you have happened to ship CSS in a Rails Engine that was written with Tailwind CSS but meant to be included and precompiled with the Rails app that is using the engine?

@javierjulio I'm facing a similar problem right now. In Avo we're building our own style-sheet and we add it with a stylesheet_link_tag. The host app can have it's own separate style-sheets and also import them with the stylesheet_link_tag through a hook into our application.html.erb. The problem here is that sometimes there are conflicts between stylesheets even on simple and equal styles.

I'm trying to figure out ways to compile both, the Avo's and host app styles and include them with only one stylesheet_link_tag. I've been trying several approaches one of them is this one with the Bundler:

const execSync = require('child_process').execSync;
const gemPath = execSync('bundle show gem-name', { encoding: 'utf-8' });
module.exports = {
  content: [
    gemPath.trim() + '/app/assets/stylesheets/**/*.css',
    gemPath.trim() + '/app/views/**/*.{erb,haml,html,rb}',
    // ...
  ]
}

Unfortunately I'm facing some issues with it since Avo's module.exports become complex and I need to include more than just the content (plugins etc...).

Did you solved it? Is there a documented way to build both, gem's and host's app tailwind together?! Or is there another better path to follow? We're open to suggestions.

@javierjulio
Copy link

@Paul-Bob hello. I had looked into Avo back then and how they did this but didn't like it because of the CSS conflict use case so I didn't take that approach for ActiveAdmin (working on a new version with Tailwind). I think having compiled styles in an engine can make sense as an optimization if no real need to customize, meant to be standalone, e.g. Sidekiq. I'm not sure if you can precompile Avo's styles in your app because they are already compiled and shipped with the gem itself so all your app is doing is loading a plain CSS file (no Tailwind compilation performed), if I understand right. With what's been suggested here, you should be able to precompile both engine and app stylesheets but Avo is a unique case so I'm not sure, sorry.

@Paul-Bob
Copy link

I'm not sure if you can precompile Avo's styles in your app because they are already compiled and shipped with the gem itself so all your app is doing is loading a plain CSS file (no Tailwind compilation performed), if I understand right.

You understand that right @javierjulio. Actually I'm working on improving that process on Avo itself and allow the Tailwind compilation to perform on the host app. I implemented a POC following the Stackoverflow answer that used Bundler for providing the path but I wonder if there is another straight forward way of doing this integration between the gem and the app.

@javierjulio
Copy link

@Paul-Bob not that I know of, sorry. That's why I commented here to share what I found and see what others are doing or would suggest/comment with. If you do find another approach, please let us know. I'd like to know what other approach can be taken. It just doesn't seem that the new asset gems (e.g. jsbundling-rails) account for this when it comes to engines.

@flavorjones
Copy link
Member

Let's consolidate this conversation into one place: #355

@flavorjones flavorjones closed this as not planned Won't fix, can't repro, duplicate, stale Apr 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants