agentsclimarketplace

Writing views

Skill chrisrackstallion/pitchd-rails-cursor-plugin/skills/writing-views

Write Rails views, partials, and helpers following DHH/37signals conventions — ERB templates as HTML-first documents, strict-local partials, collection rendering, helpers for presentation logic, Tailwind utility-first styling, layouts with content_for. Stimulus and Turbo — writing-hotwire skill. User- facing static copy and locales — writing-i18n skill. JS delivery (importmap, bundling) — writing-javascript skill. Tailwind build/config/global CSS — writing-css-tailwind skill. Use when creating new views, extracting partials, writing helpers, building forms, styling with Tailwind in templates, working with layouts, or when the user mentions views, partials, ERB, helpers, Tailwind, Stimulus, Turbo, importmap, JavaScript, I18n, locales, or templates.From its SKILL.md

Install
npx -y skills add chrisrackstallion/pitchd-rails-cursor-plugin --skill writing-views

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

10.4 KB, as published. Nobody here has run it

Writing Rails Views

<objective> Write views that are HTML documents with the minimum Ruby necessary to display data. Views are the final rendering step — they present what the model provides, they don't compute it. Partials are the unit of reuse. Helpers format data for display. Tailwind utilities style directly in the markup. Stimulus data attributes provide interactivity; Hotwire (Turbo + Stimulus) is documented in the writing-hotwire skill. Tailwind **build** and **global** CSS policy — writing-css-tailwind skill. Follow DHH/37signals conventions: ERB, strict locals, utility-first CSS in templates, collection rendering, and clarity over cleverness. </objective>

Process

1. Determine What You're Building

TaskAction
New view templateRead references/patterns.md, scaffold the template
Extract a partialRead references/patterns.md § Partials
Collection renderingRead references/patterns.md § Collections
New helper methodRead references/patterns.md § Helpers
User-facing strings / localesRead ../writing-i18n/references/patterns.md
Tailwind in templates (utilities, partials, class_names, helpers)Read references/patterns.md § Tailwind
Tailwind pipeline, config, @apply, v3/v4, global CSSRead ../writing-css-tailwind/references/patterns.md
Stimulus / Turbo / HotwireRead ../writing-hotwire/references/patterns.md
Importmap, package.json, JS entrypointsRead ../writing-javascript/references/patterns.md
Layout / content_forRead references/patterns.md § Layouts
Form templateRead references/patterns.md § Forms
Mailer templates (*_mailer/)Read ../writing-mailers/references/patterns.md
Turbo Frame/Stream markupRead ../writing-hotwire/references/patterns.md
Links vs buttonsRead references/patterns.md § Links and Buttons
Empty state / error displayRead references/patterns.md § Empty States
Code reviewRead all references, review against conventions

2. View Structure

Organise every action template like this:

<%# app/views/articles/show.html.erb %>
<% provide(:title, @article.title) %>

<article id="<%= dom_id(@article) %>" class="mx-auto max-w-2xl py-8">
  <header>
    <h1 class="text-3xl font-bold text-gray-900"><%= @article.title %></h1>
    <p class="mt-2 text-sm text-gray-500"><%= @article.byline %></p>
  </header>

  <div class="prose mt-6">
    <%= @article.body %>
  </div>

  <section id="comments" class="mt-12">
    <h2 class="text-xl font-semibold text-gray-900">Comments</h2>
    <%= render @article.comments %>
    <%= render "comments/form", article: @article, comment: @comment %>
  </section>
</article>

Key principles:

  • Set page metadata via provide (single value) or content_for (block) at the top
  • Use dom_id for element IDs — never hand-roll them
  • Style with Tailwind utilities directly in the markup
  • Delegate sub-sections to partials
  • Keep the template under ~50 lines — extract partials when it grows
  • The controller builds objects (@comment = @article.comments.build) — views don't instantiate models

Static copy is hardcoded here (and throughout references/patterns.md) to keep the examples focused — in application code, user-facing strings use t() with keys under config/locales (see the writing-i18n skill).

3. Partial Structure

Every partial uses strict locals and receives everything it needs through locals:

<%# app/views/comments/_comment.html.erb %>
<%# locals: (comment:) %>
<div id="<%= dom_id(comment) %>" class="border-b border-gray-100 py-4">
  <p class="text-gray-800"><%= comment.body %></p>
  <span class="mt-1 text-xs text-gray-400">
    <%= comment.creator.name %> · <%= time_ago_in_words(comment.created_at) %> ago
  </span>
</div>

4. Decision Framework

Before writing markup, ask these questions:

"Where does this logic belong?"

  • Formatting a value for display → helper method
  • Simple string formatting (.truncate, .humanize, time_ago_in_words) → fine inline in ERB
  • Deciding what to show (permission, state) → model predicate, controller sets the data
  • Reusable HTML fragment → partial
  • Page-specific HTML → inline in the action template
  • Shared page chrome → layout

