Skip to content

TailwindCSS integration

INFO

This integration is especially useful when you style UI extension points with custom CSS or Tailwind classes, like custom pages and custom fields, where classes may not exist in the precompiled Avo bundle.

This integration is built for Tailwind CSS 4.

Avo ships with precompiled styles that are easy to use and work out of the box, so most apps do not need to care about extra build steps.

When Avo detects tailwindcss-ruby, it automatically enables this integration and builds an app-level Avo stylesheet with zero (or close to zero) configuration.

If you have tailwindcss-ruby installed (or tailwindcss-rails, which depends on tailwindcss-ruby) but you do not use custom Tailwind/CSS classes in custom tools, ejected components, custom fields, or other extended UI areas, you can opt out and keep using only Avo's precompiled styles.

When enabled, this integration compiles a stylesheet that includes:

  • Avo core styles
  • loaded Avo plugin styles
  • your host app Avo styles from app/assets/stylesheets/avo/**/*.css
  • utility classes discovered under your Rails app/ directory (configurable; see below), plus Avo and plugin sources

The output file is:

app/assets/builds/avo/application.css

This integration writes to the same logical Avo stylesheet path (avo/application) so your app always loads one stylesheet entrypoint.

Auto-enable behavior

Add tailwindcss-ruby to your app:

ruby
gem "tailwindcss-ruby"

Once present, Avo will:

  • auto-build in development (on server boot)
  • hook into assets:precompile in production

WARNING

Avo's build emits Tailwind v4 syntax. If your app pulls Tailwind in through tailwindcss-rails, it must be >= 4.0 — on tailwindcss-rails 3.x the integration stays disabled even though tailwindcss-ruby is present. Apps that depend on tailwindcss-ruby directly (no tailwindcss-rails) aren't affected by this check.

TIP

We highly recommend running this integration through bin/dev with a Procfile.dev watcher process:

bash
web: bin/rails server -p 3000
avo_css: bin/rails avo:tailwindcss:watch

This keeps your Avo Tailwind build running in parallel with the Rails server, so style changes are compiled automatically while you work without extra manual steps.

Add your custom Avo styles

Add your Avo-specific stylesheets under app/assets/stylesheets/avo/.

Every CSS file in this path is included in the final avo/application.css build.

For example:

css
/* app/assets/stylesheets/avo/buttons.css */
@layer components {
  .avo-btn-highlight {
    @apply px-3 py-2 rounded-md bg-indigo-600 text-white;
  }
}

Override styles and scripts (avo-overrides.css / avo-overrides.js)

Avo ships two empty files and loads them automatically on every screen:

  • avo-overrides.css — loaded after avo/application.css, so its rules win the cascade.
  • avo-overrides.js — loaded after avo/application.js, so window.Stimulus is available.

Both are the no-build escape hatch: unlike app/assets/stylesheets/avo/, they are not run through the Tailwind build — they are served as-is. Use them for quick tweaks, re-theming, or small behaviors without touching the pipeline. To customize them, eject the file into your own app (Avo's copy is then shadowed by yours):

bash
# just the stylesheet
rails g avo:eject --partial :avo_overrides_css
# just the script
rails g avo:eject --partial :avo_overrides_js
# both at once
rails g avo:eject --partial :asset_overrides

Because avo-overrides.css loads after Avo's own stylesheet, overriding Avo's CSS variables here is enough to re-skin the whole interface — no @apply, no build step. See the Theming guide for how to re-color and re-skin Avo through these variables.

Add behavior with Stimulus

avo-overrides.js runs once, but Avo navigates with Turbo. Register a Stimulus controller (Stimulus re-connects it on every visit) or attach a turbo:load listener — avoid one-shot DOM edits, they won't survive navigation:

js
// app/assets/javascripts/avo-overrides.js
document.addEventListener("turbo:load", () => {
  // runs on every Turbo visit
})

// or register a controller against Avo's Stimulus instance:
// window.Stimulus.register("my-controller", class extends Controller { ... })

TIP

For custom JS that needs bundling or imports (esbuild/importmap), use asset handling and the JavaScript guide instead. avo-overrides.js is for small, dependency-free snippets.

Disable integration (opt-out)

If your app has tailwindcss-ruby (directly or via tailwindcss-rails) but you do not need Avo custom utility coverage, disable the integration in your initializer:

ruby
Avo.configure do |config|
  config.tailwindcss_integration_enabled = false
end

Default is true.

The default is intentionally true to preserve the zero-configuration experience: if your app has tailwindcss-ruby and you start adding classes/styles in custom Avo UI extension points, this integration should just work without you needing to think about setup details.

Host content scan paths (tailwindcss_content_sources)

Avo writes Tailwind v4 @source directives into tmp/avo/avo.tailwind.input.css so the compiler can see utility classes used in templates and Ruby/HTML. By default, the host-side scan is limited to Rails.root.join("app") (the usual Rails tree: views, components, app/avo resources, and similar).

That keeps discovery focused on application code and avoids scanning unrelated directories under the project root.

Configure extra roots (or override the list entirely) in config/initializers/avo.rb:

ruby
Avo.configure do |config|
  config.tailwindcss_content_sources = [
    Rails.root.join("app"),
    Rails.root.join("lib", "components")
  ]
end

Each entry may be an absolute path or a path relative to Rails.root. Directories that do not exist are skipped.

To include the entire project root in the host scan (everything under Rails.root), set:

ruby
config.tailwindcss_content_sources = [Rails.root]

Debugging

If classes are missing from avo/application.css, check these files first:

  • input file generated by Avo: tmp/avo/avo.tailwind.input.css
  • output file generated by Tailwind: app/assets/builds/avo/application.css

How it works, in short:

  • Avo generates tmp/avo/avo.tailwind.input.css on each build/watch cycle.
  • That input file imports Avo/plugin application.css files and your host styles from app/assets/stylesheets/avo/**/*.css.
  • It also adds @source entries for Avo, loaded plugins, and your configured host directories (default: Rails.root.join("app")) so Tailwind can discover utility classes from templates and code.
  • Tailwind then compiles everything into app/assets/builds/avo/application.css.

Quick checks:

  • confirm the integration is enabled (config.tailwindcss_integration_enabled = true);
  • ensure tailwindcss-ruby is installed (directly, or via tailwindcss-rails >= 4.0);
  • verify your custom styles are under app/assets/stylesheets/avo/;
  • run with the watcher (bin/dev) so changes are rebuilt continuously.