Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save AnalyzePlatypus/77e7e164dd5eb80a70b5c0ee62f80fed to your computer and use it in GitHub Desktop.
Save AnalyzePlatypus/77e7e164dd5eb80a70b5c0ee62f80fed to your computer and use it in GitHub Desktop.
Rails + ViewComponent + TailwindCSS: Component CSS files

ViewComponents: Individual component CSS files

ViewComponent is a Rails library for building reusable front-end components.

I prefer to colocate my component-specific CSS within the component's directory.

For example, table is stored like this:

app/
├─ components/
   ├─ table/
      ├─ table_component.rb
      ├─ table_component.html.erb
      ├─ table_component.css
      ├─ table_component_controller.js

I think this layout is clean and easy to understand.

I heavily use Tailwind CSS, and I want any additional CSS files to be run through Tailwind's pipeline so I can use the @apply directive.

For my Stimulus.js setup, see this gist.

Setting up CSS imports for controllers

TL;DR:

  • A Rake task finds all component CSS, and generates a CSS file that contains all the imports.
  • Import the generated file into the main CSS file.
  • An optional process watches the app/components directory and regenerates the import file when changes are made.

1. Creating the task

Create lib/tasks/component_css.rake:

namespace :build do
  desc "Generate _components.css with imports for all component CSS files"
  task :component_css do
    components_dir = Rails.root.join("app/components")
    css_files = Dir.glob("#{components_dir}/**/*.css")

    imports = css_files.map { |file| "@import \"../../components/#{file.gsub("#{components_dir}/", "")}\";" }

    output_file = Rails.root.join("app/assets/stylesheets/_components.css")
    file_leading_comment = "/* This file is auto-generated by the task `rake build:component_css`.\n   Instead of modifying it directly, re-run the task. */\n\n"

    File.write(output_file, file_leading_comment + imports.join("\n"))

    puts "✅ Updated _components.css with #{css_files.count} components."
  end
end

Running rake build:component_css will generate the file app/assets/stylesheets/_components.css.

Make sure to check it in to your source control. Do not add this to .gitignore, even though it's a generated file.

2. Importing the generated file

Modify app/assets/tailwind/application.css:

@import "../stylesheets/_components.css" layer(utilities);

Re-run rake build:component_css after you add any new component CSS files.

(If you don't want to remember this, add a custom file watcher to your dev environment in the next step.)

3. [Optional] Add a watch process to regenerate the file when new CSS files are created

We'll use the listen gem to build a file watcher.

  1. bundle add listen --group=development
  2. Create the watch script and make it executable:
touch bin/component-css
chmod +x bin/component-css
  1. Write the watch script:
#!/usr/bin/env ruby

require 'listen'

# Path to watch
watch_dirs = ['app/components']

# Callback function
listener = Listen.to(*watch_dirs, only: /\.css$/) do |_modified, _added, _removed|
  puts "[WATCHER] Detected changes in component CSS files. Running rake task..."
  system("bin/rails build:component_css")
end

puts "[WATCHER] Watching component CSS..."
listener.start
sleep
  1. Add to Procfile (or Procfile.dev):
component_css: bin/component-css

Finally, restart the dev server: bin/dev

🎉 Done!

  • ✅ All component CSS is automatically inlined into your main CSS file
  • ✅ Tailwind can be used in all component CSS files
  • ✅ Any new component CSS files will be automatically detected and added to the build.

💡 Note: The app build process doesn't need to be changed, because the generated _component.css CSS file is checked in to source control

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment