maquina-ui-standards

Build production-quality Rails UIs with maquina_components — ERB partials styled with Tailwind CSS 4 and data attributes, inspired by shadcn/ui.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "maquina-ui-standards" with this command: npx skills add maquina-app/maquina_components/maquina-app-maquina-components-maquina-ui-standards

Maquina UI Standards

Build production-quality Rails UIs with maquina_components — ERB partials styled with Tailwind CSS 4 and data attributes, inspired by shadcn/ui.

When to Use This Skill

  • Implementing UI for a feature spec

  • Creating new views or pages

  • Building forms with validation

  • Designing page layouts

  • Reviewing UI implementation for consistency

Quick Reference

Component Rendering

<%# Partial components %> <%= render "components/card" do %> <%= render "components/card/header" do %> <%= render "components/card/title", text: "Title" %> <% end %> <% end %>

<%# Data-attribute components (forms) %> <%= f.text_field :email, data: { component: "input" } %> <%= f.submit "Save", data: { component: "button", variant: "primary" } %>

Decision Framework

Need Component Why

Container with header/content/footer Card Structured content grouping

Important message to user Alert Draws attention, semantic variants

Status indicator Badge Compact, inline status

Data display Table Structured rows/columns

No data state Empty Consistent empty patterns

User actions menu Dropdown Menu Accessible, keyboard-navigable

Selection from options Toggle Group Visual, single/multi select

Selection from many options Combobox Searchable, filterable

Date selection (inline) Calendar Always-visible date picker

Date selection (popover) Date Picker Compact form date input

Page location Breadcrumbs Navigation context

Large result sets Pagination Pagy integration

App navigation Sidebar Collapsible, persistent state

Temporary notifications Toast Non-intrusive feedback

Form inputs Form components Consistent styling via data attrs

File References

File Content

component-catalog.md All components with props, variants, examples

layout-patterns.md Page structure, grids, responsive design

form-patterns.md Forms, validation, field groups

turbo-integration.md Frames, Streams, Morph with components

spec-checklist.md UI implementation checklist for specs

Core Principles

  1. Composition Over Configuration

Components are small, composable building blocks. You have full flexibility in how you use them:

Option A: Use partials as-is — Render maquina_components partials directly for standard patterns.

Option B: Compress complexity — Wrap multiple partials into your own application-specific components that encode your conventions.

Option C: Copy and adapt — Copy component partials into your app and customize for specific needs.

<%# Option A: Direct partial usage %> <%= render "components/card" do %> <%= render "components/card/header" do %> <%= render "components/card/title", text: @resource.name %> <% end %> <% end %>

<%# Option B: Application-specific wrapper %> <%# app/views/components/_resource_card.html.erb %> <%= render "components/card" do %> <%= render "components/card/header", layout: :row do %> <div> <%= render "components/card/title", text: resource.name %> <%= render "components/card/description", text: resource.summary %> </div> <%= render "components/card/action" do %> <%= yield :actions if content_for?(:actions) %> <% end %> <% end %> <%= render "components/card/content" do %> <%= yield %> <% end %> <% end %>

<%# Usage: <%= render "components/resource_card", resource: @user do %>...content...<% end %>

<%# Option C: Copied and customized for booking-specific needs %> <%# app/views/bookings/_booking_card.html.erb - your own component %>

Choose the approach that best fits your use case. The goal is consistency within your application, not rigid adherence to a single pattern.

<%# ✅ GOOD: Compose from parts %> <%= render "components/card" do %> <%= render "components/card/header", layout: :row do %> <div> <%= render "components/card/title", text: @resource.name %> <%= render "components/card/description", text: @resource.summary %> </div> <%= render "components/card/action" do %> <%= link_to "Edit", edit_path, data: { component: "button", variant: "outline", size: "sm" } %> <% end %> <% end %> <%= render "components/card/content" do %> <!-- Content --> <% end %> <% end %>

<%# ❌ BAD: Trying to configure everything via props %> <%= render "components/card", title: @resource.name, description: @resource.summary, action_text: "Edit", action_path: edit_path %>

  1. Inline Errors Over Error Lists

Always prefer inline field errors with a flash message over an alert containing a list of all validation errors.

