---
name: build-rails-hotwire-ui
description: Inspect, plan, implement, and verify polished product UI in server-rendered Ruby on Rails applications that use Action View, Hotwire Turbo, and Stimulus. Use when Codex needs to create or redesign Rails pages, partials, forms, navigation, responsive layouts, or accessible interactions; restructure user journeys, information hierarchy, dense data, interface copy, or page actions; replace generic or AI-looking UI with a coherent visual direction and semantic color system; choose among Turbo Drive, page morphing, Frames, Streams, and Stimulus; add View Transition animations or small optimistic behaviors; repair Hotwire lifecycle or response problems; or review an existing Rails UI for idiomatic structure and progressive enhancement.
---

# Build Rails Hotwire UI

## Start From The Existing App

Read the repository instructions and inspect the relevant vertical slice before changing it:

1. Identify the product purpose, target user, workflow, and evidence that explains what the page must help them decide or do.
2. Identify the Rails, `turbo-rails`, and Stimulus versions from lockfiles and import/package configuration.
3. Identify the view language, asset pipeline, CSS system, component approach, icons, typography, spacing and color tokens, breakpoints, and existing interaction patterns.
4. Read strong existing voice and brand samples: product screens, support copy, onboarding, changelogs, screenshots, or design-system guidance.
5. Trace the route, controller action, policy, model/query, template, partials/components, Turbo responses, Stimulus controllers, and tests for the workflow.
6. Run the current page when practical. Check its wide, narrow, loading, empty, validation-error, success, and permission-denied states.
7. Preserve unrelated changes and established product conventions. Do not introduce a component framework, JavaScript bundler, or CSS system merely to complete a page.

For Rails view structure and form contracts, read `references/rails-view-architecture.md`. For any Turbo or Stimulus work, read `references/hotwire-interaction-patterns.md`. For page restructuring, interface copy, dense data, visual direction, or color systems, read `references/product-ui-content-and-visual-direction.md`. For animation, optimistic UI, accessibility, or final browser QA, read `references/motion-accessibility-verification.md`.

## Define The UI Contract

Before editing, state the page's job in one sentence and list:

- the user, their entry context, the decision or task, and the useful next step;
- the primary action, secondary actions, information hierarchy, and content that can be deferred;
- the user-facing names for objects, actions, statuses, errors, and outcomes;
- server-owned persistent state versus local transient state;
- responsive behavior and all meaningful UI states;
- the URL/history, focus, scroll, and announcement behavior;
- the visual direction, one restrained signature gesture, and the semantic tokens it uses;
- the smallest server-rendered HTML boundary that can express the result.

If the user requests implementation, complete the vertical slice rather than stopping at a mockup. If the user requests only an audit or plan, do not mutate the app.

## Reformat The User Path Before Styling

- Map the path as entry → orient → inspect or enter information → act → confirm or recover → next step. Remove repeated orientation, hidden prerequisites, dead ends, and competing primary actions.
- Choose a page archetype that matches the job: browse/list, detail, form/workflow, dashboard, settings, or onboarding. Do not force every page into a hero plus equal card grid.
- Put identity, scope, status, and the primary action where users first need them. Stage advanced controls and supporting detail near the decision they affect.
- Keep filters, sorting, pagination, tabs, and selected scope in the URL when users should be able to bookmark, share, or restore the view.
- Write interface copy from verified product facts. Name what the action does and what changes next; make empty states instructional, errors symptom-first, and confirmations specific.
- Replace vague words such as “streamline,” “unlock,” “powerful,” “seamless,” “insights,” “continue,” and “submit” when the product can name the real object, action, or result.
- For dense pages, surface freshness, source, scope, owner, severity, exact values, and next actions. Preserve context between overview, detail, and action views.

## Choose A Coherent Visual Direction

- Derive the direction from the product, audience, and strongest existing brand cues. Describe it in a short phrase and commit to one visual language.
- Select one signature gesture—such as an editorial rule, utilitarian data rail, distinctive type scale, restrained texture, or asymmetric emphasis—and let the rest of the interface support it.
- Define semantic roles for typography, spacing, canvas, surfaces, text, controls, borders, focus, accent, statuses, charts, radii, shadows, and motion. Reuse the app's token mechanism.
- Preserve information density and task speed. Do not decorate every container, animate every section, or turn every datum into a rounded card.
- Reject generic template habits: default purple-blue gradients, interchangeable glass cards, excessive pills, uniformly centered copy, decorative icon boxes, unsupported metrics, and repeated three-column feature grids.
- Treat contrast as a role-by-surface contract. Measure actual rendered pairs across states and themes, target WCAG 2.2 AA by default, and provide non-color cues for status, selection, validation, and charts.
- When changing the shared color system, add or update a permanent contrast check in the app's native test/build path. Distinctive must remain readable and accessible.

## Choose The Smallest Interaction Model

