
Production-ready UI components for Rails applications. Copy-paste ERB partials styled with Tailwind CSS 4.0 and optional Stimulus controllers.

**What you get:**
- **20+ components** — From layouts to forms, navigation to feedback
- **Zero dependencies** — Just Tailwind CSS and optionally Stimulus
- **Token-driven theming** — Familiar shadcn/ui CSS variables for color, plus tokens for shape, elevation, focus rings and weight
- **Rails conventions** — ERB partials, data attributes, form helpers

<div class="not-prose mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2">
  <img src="/images/components/light.png" alt="Maquina Components light theme" class="rounded-lg border border-zinc-200 dark:border-zinc-800" />
  <img src="/images/components/dark.png" alt="Maquina Components dark theme" class="rounded-lg border border-zinc-200 dark:border-zinc-800" />
</div>

> **Already on 0.5.1?** 0.6.0 is a deliberately breaking release: engine CSS moved into `@layer components`, and radius, elevation, focus rings and weights became tokens. One change affects every existing app and fails silently — the unlayered `*` rule in your installed `theme.css`. Run the scanner, then read the guide:
>
> ```bash
> bundle update maquina-components
> bin/rails maquina:doctor
> ```
>
> → [Upgrading to 0.6.0](/documentation/components/upgrading/) · [Theming](/documentation/components/theming/)

---

## Demo Application