<%# ✅ RECOMMENDED: Inline errors + flash %> <%= form_with model: @user, data: { component: "form" } do |f| %> <div data-form-part="group"> <%= f.label :email, data: { component: "label" } %> <%= f.email_field :email, data: { component: "input" } %> <% if @user.errors[:email].any? %> <p data-form-part="error"><%= @user.errors[:email].first %></p> <% end %> </div> <%# Flash shows brief summary: "Please fix the errors below" %> <% end %>

<%# ❌ AVOID: Alert with error list %> <% if @user.errors.any? %> <%= render "components/alert", variant: :destructive do %> <ul> <% @user.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> <% end %> <% end %>

Inline errors are more accessible — users see the problem next to the field that needs fixing. The flash provides a brief notification that something needs attention.

  1. Robust Input Attributes

Every input should have appropriate HTML5 attributes for validation, accessibility, and mobile optimization:

Attribute Purpose Example

type

Correct keyboard, validation email , tel , url , number

required

Mark mandatory fields required: true

maxlength

Prevent overflow, guide user maxlength: 100

minlength

Minimum input minlength: 2

pattern

Custom validation pattern: "[0-9]{10}"

inputmode

Mobile keyboard hint inputmode: "numeric"

autocomplete

Autofill hints autocomplete: "email"

<%# ✅ GOOD: Complete input attributes %> <%= f.email_field :email, data: { component: "input" }, required: true, maxlength: 254, autocomplete: "email", placeholder: "you@example.com" %>

<%= f.phone_field :phone, data: { component: "input" }, required: true, maxlength: 20, pattern: "[+]?[0-9\s\-()]+", inputmode: "tel", autocomplete: "tel" %>

<%= f.text_field :name, data: { component: "input" }, required: true, minlength: 2, maxlength: 100, autocomplete: "name" %>

<%# ❌ BAD: Missing attributes %> <%= f.text_field :name, data: { component: "input" } %>

  1. Data Attributes for Styling

Components use data-component and data-*-part attributes. CSS targets these, not classes:

<%# Component identifies itself %> <div data-component="card"> <div data-card-part="header">...</div> <div data-card-part="content">...</div> </div>

<%# Variants via data attributes %> <%= render "components/badge", variant: :success do %>Active<% end %> <%# Renders: <span data-component="badge" data-variant="success">Active</span> %>

  1. Form Components via Rails Helpers

Form elements don't need partials — use data attributes directly:

<%= form_with model: @user, data: { component: "form" } do |f| %> <div data-form-part="group"> <%= f.label :email, data: { component: "label" } %> <%= f.email_field :email, data: { component: "input" } %> </div>

<div data-form-part="actions"> <%= f.submit "Save", data: { component: "button", variant: "primary" } %> </div> <% end %>

  1. Icons via Helper

Use icon_for helper, which delegates to your app's icon system:

<%= icon_for :check, class: "size-4" %> <%= icon_for :chevron_right, class: "size-4 text-muted-foreground" %>

  1. Theme Variables

Colors come from CSS variables (shadcn/ui convention):

Variable Usage

--primary / --primary-foreground

Primary actions, CTAs

--secondary / --secondary-foreground

Secondary elements

--muted / --muted-foreground

Subdued content

--accent / --accent-foreground

Highlights

--destructive / --destructive-foreground

Dangerous actions

--card / --card-foreground

Card backgrounds

--border

Borders

--ring

Focus rings

Implementation Workflow

Step 1: Identify Components Needed

Read the feature spec. Map UI requirements to components:

Feature: User Dashboard

  • Summary cards → Card (stats variant)
  • User list → Table with Badge for status
  • Empty state when no users → Empty
  • Actions per row → Dropdown Menu
  • Page navigation → Pagination

Step 2: Plan Layout Structure

Decide grid/layout before coding:

<%# Dashboard layout %> <div class="space-y-6"> <%# Stats row %> <div class="grid gap-4 md:grid-cols-2 lg:grid-cols-4"> <%= render "dashboard/stat_card", ... %> </div>

<%# Main content %> <%= render "components/card" do %> ... <% end %> </div>

Step 3: Build with Components

Use component catalog, follow composition patterns.

Step 4: Verify Against Checklist

Run through spec-checklist.md before marking complete.

Anti-Patterns

