A Rails engine providing reusable UI components with Tailwind CSS styling and Stimulus controllers. Built with ViewComponent and designed for easy integration into Rails engines.
The Aeno gem provides the following components:
Button- Styled button with variants (default, white, light, outline) and sizes (xsmall, small, large)InputText- Text input field with wrapper and label supportInputPassword- Password input with show/hide toggle functionalityInputSelect- Styled select dropdownInputWrapper- Wrapper component for form inputs with labels
Page- Page container componentCard- Card container with stylingTable- Styled table componentDropdown- Interactive dropdown menu with Stimulus controllerEmpty- Empty state componentSpinner- Loading spinner with size and variant options
<%= ui("button", label: "Click me", variant: :default) %>
<%= ui("input_text", name: "email", label: "Email") %>
<%= ui("input_password", name: "password", label: "Password") %>
<%= ui("card") do %>
<h2>Card content</h2>
<% end %>Add this line to your engine's Gemfile:
gem "aeno", path: "../ui" # or from a gem serverAnd then execute:
$ bundleThe Aeno gem provides convenience helpers to quickly integrate it into your Rails engine.
In your lib/your_engine/engine.rb:
require "aeno"
module YourEngine
class << self
attr_accessor :importmap
end
class Engine < ::Rails::Engine
isolate_namespace YourEngine
# Setup assets and importmap with Aeno gem integration
Aeno::EngineHelpers.setup_assets(self, namespace: YourEngine)
Aeno::EngineHelpers.setup_importmap(self, namespace: YourEngine)
end
endWhat this does:
- Configures asset paths for your engine and the Aeno gem
- Sets up importmap that automatically includes Aeno gem's JavaScript and components
- Configures cache sweepers for development hot-reloading
Create app/components/your_engine/application_view_component.rb:
module YourEngine
class ApplicationViewComponent < Aeno::ApplicationViewComponent
def controller_name
# Override to use your engine's namespace for Stimulus controllers
name = self.class.name
.sub(/^YourEngine::/, "")
.sub(/::Component$/, "")
.underscore
"your-engine--#{name.gsub('/', '--').gsub('_', '-')}"
end
end
endWhat this provides:
- All Aeno gem helper methods (stimulus_controller, stimulus_target, etc.)
- TailwindMerge integration for CSS class merging
- Style variants support via ViewComponentContrib
- Dry::Initializer for clean option definitions
In app/javascript/your_engine/controllers/index.js:
import { application } from "your_engine/controllers/application";
import { eagerLoadEngineControllersFrom } from "aeno/controllers/loader";
// Load your engine's controllers
eagerLoadEngineControllersFrom("your_engine/controllers", application);
eagerLoadEngineControllersFrom("your_engine/components", application);
// Load Aeno gem component controllers
eagerLoadEngineControllersFrom("aeno/components", application);What this does:
- Uses the shared controller loader from Aeno gem
- Automatically registers Stimulus controllers with correct naming conventions
- Supports multiple namespaces (your engine + Aeno gem)
In app/views/layouts/your_engine/application.html.erb:
<!DOCTYPE html>
<html>
<head>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<!-- Include Aeno gem stylesheets -->
<%= stylesheet_link_tag "aeno/application", media: "all" %>
<%= stylesheet_link_tag "aeno/tailwind", media: "all" %>
<!-- Your engine's stylesheets -->
<%= stylesheet_link_tag "your_engine/application", media: "all" %>
<%= stylesheet_link_tag "your_engine/tailwind", media: "all" %>
<!-- Importmap with Aeno gem integration -->
<%= javascript_importmap_tags "your_engine/application", importmap: YourEngine.importmap %>
</head>
<body>
<%= yield %>
</body>
</html>In config/importmap.rb:
pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "your_engine/application"
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
pin_all_from(
YourEngine::Engine.root.join("app/javascript/your_engine/controllers"),
under: "your_engine/controllers",
)
pin_all_from(
YourEngine::Engine.root.join("app/components/your_engine"),
under: "your_engine/components",
to: "your_engine"
)Note: Aeno gem's importmap is automatically included via Aeno::EngineHelpers.setup_importmap
Sets up asset paths for your engine with Propshaft/Sprockets support.
Configures importmap with:
- Your engine's importmap
- Aeno gem's importmap (automatic)
- Cache sweepers for development
- Controller action integration
Intelligent Stimulus controller loader for Rails engines that:
- Parses importmap to find controllers
- Registers with correct naming conventions (e.g.,
aeno--button,your-engine--views--index) - Handles both
controller.jsand*_controller.jsnaming - Supports multiple namespaces
cd ui
bin/rails serverbundle exec rake app:aeno:tailwind_engine_watch- Rails >= 8.0.3
- importmap-rails ~> 2.2.2
- turbo-rails ~> 2.0
- stimulus-rails ~> 1.3
- tailwindcss-rails ~> 4.3.0
- view_component ~> 4.0
- view_component-contrib ~> 0.2.5
- dry-effects ~> 0.5.0
- tailwind_merge ~> 1.3
The gem is available as open source under the terms of the MIT License.