skills/frontend-patterns/SKILL.md
Frontend patterns for Rails applications using Slim templates, Stimulus JavaScript framework, CSS with Optics utilities. Use when building views, adding interactivity, styling components, or when the user mentions Slim, Stimulus, JavaScript, CSS, or frontend development.
npx skillsauth add rolemodel/rolemodel-skills frontend-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build Rails frontend using Slim templates, Simple Form, Stimulus controllers, and Optics CSS.
Tech Stack: Slim (HTML) • Simple Form (forms) • Stimulus (JavaScript) • Optics (CSS)
See references/EXAMPLES.md for detailed code examples.
:)render 'partial', user:, active: trueUse Helpers for:
status_badge(status)Use Partials for:
_form.html.slim, _user_card.html.slimRule: Single element = helper. Structure/layout = partial.
app/views/
resource_name/
index.html.slim # Main views
_form.html.slim # Forms (shared by new/edit)
_resource_name.html.slim # Individual item
shared/
_status_badge.html.slim # Cross-feature components
-# Conditional classes
.card class=class_names('card--active': active, 'card--urgent': urgent)
-# Partial with locals
= render 'user_card', user:, show_actions: true
-# Collection rendering
= render partial: 'item', collection: @items
-# Conditional rendering
- if policy(@resource).update?
= link_to 'Edit', edit_path(@resource)
Always use Simple Form. Never use form_with or form_for.
Model form:
= simple_form_for @user do |f|
= f.input :name
= f.input :email, required: true
.form__actions
= link_to 'Cancel', :back, class: 'btn btn--outline'
= f.submit 'Save', class: 'btn btn--primary'
Non-model form (search, filters, bulk actions):
= simple_form_for :search, url: search_path, method: :get do |f|
= f.input :query
= f.submit 'Search', class: 'btn btn--primary'
Important: :symbol forms nest params: params.dig(:search, :query)
| Type | Example |
|------|---------|
| Text | = f.input :name |
| Textarea | = f.input :description, as: :text, input_html: { rows: 4 } |
| Association | = f.association :project, collection: @projects, prompt: 'Select...' |
| Select | = f.input :type, collection: @project_types, prompt: 'Select...' |
| Boolean | = f.input :active, as: :boolean |
| Date | = f.input :start_date, as: :date |
| Hidden | = f.hidden_field :organization_id, value: current_user.organization_id |
placeholder: - Placeholder textlabel: - Custom labelhint: - Help text below inputrequired: true - Mark as requireddisabled: true - Disable inputinput_html: {} - HTML attributes for input elementwrapper_html: {} - HTML attributes for wrapper div/ Basic collection
= f.input :category_id, collection: @categories
/ Custom label/value methods
= f.input :project_id,
collection: @projects,
label_method: :name,
value_method: :id,
prompt: 'Select project...'
Bulk action form:
= simple_form_for :bulk_action, url: bulk_path, method: :post, html: { id: 'bulk-form' } do |f|
= f.button 'Approve Selected', class: 'btn btn--primary'
-# Checkboxes reference form
= check_box_tag 'entry_ids[]', entry.id, false, form: 'bulk-form'
Modal form with external submit:
= simple_form_for @record, html: { id: 'modal-form' }, data: { turbo_frame: '_top' } do |f|
= f.input :reason, as: :text, input_html: { rows: 3, required: true }
-# In modal footer
= button_tag 'Submit', type: 'submit', form: 'modal-form', class: 'btn btn--primary'
html: {} - HTML attributes for form elementdata: {} - Data attributes (e.g., Turbo, Stimulus)url: - Form submission URL (required for :symbol forms)method: - HTTP method (:get, :post, :patch, :delete)See references/EXAMPLES.md for complex form examples.
JavaScript interactions using Stimulus framework.
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["output", "input"]
static values = { url: String, delay: Number }
static classes = ["hidden", "active"]
connect() {
// Initialization when controller connects to DOM
}
disconnect() {
// Cleanup when controller disconnects
}
action(event) {
// Action methods called from HTML
}
}
.component data-controller="example" data-example-url-value="/api/endpoint"
input data-example-target="input" data-action="input->example#search"
.results data-example-target="output"
disconnect() (timers, listeners)See references/EXAMPLES.md for complete controller examples.
class_names helper for conditional classes/ Custom component with BEM
.time-entry.time-entry--running
.time-entry__header
h3.time-entry__title = entry.description
.time-entry__body
span.time-entry__duration = entry.duration
/ Using class_names helper
.card class=class_names(
'card--active': @record.active?,
'card--featured': @record.featured?
)
Form actions pattern:
.form__actions
= link_to 'Cancel', :back, class: 'btn btn--outline'
= f.submit 'Save', class: 'btn btn--primary'
Empty state:
- if @items.empty?
= render 'shared/empty_state', title: 'No items', message: 'Create your first item.'
Authorization check:
- if policy(@resource).update?
= link_to 'Edit', edit_path(@resource), class: 'btn'
Turbo confirmation:
= button_to 'Delete', path, method: :delete, data: { turbo_confirm: 'Are you sure?' }
testing
Verify what Ruby versions actually exist and install a specific Ruby via rbenv. Use BEFORE asserting that any Ruby version does or doesn't exist (e.g., "Ruby 4.0 isn't out yet", "the latest Ruby is 3.x", "Ruby X.Y.Z doesn't exist"). Also use when the user asks "what's the latest Ruby", "is Ruby X out", "does Ruby X.Y exist", "install Ruby", "switch to Ruby X", "what Ruby is installed", or mentions a specific Ruby version you're unsure about. Claude's training data may be out of date — run `check.sh` first.
development
Trace code through the stack — upward to entry points, downward to data, or laterally across boundaries. Use when the user asks "where does this get called from", "what calls this method", "trace this through the stack", "how does this request flow", "where does this data come from", "follow this through the code", or pastes/selects a piece of code and wants to understand where it fits in the larger system.
tools
Pick the single highest-priority unresolved Sentry issue and hand it off to a fixer skill. Use when triaging Sentry errors, running automated issue triage, or when asked to fix the top Sentry issue in a project.
tools
Find and fix issues from Sentry using MCP. Use when asked to fix Sentry errors, debug production issues, investigate exceptions, or resolve bugs reported in Sentry. Methodically analyzes stack traces, breadcrumbs, traces, and context to identify root causes.