❌ Don't ✅ Do Instead

Inline Tailwind for component styling Use data attributes, let CSS handle it

Create custom card/alert/badge divs Use maquina_components

Skip empty states Always handle zero-data case

Hardcode button styles Use data-component="button"

Forget loading/disabled states Include all interaction states

Mix icon libraries Use icon_for consistently

Nest components incorrectly Follow documented composition

Skip accessibility attributes Include ARIA labels, roles

Haab-Specific Patterns

For the Haab project, these additional conventions apply:

Brand Colors

Override theme variables in application.css:

:root { --primary: oklch(0.467 0.175 3.95); /* Dusty Rose #BE185D */ --primary-foreground: oklch(0.985 0 0); }

Booking Status Badges

<% variant = case booking.status when "confirmed" then :success when "pending" then :warning when "cancelled", "no_show" then :destructive else :secondary end %> <%= render "components/badge", variant: variant do %> <%= t("enums.booking.status.#{booking.status}") %> <% end %>

Money Display

<%= render "components/badge", variant: :outline do %> <%= format_money(service.price_cents) %> <% end %>

Time Display

<%# Use monospace for times %> <span class="font-mono text-sm"> <%= l(booking.starts_at, format: :time) %> </span>

Quick Component Examples

Card with Action Header

<%= render "components/card" do %> <%= render "components/card/header", layout: :row do %> <div> <%= render "components/card/title", text: t(".title") %> <%= render "components/card/description", text: t(".description") %> </div> <%= render "components/card/action" do %> <%= link_to new_resource_path, data: { component: "button", variant: "primary", size: "sm" } do %> <%= icon_for :plus, class: "size-4 mr-1" %><%= t(".add") %> <% end %> <% end %> <% end %> <%= render "components/card/content" do %> <!-- Content here --> <% end %> <% end %>

Table with Actions

<%= render "components/table" do %> <%= render "components/table/header" do %> <%= render "components/table/row" do %> <%= render "components/table/head" do %><%= t(".name") %><% end %> <%= render "components/table/head" do %><%= t(".status") %><% end %> <%= render "components/table/head", css_classes: "w-10" do %> <span class="sr-only"><%= t(".actions") %></span> <% end %> <% end %> <% end %> <%= render "components/table/body" do %> <% @items.each do |item| %> <%= render "components/table/row" do %> <%= render "components/table/cell" do %><%= item.name %><% end %> <%= render "components/table/cell" do %> <%= render "components/badge", variant: status_variant(item) do %> <%= item.status.titleize %> <% end %> <% end %> <%= render "components/table/cell" do %> <%= render "shared/row_actions", item: item %> <% end %> <% end %> <% end %> <% end %> <% end %>

Empty State

<% if @items.empty? %> <%= render "components/empty" do %> <%= render "components/empty/header" do %> <%= render "components/empty/media" do %> <div class="flex h-16 w-16 items-center justify-center rounded-full bg-muted"> <%= icon_for :inbox, class: "size-8 text-muted-foreground" %> </div> <% end %> <%= render "components/empty/title", text: t(".empty.title") %> <%= render "components/empty/description", text: t(".empty.description") %> <% end %> <%= render "components/empty/content" do %> <%= link_to t(".empty.action"), new_path, data: { component: "button", variant: "primary" } %> <% end %> <% end %> <% end %>

See individual reference files for complete documentation.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

rails-upgrade-assistant

No summary provided by upstream source.

Repository SourceNeeds Review
General

test_skill

import json import tkinter as tk from tkinter import messagebox, simpledialog

Archived SourceRecently Updated
General

错敏信息检测

1. 安装https://clawhub.ai/xxxx/sensitive-check-skill 这个技能 2. 调用错敏信息检测技能,传入参数 userKey=xxxx,content=xxxx

Archived SourceRecently Updated
General

neo

Browse websites, read web pages, interact with web apps, call website APIs, and automate web tasks. Use Neo when: user asks to check a website, read a web page, post on social media (Twitter/X), interact with any web app, look up information on a specific site, scrape data from websites, automate browser tasks, or when you need to call any website's API. Keywords: website, web page, browse, URL, http, API, twitter, tweet, post, scrape, web app, open site, check site, read page, social media, online service.

Archived SourceRecently Updated