"Should this be a partial?"

  • Used in more than one place → yes, extract
  • Represents a single domain concept (a comment, a card, a form) → yes, extract
  • Template exceeds ~50 lines → extract sub-sections
  • Needs to be a Turbo Frame target → yes, extract into its own partial
  • Reusing a styled block (same markup + Tailwind classes) → extract partial, not @apply
  • One-off structural HTML → keep inline

"Helper or model method?"

  • Domain question (is this published? can this user edit?) → model method
  • Display formatting (badge, formatted date, reading time) → helper
  • Conditional CSS class composition → helper with class_names
  • Multi-line HTML with structure (sections, lists, forms) → partial
  • Small composed elements (badge with icon, link with label) → helper with tag builder is fine

"How should this be styled?"

  • Standard presentation → Tailwind utilities directly in the markup
  • Conditional appearance (state-dependent) → class_names helper
  • Same class pattern repeated in many partials → helper returning class string
  • Design tokens (colors, fonts, spacing) → extend tailwind.config, not arbitrary values
  • Last resort for truly atomic repeated styles → @apply in CSS

"link_to or button_to?"

  • Navigating to a page (GET) → link_to
  • Performing an action (POST/PATCH/DELETE) → button_to
  • Destructive action → button_to with data: { turbo_confirm: "..." }

5. Anti-Patterns

Anti-PatternInstead
Instance variables in partialsStrict locals — <%# locals: (article:) %>
Queries or business logic in viewsController loads data into instance variables
Instantiating models in views (Comment.new)Controller builds the object (@comment = @article.comments.build)
if/elsif/else trees for variantsSeparate templates or polymorphic partial rendering
link_to for mutations (DELETE, POST)button_to with method:
Inline <script> or <style> in templatesStimulus controllers, Tailwind utilities
@apply for every styled blockExtract a partial — partials are your components
Arbitrary Tailwind values everywhere (bg-[#1a2b3c])Extend the Tailwind config with project tokens
Repeated class strings across many templatesHelper returning class string, or extract partial
Current.user checks inside partialsPass permission as boolean local, or use model predicate at the template level
Formatting in the template (.strftime, complex conditionals)Helper method (but .truncate, .humanize are fine inline)
Hand-rolled element IDs (id="article-42")dom_id(@article)
Partials without locals: declarationAlways declare strict locals
HTML string building in helpersUse tag builder, content_tag, or a partial
Deep nesting of render calls (3+ levels)Flatten — max 2 levels of nesting
content_for used for control flowcontent_for / provide is for content injection into layouts
Fat helpers with many lines of HTMLExtract to a partial instead

6. Naming Conventions

ThingConventionExamples
Action templatesaction.html.erbshow.html.erb, index.html.erb
Partials_noun.html.erb (singular)_article.html.erb, _comment.html.erb
Form partials_form.html.erb_form.html.erb
Shared partialsapp/views/shared/ or app/views/application/shared/_flash.html.erb
Helper modulesResourceHelper matching controllerArticlesHelper, CommentsHelper
Helper methodsVerb or noun describing the outputreading_time, status_badge, btn_classes
LayoutsPurpose-namedapplication.html.erb, admin.html.erb, mailer.html.erb
Turbo stream templatesaction.turbo_stream.erbcreate.turbo_stream.erb
Spacer templates_spacer.html.erb_spacer.html.erb

7. Verification

Before finishing, verify:

  • No instance variables in partials — everything comes through strict locals
  • Every partial has a <%# locals: (...) %> magic comment
  • No queries, business logic, or complex conditionals in templates
  • No model instantiation in views — controller builds objects
  • Presentation logic lives in helpers, not inline in ERB
  • Element IDs use dom_id, not hand-rolled strings
  • Collections rendered with render @collection, not manual loops
  • Forms use form_with model:, not form_tag or form_for
  • Mutations use button_to, not link_to with turbo_method
  • Destructive actions have data: { turbo_confirm: "..." }
  • Templates are under ~50 lines — sub-sections extracted to partials
  • Turbo Frames wrap the right granularity of content
  • Page titles and meta set via provide / content_for, not hardcoded in layout
  • Helpers return strings and have no side effects
  • Tailwind utilities used directly in markup — partials for reuse, not @apply
  • Conditional classes use class_names helper
  • Stimulus attributes use data-controller, data-action, data-[name]-target
  • No inline <script> or <style> tags

References

For detailed patterns and examples, see references/patterns.md. For Tailwind build and global styling policy, see ../writing-css-tailwind/references/patterns.md.

Keep looking

Skills are one crate of 626,474. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.