Use this ladder in order. Move down only when the preceding option cannot express the behavior cleanly.

| Need | Default technique |
| --- | --- |
| Static presentation or a native disclosure/control | Semantic HTML and CSS, including browser primitives where appropriate |
| Normal navigation or form submission | Turbo Drive with ordinary Rails routes and full HTML responses |
| Refreshing the current URL while preserving screen state | Turbo page refresh with morphing and intentional scroll policy |
| One independently navigable region with its own link/form context | Turbo Frame with a stable, unique ID |
| One action updates multiple regions, removes/appends records, or receives live updates | Turbo Stream targeting ordinary DOM elements |
| Local behavior the server cannot describe directly | A small Stimulus controller using actions, targets, values, and lifecycle callbacks |

Do not add a Frame solely so a Stream can target an element. Do not replace an ordinary link or form with custom `fetch`. Keep server responses and normal browser behavior as the foundation.

## Implement Server-Rendered HTML First

- Keep routing, authorization, validation, and durable state on the server.
- Keep database access and business decisions out of templates. Prepare collections and policy results before rendering.
- Compose pages with the app's existing layout, partial, helper, or component conventions. Prefer one canonical record partial for initial rendering and subsequent Turbo updates.
- Build recurring UI primitives around semantic roles, not page-specific copies of the same card, badge, empty state, or action cluster.
- Use Rails URL, form, translation, and DOM ID helpers. Give replaceable fragments stable, unique IDs.
- Use links for navigation and forms/buttons for mutations. Preserve CSRF protection and HTTP semantics.
- Render invalid forms with their submitted object and an error status; redirect after successful state-changing submissions with the status expected by the installed Rails/Turbo versions.
- Treat empty, loading, error, success, and destructive-confirmation states as part of the page, not follow-up polish.
- Preserve a usable non-JavaScript path where the product workflow reasonably permits it.

## Add Hotwire Deliberately

- Use Drive by default and opt out only for a demonstrated incompatibility.
- Use Frames for genuine navigation compartments. Ensure direct visits still render a coherent full page and frame responses contain the matching frame.
- Use Streams for declarative DOM operations. Reuse the same partials as the full page and scope each target precisely.
- Prefer morphing when the server can cheaply return canonical page HTML and screen-state preservation matters more than hand-authored stream actions.
- Account for Turbo's persistent document and cache. Make setup idempotent, remove temporary UI before caching, and avoid body scripts for long-lived behavior.
- Treat missing-frame errors, duplicate IDs, stale permanent elements, and focus loss as architecture defects rather than adding timing workarounds.

## Keep Stimulus Small And Lifecycle-Safe

- Name controllers for behavior, not a page or visual style.
- Use action descriptors instead of manual element listeners, targets instead of selectors, and typed values instead of parsing ad hoc attributes.
- Represent visual-only state with `data-state` or a class. Use `aria-*` only when it truthfully exposes valid semantics for the element or role.
- Make `connect()` safe to run repeatedly. Clean up timers, observers, subscriptions, manually installed listeners, and third-party instances in `disconnect()`.
- Put shareable state in the URL and durable state on the server. Keep Stimulus state local and disposable.
- Limit optimistic UI to low-risk, reversible feedback. Keep the server authoritative and handle failure, rollback, repeated submission, and late responses.

## Add Motion As Progressive Enhancement

- Stabilize DOM identity, rendering boundaries, and focus behavior before animating.
- Prefer Turbo-integrated View Transitions and CSS over manual element measurement or duplicated client rendering.
- Assign transition names only to meaningful, stable elements and keep every active name unique.
- Keep transitions short and explanatory. Disable non-essential motion under `prefers-reduced-motion`.
- Do not weaken Content Security Policy to add animation.
- Verify the activation syntax against the installed Turbo version. Do not copy version-sensitive meta tags from an article or another application without checking current documentation.

## Verify The Whole Interaction

Run the repository's own checks, then add focused coverage in proportion to the change:

1. Assert response status, redirects, rendered HTML, stable IDs, and Turbo Stream actions at the request/view layer.
2. Use a system test for the critical browser interaction, especially Frames, Streams, focus, history, or Stimulus behavior.
3. Exercise valid and invalid submissions, direct URL entry, Back/Forward restoration, repeated clicks, slow responses, and the relevant authorization boundary.
4. Inspect browser console and network failures. Check desktop and narrow layouts, keyboard use, visible focus, labels, announcements, contrast, reduced motion, and JavaScript-disabled fallback where applicable.
5. Confirm that Turbo cache restoration does not repeat flash messages, preserve stale overlays, or duplicate listeners.
6. Review the result as a product path: one clear page job, concrete copy, visible state and next actions, useful progressive disclosure, and no generic template patterns that could belong to any application.

Report the changed user path and information hierarchy, copy and visual-system decisions, chosen Hotwire boundary, tests run, and any compatibility or browser checks that remain.
