mcpField
V
viewcomponent-coder

viewcomponent-coder

github.com/majesticlabs-devDevelopment5052 installsUpdated 7w ago
Repository

About viewcomponent-coder

viewcomponent-coder is a skill for building and maintaining Rails UIs with ViewComponent and view_component-contrib patterns. It covers component design, previews, slots, variants, and isolated tests.

Key features

  1. 01Component-based UI patterns
  2. 02Slots and style variants
  3. 03Lookbook previews
  4. 04Isolated component tests
  5. 05Rails helper and SVG guidance

Use cases

  1. 01Create reusable Rails UI components
  2. 02Add slots and visual variants
  3. 03Preview components in Lookbook
  4. 04Test components independently
  5. 05Refactor partials into components

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/majesticlabs-dev/majestic-marketplace --skill viewcomponent-coder

After installing, type/viewcomponent-coderin Claude Code to confirm it loaded.

Bundled filesreferences/patterns.mdreferences/style-variants.md

Details

From the project README

ViewComponent Patterns

Build modern, component-based UIs with ViewComponent using Evil Martians' view_component-contrib patterns.

When to Use This Skill

  • Creating ViewComponent classes
  • Implementing slots and style variants
  • Building Lookbook previews
  • Testing components in isolation
  • Refactoring partials to components

Core Principle: Components Over Partials

Prefer ViewComponents over partials for reusable UI.

Why ViewComponents?
  • Better encapsulation than partials
  • Testable in isolation
  • Object-oriented approach with explicit contracts
  • IDE support and type safety
  • Performance benefits (compiled templates)

Setup

# Gemfile
gem "view_component"
gem "view_component-contrib"  # Evil Martians patterns
gem "dry-initializer"         # Declarative initialization
gem "lookbook"                # Component previews
gem "inline_svg"              # SVG icons

Install with Rails template:

rails app:template LOCATION="https://railsbytes.com/script/zJosO5"

Base Classes

# app/components/application_view_component.rb
class ApplicationViewComponent < ViewComponentContrib::Base
  extend Dry::Initializer
end

# spec/components/previews/application_view_component_preview.rb
class ApplicationViewComponentPreview < ViewComponentContrib::Preview::Base
  self.abstract_class = true
end

Basic Component with dry-initializer

# app/components/button_component.rb
class ButtonComponent < ApplicationViewComponent
  option :text
  option :variant, default: -> { :primary }
  option :size, default: -> { :md }
end
<%# app/components/button_component.html.erb %>
<button class="btn btn-<%= variant %> btn-<%= size %>">
  <%= text %>
</button>

Style Variants DSL

Replace manual VARIANTS hashes with the Style Variants DSL:

class ButtonComponent < ApplicationViewComponent
  include ViewComponentContrib::StyleVariants

  option :text
  option :color, default: -> { :primary }
  option :size, default: -> { :md }

  style do
    base { %w[font-medium rounded-full] }

    variants {
      color {
        primary { %w[bg-blue-500 text-white] }
        secondary { %w[bg-gray-500 text-white] }
        danger { %w[bg-red-500 text-white] }
      }
      size {
        sm { "text-sm px-2 py-1" }
        md { "text-base px-4 py-2" }
        lg { "text-lg px-6 py-3" }
      }
    }

    # Apply when multiple conditions match
    compound(size: :lg, color: :primary) { "uppercase" }

    defaults { { color: :primary, size: :md } }
  end
end
<button class="<%= style(color:, size:) %>">
  <%= text %>
</button>

Component with Slots

class CardComponent < ApplicationViewComponent
  renders_one :header
  renders_one :footer
  renders_many :actions
end
<%= render CardComponent.new do |card| %>
  <% card.with_header do %>
    <h3>Title</h3>
  <% end %>

  <p>Body content</p>

  <% card.with_action do %>
    <%= helpers.link_to "Edit", edit_path %>
  <% end %>
<% end %>

Important Rules

1. Prefix Rails helpers with helpers.

<%# CORRECT %>
<%= helpers.link_to "Home", root_path %>
<%= helpers.image_tag "logo.png" %>
<%= helpers.inline_svg_tag "icons/user.svg" %>

<%# WRONG - will fail in component context %>
<%= link_to "Home", root_path %>

Exception: t() i18n helper does NOT need prefix:

<%= t('.title') %>

2. SVG Icons as Separate Files

Store SVGs in app/assets/images/icons/ and render with inline_svg gem:

<%= helpers.inline_svg_tag "icons/user.svg", class: "w-5 h-5" %>

Don't inline SVG markup in Ruby code - use separate files instead.

Conditional Rendering

class AlertComponent < ApplicationViewComponent
  option :message
  option :type, default: -> { :info }
  option :dismissible, default: -> { true }

  # Skip rendering if no message
  def render?
    message.present?
  end
end

Lookbook Previews

# spec/components/previews/button_component_preview.rb
class ButtonComponentPreview < ApplicationViewComponentPreview
  def default
    render ButtonComponent.new(text: "Click me")
  end

  def primary
    render ButtonComponent.new(text: "Primary", color: :primary)
  end

  def all_sizes
    render_with(wrapper: :flex_row) do
      safe_join([
        render(ButtonComponent.new(text: "Small", size: :sm)),
        render(ButtonComponent.new(text: "Medium", size: :md)),
        render(ButtonComponent.new(text: "Large", size: :lg))
      ])
    end
  end
end

Access at: http://localhost:3000/lookbook

Testing Components

RSpec.describe ButtonComponent, type: :component do
  it "renders button text" do
    render_inline(ButtonComponent.new(text: "Click me"))
    expect(page).to have_button("Click me")
  end

  it "applies style variant classes" do
    render_inline(ButtonComponent.new(text: "Save", color: :primary, size: :lg))
    expect(page).to have_css("button.bg-blue-500.text-lg")
  end
end

Detailed References

For advanced patterns and examples:

  • references/patterns.md - Slots, collections, polymorphic components, Turbo integration
  • references/style-variants.md - Full Style Variants DSL, compound variants, TailwindMerge

Comments

Markdown supported