**[View Live Demo →](https://demo.maquina.app)**

Explore all components in action without installing anything. The demo showcases light/dark themes, color themes, and responsive layouts.

For local development, clone the [components repository](https://github.com/maquina-app/maquina_components):

```bash
git clone https://github.com/maquina-app/maquina_components.git
cd maquina_components/test/dummy
bin/rails server
```

Visit `http://localhost:3000` to explore the components locally.

---

## Quick Start

### 1. Add the Gem

```ruby
# Gemfile
gem "maquina-components"
```

```bash
bundle install
```

### 2. Run the Install Generator

```bash
bin/rails generate maquina_components:install
```

This adds the engine CSS import, theme variables (shadcn/ui convention), a shape/state token block for radius, elevation, focus rings and weights, and a helper file for icon customization.

Re-running the generator is safe: it is idempotent, appends each block only once, and never rewrites your palette.

### 3. Start Using Components

```erb
<%%= render "components/card" do %>
  <%%= render "components/card/header" do %>
    <%%= render "components/card/title", text: "Welcome" %>
  <%% end %>
  <%%= render "components/card/content" do %>
    <p>Your content here</p>
  <%% end %>
<%% end %>
```

For form elements, use data attributes:

```erb
<%%= form_with model: @user do |f| %>
  <%%= f.text_field :email, data: { component: "input" } %>
  <%%= f.submit "Save", data: { component: "button", variant: "primary" } %>
<%% end %>
```

---

## AI-Assisted Development

Use the [Maquina UI Standards](/documentation/ai-tools/maquina-ui-standards/) Claude Code plugin to generate views that follow component conventions automatically.

Instead of correcting AI-generated code ("use the card partial, not a div"), the plugin teaches Claude your component patterns:

```
> Create the users index view with a table showing name, email, and status
```

Claude generates code using your actual components — proper partials, correct data attributes, and consistent patterns.

---

## Available Components

### Layout

| Component | Description |
|-----------|-------------|
| [Sidebar](/documentation/components/sidebar/) | Collapsible navigation with mobile support and keyboard shortcuts |
| [Header](/documentation/components/header/) | Page header for sidebar layouts with breadcrumbs and actions |
| [Drawer](/documentation/components/drawer/) | Slide-out panel with overlay, persistence, and keyboard shortcut |

### Content

| Component | Description |
|-----------|-------------|
| [Card](/documentation/components/card/) | Content containers with header, body, and footer sections |
| [Alert](/documentation/components/alert/) | Callouts with 4 variants and icon support |
| [Badge](/documentation/components/badge/) | Status indicators with 7 variants and 3 sizes |
| [Table](/documentation/components/table/) | Responsive data tables with striped and bordered variants, sticky headers, and a collection helper |
| [Empty State](/documentation/components/empty/) | Placeholder for no-data scenarios with icons and actions |
| [Separator](/documentation/components/separator/) | Horizontal or vertical divider |
| [Stats](/documentation/components/stats/) | Metric cards in a responsive grid |

### Navigation

| Component | Description |
|-----------|-------------|
| [Breadcrumbs](/documentation/components/breadcrumbs/) | Navigation with responsive collapsing support |
| [Dropdown Menu](/documentation/components/dropdown-menu/) | Actions menu triggered by a button with keyboard navigation |
| [Pagination](/documentation/components/pagination/) | Navigation for paginated content with Pagy integration |

### Interactive

| Component | Description |
|-----------|-------------|
| [Calendar](/documentation/components/calendar/) | Date selection with single and range modes |
| [Combobox](/documentation/components/combobox/) | Searchable dropdown with keyboard navigation and filtering |
| [Date Picker](/documentation/components/date-picker/) | Popover calendar triggered by a button for date selection |
| [Toggle Group](/documentation/components/toggle-group/) | Single or multiple selection button groups |

### Feedback

| Component | Description |
|-----------|-------------|
| [Toast](/documentation/components/toast/) | Non-intrusive notifications with auto-dismiss and variants |

### Forms

| Component | Description |
|-----------|-------------|
| [Form Components](/documentation/components/form/) | Inputs, selects, checkboxes styled with data attributes |

---

## Prerequisites

The generator requires **tailwindcss-rails**:

```bash
bundle add tailwindcss-rails
bin/rails tailwindcss:install
```

---

## Stimulus Setup

Interactive components (Sidebar, Dropdown Menu, Toggle Group, Breadcrumbs, Combobox, Toast) require Stimulus. With importmaps:

```ruby
# config/importmap.rb
pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
pin_all_from "app/javascript/controllers", under: "controllers"
```

```javascript
// app/javascript/application.js
import "@hotwired/turbo-rails"
import { Application } from "@hotwired/stimulus"
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"

const application = Application.start()
application.debug = false
window.Stimulus = application

eagerLoadControllersFrom("controllers", application)
```

Static components (Badge, Card, Alert, Button, form elements) work without JavaScript.

### Extending Component Behavior

Every component merges your `data:` hash with its own data attributes. Identity keys (component, variant, size) always win, but `controller` and `action` concatenate — so you can attach your own Stimulus behavior to any component without losing the built-in one:

```erb
<%%= render "components/combobox", name: "country",
      data: { controller: "analytics", action: "change->analytics#track" } %>
<%%# renders data-controller="combobox analytics" %>
```

---

## Icons

Components render icons through the `icon_for` helper, which falls back to a built-in set of inline SVGs (check, chevrons, calendar, search, mail, trash, and more):

```erb
<%%= icon_for :check, class: "size-4" %>
<%%= icon_for :trash, class: "size-4", stroke_width: 1.5 %>
```

To use your own icon system (Heroicons, Lucide, inline SVG files), override `main_icon_svg_for` in the generated `MaquinaComponentsHelper` — `icon_for` consults it first and only falls back to the built-ins when it returns nil:

```ruby
# app/helpers/maquina_components_helper.rb
def main_icon_svg_for(name)
  lucide_icon(name)
end
```

---

## Theme Variables

Colors are CSS variables following the [shadcn/ui theming convention](https://ui.shadcn.com/docs/theming).

Each one is defined twice, which Tailwind CSS v4 requires: in `:root` for the value, and in `@theme` so it also becomes a utility (`bg-primary`, `text-muted-foreground`).

```css
:root {
  --primary: oklch(0.488 0.243 264.376);
  --primary-foreground: oklch(0.985 0 0);
}

@theme {
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
}
```

Edit the values in `:root` to match your brand — the generator installs neutral grays. Adding a semantic color works the same way: declare it in `:root`, then mirror it in `@theme` if you want the utility.

Everything that is not a color — shape, focus rings, elevation, weights, control marks — is a token too, and a theme changes values, not selectors. See **[Theming](/documentation/components/theming/)** for the full token table, ready-made themes, and how to pin a single component.

---

## Icon System

Components use `icon_for(:name)` to render icons. Override icon lookup in `app/helpers/maquina_components_helper.rb`:

```ruby
module MaquinaComponentsHelper
  def main_icon_svg_for(name)
    case name
    when :home
      <<~SVG
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"/>
          <path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
        </svg>
      SVG
    end
  end
end
```

Icons are sourced from [Lucide](https://lucide.dev). Copy SVG code directly from their website.

---

## Generator Options

```bash
# Skip theme variables (if you already have them)
bin/rails generate maquina_components:install --skip-theme

# Skip helper creation
bin/rails generate maquina_components:install --skip-helper

# Skip both
bin/rails generate maquina_components:install --skip-theme --skip-helper
```

---

## File Structure After Setup

```
app/
├── assets/tailwind/
│   └── application.css               # Theme + engine import
├── helpers/
│   └── maquina_components_helper.rb  # Icon override
├── javascript/
│   └── application.js                # Stimulus init
└── views/layouts/
    └── application.html.erb          # Layout with components
```

---

## Troubleshooting

### Generator Issues

**"tailwindcss-rails doesn't appear to be installed"**

Install it first:

```bash
bundle add tailwindcss-rails
bin/rails tailwindcss:install
```

### Runtime Issues

**Sidebar trigger not working**

- Ensure Stimulus is initialized
- Verify provider wraps both sidebar and content
- Check browser console for errors

**Styles not applying**

- Verify engine CSS is imported after `@import "tailwindcss";`
- Check that `@theme` block exists with color bindings
- Restart dev server after CSS changes

**Dark mode not working**

- Add `.dark` class to `<html>` element
- Ensure `.dark { }` block has variable overrides

---

## Next Steps

<div class="not-prose mt-8 grid grid-cols-1 gap-4 sm:grid-cols-2">
  <a href="/documentation/components/sidebar/" class="group relative rounded-2xl border border-zinc-200 p-6 hover:border-[#50B1FD]/50 dark:border-zinc-800 dark:hover:border-[#50B1FD]/50 transition">
    <h3 class="font-sora font-semibold text-zinc-900 dark:text-white group-hover:text-[#50B1FD] transition">
      Sidebar
    </h3>
    <p class="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
      Build your application layout with collapsible navigation.
    </p>
  </a>

  <a href="/documentation/components/card/" class="group relative rounded-2xl border border-zinc-200 p-6 hover:border-[#50B1FD]/50 dark:border-zinc-800 dark:hover:border-[#50B1FD]/50 transition">
    <h3 class="font-sora font-semibold text-zinc-900 dark:text-white group-hover:text-[#50B1FD] transition">
      Card
    </h3>
    <p class="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
      Display content in containers with header, body, and footer.
    </p>
  </a>

  <a href="/documentation/components/form/" class="group relative rounded-2xl border border-zinc-200 p-6 hover:border-[#50B1FD]/50 dark:border-zinc-800 dark:hover:border-[#50B1FD]/50 transition">
    <h3 class="font-sora font-semibold text-zinc-900 dark:text-white group-hover:text-[#50B1FD] transition">
      Form Components
    </h3>
    <p class="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
      Style inputs, selects, and buttons with data attributes.
    </p>
  </a>

  <a href="/documentation/ai-tools/maquina-ui-standards/" class="group relative rounded-2xl border border-zinc-200 p-6 hover:border-[#50B1FD]/50 dark:border-zinc-800 dark:hover:border-[#50B1FD]/50 transition">
    <h3 class="font-sora font-semibold text-zinc-900 dark:text-white group-hover:text-[#50B1FD] transition">
      AI-Assisted Development
    </h3>
    <p class="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
      Use Claude Code to generate views with component conventions.
    </p>
  </a>
</div>
