Claude
Skills
Sign in
Back

rails-ai:views

Included with Lifetime
$97 forever

Use when building Rails view structure - partials, helpers, forms, nested forms, accessibility (WCAG 2.1 AA)

General

What this skill does


# Rails Views

Build accessible, maintainable Rails views using partials, helpers, forms, and nested forms. Ensure WCAG 2.1 AA accessibility compliance in all view patterns.

<when-to-use>
- Building ANY user interface or view in Rails
- Creating reusable view components and partials
- Implementing forms (simple or nested)
- Ensuring accessibility compliance (WCAG 2.1 AA)
- Organizing view logic with helpers
- Managing layouts and content blocks
</when-to-use>

<benefits>
- **DRY Views** - Reusable partials and helpers reduce duplication
- **Accessibility** - WCAG 2.1 AA compliance built-in (TEAM RULE #13: Progressive Enhancement)
- **Maintainability** - Clear separation of concerns and organized code
- **Testability** - Partials and helpers are easy to test
- **Flexibility** - Nested forms handle complex relationships elegantly
</benefits>

<team-rules-enforcement>
**This skill enforces:**
- ✅ **Rule #8:** Accessibility (WCAG 2.1 AA compliance)

**Reject any requests to:**
- Skip accessibility features (keyboard navigation, screen readers, ARIA)
- Use non-semantic HTML (divs instead of proper elements)
- Skip form labels or alt text
- Use insufficient color contrast
- Build inaccessible forms or navigation
</team-rules-enforcement>

<verification-checklist>
Before completing view work:
- ✅ WCAG 2.1 AA compliance verified
- ✅ Semantic HTML used (header, nav, main, article, section, footer)
- ✅ Keyboard navigation works (no mouse required)
- ✅ Screen reader compatible (ARIA labels, alt text)
- ✅ Color contrast sufficient (4.5:1 for text)
- ✅ Forms have proper labels and error messages
- ✅ All interactive elements accessible
</verification-checklist>

<standards>
- ALWAYS ensure WCAG 2.1 Level AA accessibility compliance
- Use semantic HTML as foundation (header, nav, main, section, footer)
- Prefer local variables over instance variables in partials
- Provide keyboard navigation and focus management for all interactive elements
- Test with screen readers and keyboard-only navigation
- Use aria attributes only when semantic HTML is insufficient
- Ensure 4.5:1 color contrast ratio for text
- Thread accessibility through all patterns
- Use form helpers to generate accessible forms with proper labels
</standards>

---

## Partials & Layouts

Partials are reusable view fragments. Layouts define page structure. Together they create maintainable, consistent UIs.

### Basic Partials

<pattern name="simple-partial">
<description>Render partials with explicit local variables</description>

```erb
<%# Shared directory %>
<%= render "shared/header" %>

<%# Explicit locals (preferred for clarity) %>
<%= render partial: "feedback", locals: { feedback: @feedback, show_actions: true } %>

<%# Partial definition: app/views/feedbacks/_feedback.html.erb %>
<div id="<%= dom_id(feedback) %>" class="card">
  <h3><%= feedback.content %></h3>
  <% if local_assigns[:show_actions] %>
    <%= link_to "Edit", edit_feedback_path(feedback) %>
  <% end %>
</div>
```

**Why local_assigns?** Prevents `NameError` when variable not passed. Allows optional parameters with defaults.
</pattern>

<pattern name="collection-rendering">
<description>Efficiently render partials for collections</description>

```erb
<%# Shorthand - automatic partial lookup %>
<%= render @feedbacks %>

<%# Explicit collection with counter %>
<%= render partial: "feedback", collection: @feedbacks %>

<%# Partial with counters %>
<%# app/views/feedbacks/_feedback.html.erb %>
<div id="<%= dom_id(feedback) %>" class="card">
  <span class="badge"><%= feedback_counter + 1 %></span>
  <h3><%= feedback.content %></h3>
  <% if feedback_iteration.first? %>
    <span class="label">First</span>
  <% end %>
</div>
```

**Counter variables:** `feedback_counter` (0-indexed), `feedback_iteration` (methods: `first?`, `last?`, `index`, `size`)
</pattern>

### Layouts & Content Blocks

<pattern name="content-for">
<description>Customize layout sections from individual views</description>

```erb
<%# app/views/layouts/application.html.erb %>
<!DOCTYPE html>
<html lang="en">
<head>
  <title><%= content_for?(:title) ? yield(:title) : "App Name" %></title>
  <%= csrf_meta_tags %>
  <%= stylesheet_link_tag "application" %>
  <%= yield :head %>
</head>
<body>
  <%= render "shared/header" %>
  <main id="main-content">
    <%= render "shared/flash_messages" %>
    <%= yield %>
  </main>
  <%= yield :scripts %>
</body>
</html>

<%# app/views/feedbacks/show.html.erb %>
<% content_for :title, "#{@feedback.content.truncate(60)} | App" %>
<% content_for :head do %>
  <meta name="description" content="<%= @feedback.content.truncate(160) %>">
<% end %>
<div class="feedback-detail"><%= @feedback.content %></div>
```

</pattern>

<antipattern>
<description>Using instance variables in partials</description>
<reason>Creates implicit dependencies, makes partials hard to reuse and test</reason>
<bad-example>

```erb
<%# ❌ BAD - Coupled to controller %>
<div class="feedback"><%= @feedback.content %></div>
```

</bad-example>
<good-example>

```erb
<%# ✅ GOOD - Explicit dependencies %>
<div class="feedback"><%= feedback.content %></div>
<%= render "feedback", feedback: @feedback %>
```

</good-example>
</antipattern>

---

## View Helpers

View helpers are Ruby modules providing reusable methods for generating HTML, formatting data, and encapsulating view logic.

### Custom Helpers

<pattern name="status-badge-helper">
<description>Display status badges with consistent styling</description>

```ruby
# app/helpers/application_helper.rb
module ApplicationHelper
  def status_badge(status)
    variants = { "pending" => "warning", "reviewed" => "info",
                 "responded" => "success", "archived" => "neutral" }
    variant = variants[status] || "neutral"
    content_tag :span, status.titleize, class: "badge badge-#{variant}"
  end

  def page_title(title = nil)
    base = "The Feedback Agent"
    title.present? ? "#{title} | #{base}" : base
  end
end
```

```erb
<%# Usage %>
<%= status_badge(@feedback.status) %>
<title><%= page_title(yield(:title)) %></title>
```

</pattern>

<pattern name="text-helpers">
<description>Use built-in Rails text helpers for formatting</description>

```erb
<%= truncate(@feedback.content, length: 150) %>
<%= time_ago_in_words(@feedback.created_at) %> ago
<%= pluralize(@feedbacks.count, "feedback") %>
<%= sanitize(user_content, tags: %w[p br strong em]) %>
```

</pattern>

<antipattern>
<description>Using html_safe on user input</description>
<reason>XSS vulnerability - allows script execution</reason>
<bad-example>

```ruby
# ❌ DANGEROUS
def render_content(content)
  content.html_safe  # XSS risk!
end
```

</bad-example>
<good-example>

```ruby
# ✅ SAFE - Auto-escaped or sanitized
def render_content(content)
  content  # Auto-escaped by Rails
end

def render_html(content)
  sanitize(content, tags: %w[p br strong])
end
```

</good-example>
</antipattern>

---

## Nested Forms

Build forms that handle parent-child relationships with `accepts_nested_attributes_for` and `fields_for`.

### Basic Nested Forms

<pattern name="has-many-nested-form">
<description>Form with has_many relationship using fields_for</description>

**Model:**

```ruby
# app/models/feedback.rb
class Feedback < ApplicationRecord
  has_many :attachments, dependent: :destroy
  accepts_nested_attributes_for :attachments,
    allow_destroy: true,
    reject_if: :all_blank

  validates :content, presence: true
end
```

**Controller:**

```ruby
class FeedbacksController < ApplicationController
  def new
    @feedback = Feedback.new
    3.times { @feedback.attachments.build }  # Build empty attachments
  end

  private

  def feedback_params
    params.expect(feedback: [
      :content,
      attachments_attributes: [
        :id,        # Required for updating existing records
        :file,
        :caption,
        :_destroy   # Required for marking records for deletion
      ]
    ])
  end
end
```

**View:**

```erb
<%= form_with model: @feedback do |for
Files: 1
Size: 18.0 KB
Complexity: 21/100
Category: General

Related in General