Skip to main content

stimulus

Client-side interactivity — phlex-reactive client ops, actions and broadcasts first, then existing Stimulus and Turbo Stream code, and the operator-confirmed exception. Use when adding any interaction (toggle, dropdown, live update, inline edit) or changing an existing Stimulus controller.

Source facts

Repository
zoolutions/claude-plugins
Last source activity
October 17, 2026 at 18:59
Detected SKILL.md language
English
Stars
2
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
stimulus
description
Client-side interactivity — phlex-reactive client ops, actions and broadcasts first, then existing Stimulus and Turbo Stream code, and the operator-confirmed exception. Use when adding any interaction (toggle, dropdown, live update, inline edit) or changing an existing Stimulus controller.
argument-hint
interaction or controller task
allowed-tools
Bash(*), Read, Write, Edit, Glob, Grep, Skill, AskUserQuestion
model
sonnet
# Interactivity: $ARGUMENTS **Read the repository first.** `AGENTS.md` → `## Workflow` → **Interactivity** says which order this app follows and whether new Stimulus or Turbo Stream code needs a human's confirmation. When it names phlex-reactive, the order below is binding. When it does not (Stimulus only, or legacy JavaScript), skip to "Maintaining Stimulus": new behaviour is a Stimulus controller. ## phlex-reactive first, in this order 1. **Client-only** (open/close, show/hide, pick one to reveal a panel, focus, copy): `Phlex::Reactive::ClientBindings` with `on_client` ops (`js.show`/`hide`/`toggle`, `global: true` beyond the root, `outside: true` for outside-close), `reactive_show`, `reactive_filter`, `reactive_compute`. No round trip, no feature JavaScript (a custom `reactive_compute` reducer is the exception you register). ```ruby div(**reactive_root) do Button(**on_client(:click, js.toggle("#details"))) { t("things.show.details") } div(id: "details", hidden: true) { … } end ``` 2. **The server decides or persists:** a reactive `action` on a `Phlex::Reactive::Component`, answering with `reply.morph`/`reply.replace`/`reply.also`. Authorize inside the action (Workflow → Authorization); `skip_verify_authorized` only with a comment saying why it is public. 3. **Live updates to other viewers:** `broadcast_to`, not `Turbo::Broadcastable`. 4. **phlex-reactive cannot express it, or expresses it badly:** `/flow:plan upstream zoolutions/phlex-reactive <the gap>` (a PR when the fix is small). Link it from our PR and from a comment at the workaround. 5. **Only then** a new Stimulus controller or new Turbo Stream code, and only after a **human operator confirms**: ask with AskUserQuestion, naming the upstream issue and why reactive cannot do it, and record the answer in the PR body under `## Interactivity decision`. An agent never makes this call; an unattended run parks the issue instead. Not gated: Hotwire Native `bridge--*` controllers, Turbo Frames, Turbo Drive navigation. **Count the JavaScript** in an app that loads the reactive client lazily per page: a reactive root makes its page import the client, so a root in a layout, header or global dialog puts it on every page. Measure the page weight and say so in the PR. ## Maintaining Stimulus and Turbo Streams - Existing controllers stay until a change already touches them. Fix them in place, don't grow them; migrate one to phlex-reactive when your change is in its path. - Declare every `static targets`/`values`; wire with `data-action`, never inline `onclick` (CSP). - Release in `disconnect()` what `connect()` acquired (listeners on `window`/`document`, timers, observers). `connect()` runs again after every Turbo visit and morph: keep it idempotent. - No module-level state; no new jQuery outside the legacy code that already uses it. - After adding a controller, confirm it is registered (import map pin or controllers index) and loads on the page that uses it. - Existing Turbo Stream responses keep a stable target `id` on both sides. ## Test and debug - Every interaction gets a system spec that ends on what the server stored, not on the click. After each reactive interaction, call the wait helper Workflow → Specs names. - A reactive click that does nothing, a 400/403/404 from the actions endpoint, or `AuthorizationNotVerified`: the app's `phlex-reactive-debugging` skill (generated by the gem, `rails g phlex:reactive:claude`). Done when the system spec is green and Workflow → Verify passes.
View on GitHub