{"id":22762334,"url":"https://github.com/fernandes/ui","last_synced_at":"2026-02-11T08:11:27.300Z","repository":{"id":265987094,"uuid":"862361285","full_name":"fernandes/ui","owner":"fernandes","description":"UI","archived":false,"fork":false,"pushed_at":"2024-12-31T18:18:19.000Z","size":484,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-07T07:51:56.556Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fernandes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-24T13:25:14.000Z","updated_at":"2024-12-31T18:18:22.000Z","dependencies_parsed_at":"2024-12-01T23:48:46.287Z","dependency_job_id":null,"html_url":"https://github.com/fernandes/ui","commit_stats":null,"previous_names":["fernandes/ui"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandes%2Fui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandes%2Fui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandes%2Fui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandes%2Fui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fernandes","download_url":"https://codeload.github.com/fernandes/ui/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248959949,"owners_count":21189970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-11T10:07:34.953Z","updated_at":"2026-02-11T08:11:27.294Z","avatar_url":"https://github.com/fernandes.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# UI Engine\n\n![UI Logo](./logo.png)\n\nA Rails engine providing a component library with Tailwind CSS 4 support. Designed to work seamlessly with multiple Rails versions (6, 7, and 8) and different asset pipelines (Propshaft, Sprockets, and Node/Bun).\n\n## Features\n\n- ✅ **Tailwind CSS 4** - CSS-first configuration using `@import`, `@source`, and `@theme`\n- ✅ **Multi-Pipeline Support** - Works with Propshaft (Rails 8), Sprockets (Rails 6/7), and Node/Bun bundlers\n- ✅ **Importmap Ready** - JavaScript modules via importmaps or bundlers\n- ✅ **Flexible Installation** - Automatic detection of your app's asset pipeline\n- ✅ **Rails 6-8 Compatible** - Supports Rails 6.0+\n\n## Installation\n\n### Ruby Gem\n\nAdd to your Gemfile:\n\n```ruby\ngem \"fernandes-ui\"\n```\n\nThen run:\n\n```bash\nbundle install\n```\n\n### JavaScript (Importmaps)\n\nFor Rails 7+ with importmaps, pin the engine in `config/importmap.rb`:\n\n```ruby\npin \"ui\", to: \"ui.esm.js\", preload: true\npin_all_from \"ui/controllers\", under: \"ui/controllers\"\n```\n\nThe gem automatically provides these pins via the engine.\n\nThen setup the JS controllers\n\n```javascript\nimport { registerControllers } from \"ui\"\nregisterControllers(application)\n```\n\n### JavaScript (jsbundling-rails)\n\nFor apps using Bun, esbuild, or Webpack:\n\n```bash\nbun add @fernandes/ui\n# or\nnpm install @fernandes/ui\n```\n\nThen import in your JavaScript:\n\n```javascript\nimport { Application } from \"@hotwired/stimulus\"\nimport * as UI from \"@fernandes/ui\"\n\nconst application = Application.start()\nUI.registerControllers(application)\n```\n\n### CSS (cssbundling-rails)\n\nFor apps using cssbundling-rails, use the generators to get UI css copied and then imported automatically.\n\n```bash\nrails generate ui:css\nrails generate ui:install\n```\n\n### CSS (Propshaft/Sprockets)\n\nFor apps using asset pipeline without bundler, import in your Tailwind config:\n\n```css\n@import \"tailwindcss\";\n@import \"ui/application.css\";\n\n/* Scan bundled gem files */\n@source \"../../../.bundle/ruby/*/gems/fernandes-ui-*/app/**/*.{erb,rb,js}\";\n```\n\n## Usage\n\nThe UI Engine provides CSS variables, Stimulus controllers, and components. You configure Tailwind CSS in your application to scan the engine files.\n\n### Selective Controller Import\n\nYou can import only the controllers you need for better performance and tree-shaking:\n\n#### **Import Individual Controllers**\n\n```javascript\n// With jsbundling-rails\nimport { Application } from \"@hotwired/stimulus\"\nimport { HelloController, DropdownController } from \"@fernandes/ui\"\n\nconst application = Application.start()\napplication.register(\"ui--hello\", HelloController)\napplication.register(\"ui--dropdown\", DropdownController)\n```\n\n#### **Import from Controller Files (Importmaps)**\n\n```javascript\nimport { Application } from \"@hotwired/stimulus\"\nimport HelloController from \"ui/controllers/hello_controller\"\n\nconst application = Application.start()\napplication.register(\"ui--hello\", HelloController)\n```\n\n#### **Selective Registration with Helper**\n\n```javascript\nimport { Application } from \"@hotwired/stimulus\"\nimport { HelloController, registerControllersInto } from \"@fernandes/ui\"\n\nconst application = Application.start()\n\n// Register only specific controllers\nregisterControllersInto(application, {\n  \"ui--hello\": HelloController\n})\n```\n\n### Benefits of Selective Import\n\n- **Tree-shaking** - Bundlers eliminate unused code\n- **Flexibility** - Choose exactly what to import\n- **Performance** - Load only what you need\n- **Compatibility** - Works with both importmaps and bundlers\n\n## Available Components\n\nThe engine currently provides the following Stimulus controllers and components:\n\n### **Accordion** (`ui--accordion`)\nCollapsible content sections with smooth CSS transitions. Supports single mode (only one item open) and multiple mode (multiple items can be open simultaneously).\n\n**Usage:**\n```erb\n\u003c%= render \"ui/accordion/accordion\", type: \"single\" do %\u003e\n  \u003c%= render \"ui/accordion/item\", value: \"item-1\", initial_open: true do %\u003e\n    \u003c%= render \"ui/accordion/trigger\" do %\u003e\n      Getting Started\n    \u003c% end %\u003e\n    \u003c%= render \"ui/accordion/content\" do %\u003e\n      \u003cp\u003eFollow the installation instructions to get started.\u003c/p\u003e\n    \u003c% end %\u003e\n  \u003c% end %\u003e\n\u003c% end %\u003e\n```\n\n### **Dropdown** (`ui--dropdown`)\nDropdown menu component with toggle functionality.\n\n### **Hello** (`ui--hello`)\nExample component demonstrating Stimulus controller basics.\n\n### CSS Variables\n\nImport engine CSS variables in your Tailwind config:\n\n```css\n@import \"ui/application.css\";\n```\n\nThis provides variables like `--ui-primary`, `--ui-spacing-*`, etc.\n\n### Tailwind Configuration\n\nYour `app/assets/stylesheets/application.tailwind.css` should include:\n\n```css\n@import \"tailwindcss\";\n@import \"ui/application.css\";  /* Engine CSS variables */\n\n/* Scan engine files for Tailwind classes */\n@source \"../../javascript/**/*.js\";\n@source \"../../views/**/*.erb\";\n\n/* If using gems, also scan the gem paths */\n@source \"../../../.bundle/ruby/*/gems/ui-*/app/views/**/*.erb\";\n@source \"../../../.bundle/ruby/*/gems/ui-*/app/javascript/**/*.js\";\n\n@theme {\n  /* Your customizations */\n}\n```\n\n### Building CSS\n\n```bash\n# One-time build\nbun run build:css\n\n# Watch for changes (development)\nbun run build:css:watch\n\n# Production build (minified)\nbun run build:css:prod\n```\n\n### Development Workflow\n\n```bash\n# Start Rails server + Tailwind watch\nbin/dev\n```\n\nThis runs both the Rails server and Tailwind CSS watcher simultaneously.\n\n## Development\n\n### Running the Dummy App\n\nThe engine includes a dummy Rails application for testing:\n\n```bash\ncd test/dummy\nbin/dev\n```\n\nThis will start both the Rails server and Tailwind CSS watch process.\n\n### Building CSS\n\n```bash\n# One-time build\nbun run build:css\n\n# Watch for changes\nbun run build:css:watch\n\n# Production build (minified)\nbun run build:css:prod\n```\n\n### Project Structure\n\n```\napp/\n├── assets/\n│   ├── stylesheets/ui/\n│   │   └── application.css     # CSS variables (shipped with gem)\n│   └── javascripts/\n│       ├── ui.js               # UMD bundle\n│       └── ui.esm.js           # ESM bundle\n├── behaviors/ui/               # Shared behavior modules (always loaded)\n│   ├── button_behavior.rb\n│   └── ...\n├── components/ui/              # Phlex components (loaded if phlex-rails \u003e= 2.0)\n│   ├── button.rb\n│   └── ...\n├── view_components/ui/         # ViewComponents (loaded if view_component \u003e= 3.0)\n│   ├── button_component.rb\n│   └── ...\n├── javascript/ui/\n│   ├── index.js                # JavaScript entry point\n│   ├── controllers/            # Stimulus controllers\n│   └── utils/                  # Utility modules\n├── views/ui/                   # ERB partials (always available)\n│   ├── _button.html.erb\n│   └── ...\n└── controllers/ui/             # Engine controllers\n\nconfig/\n└── importmap.rb                # Importmap pins for JS modules\n\nlib/\n└── ui/\n    ├── configuration.rb        # UI.configure settings\n    └── engine.rb               # Engine configuration\n```\n\n## Component Formats\n\nThe engine supports three component formats:\n\n| Format | Directory | Required Gem | Description |\n|--------|-----------|--------------|-------------|\n| **ERB Partials** | `app/views/ui/` | None | Always available, uses Behaviors |\n| **Phlex** | `app/components/ui/` | `phlex-rails` \u003e= 2.0 | Ruby-first components |\n| **ViewComponent** | `app/view_components/ui/` | `view_component` \u003e= 3.0 | GitHub's ViewComponent |\n\n### Automatic Loading\n\nBy default, the engine automatically detects which gems are available and loads the appropriate components:\n\n- **Behaviors** (`app/behaviors/ui/`) - Always loaded (required for ERB)\n- **Phlex** - Loaded if `phlex-rails` gem is present and version \u003e= 2.0.0\n- **ViewComponent** - Loaded if `view_component` gem is present and version \u003e= 3.0.0\n\n### Manual Configuration\n\nYou can override automatic detection by configuring before Rails loads. Add to `config/application.rb` **before** `Bundler.require`:\n\n```ruby\n# config/application.rb\nrequire_relative \"boot\"\n\n# Configure UI before Bundler.require loads the engine\nrequire \"ui/configuration\"\nUI.configure do |c|\n  c.enable_phlex = true           # Force enable (skip version check)\n  c.enable_view_component = false # Force disable (don't load even if gem exists)\nend\n\nrequire \"rails/all\"\nBundler.require(*Rails.groups)\n# ...\n```\n\n#### Configuration Options\n\n| Value | Behavior |\n|-------|----------|\n| `nil` (default) | Auto-detect: loads if gem available AND version meets minimum |\n| `true` | Force enable: loads if gem available (ignores version check) |\n| `false` | Force disable: never loads, even if gem is available |\n\n#### Use Cases\n\n**Force Enable** - Testing with unsupported gem versions:\n```ruby\nUI.configure do |c|\n  c.enable_phlex = true  # Use Phlex even if version \u003c 2.0\nend\n```\n\n**Force Disable** - Legacy gems in app that shouldn't be used:\n```ruby\nUI.configure do |c|\n  c.enable_view_component = false  # Don't load even though gem exists\nend\n```\n\n**ERB Only** - Minimal setup without optional dependencies:\n```ruby\nUI.configure do |c|\n  c.enable_phlex = false\n  c.enable_view_component = false\nend\n```\n\n## Architecture: Why Tailwind Config Lives in Host App\n\n**Key Principle:** The engine provides **styled components**, not Tailwind configuration.\n\n- **Engine provides:** CSS variables, JavaScript, views with Tailwind classes\n- **Host app provides:** Tailwind compilation, `@source` configuration, theme customization\n\nThis approach:\n- ✅ Allows each app to customize Tailwind output\n- ✅ Enables apps to scan their own files + engine files\n- ✅ Avoids conflicts with existing Tailwind setups\n- ✅ Keeps engine gem size small (no compiled CSS)\n- ✅ Supports different Rails versions and asset pipelines\n\n## Tailwind CSS 4 Configuration\n\nYour application (not the engine) configures Tailwind. Create `app/assets/stylesheets/application.tailwind.css`:\n\n```css\n@import \"tailwindcss\";\n@import \"ui/application.css\";  /* Import engine CSS variables */\n\n/* Scan YOUR app files */\n@source \"../../javascript/**/*.js\";\n@source \"../../views/**/*.erb\";\n\n/* Scan bundled gem files */\n@source \"../../../.bundle/ruby/*/gems/fernandes-ui-*/app/**/*.{erb,rb,js}\";\n\n@theme {\n  /* Customize using engine variables */\n  --color-primary: var(--ui-primary);\n}\n```\n\n## Contributing\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffernandes%2Fui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffernandes%2Fui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffernandes%2Fui/lists"}