design-frontend

Put on the pragmatic front-end hat for the emisar Phoenix UI and marketing HEEx — build correct LiveView/operator UI with CoreComponents and LiveTable, or execute public marketing pages from creative

SKILL.md

GitHub

Front-end hat (pragmatic LiveView)

Ship the smallest thing that works and reads clearly. LiveView-first for the operator console: the server holds the state, HEEx renders it, JS only when LiveView genuinely can't. Marketing pages are different: they are public, server-rendered HEEx and may have a distinctive visual language directed by design-creative-director, as long as they remain fast, crawlable, accessible, and honest.

Pick the surface first

  • Operator console / LiveView: follow the CoreComponents, LiveTable, and IL-18 rules below. Consistency and calm matter more than novelty.
  • Marketing pages (controllers/marketing_html/**): use design-creative-director for art direction and content-seo for crawlable positioning. Custom section layouts are allowed; generic SaaS templates are not. Keep real content in the initial HTML, avoid unnecessary dependencies, and verify with rendered desktop and mobile screenshots.

Reuse before you build

  • EmisarWeb.CoreComponents first. It's large and already covers buttons, inputs, tables, modals, flash, etc. Grep it before writing markup; extend it only if the primitive is genuinely missing — and then it's shared, not one-off.
  • EmisarWeb.LiveTable for any list/table: it's stateless and URL-driven. Feed it LiveTable.params_to_opts(params, Runs.run_filters()) (the owning context's <schema>_filters/0) and pass the opts to that context's list_* function. Don't hand-roll pagination, sorting, or filtering.
  • Match the existing screens' Tailwind utility patterns. Don't invent spacing/color scales; reuse what layouts and CoreComponents already use.

LiveView Iron Law (IL-18 — Credo won't catch these; you must)

  • No unconditional DB/context read in mount — mount runs twice. Use assign_async, or connected?(socket) with a cheap disconnected branch.
  • stream/3 for any list that can grow (runs, audit events, runners). Never assign(socket, :events, big_list) — it bloats socket memory per connection.
  • connected?(socket) guard before any PubSub subscribe (live runner/run status updates), or you double-subscribe.
  • Never assign_new for per-mount values (current_subject, current_account, locale) — use assign/3.
  • Authorize in every handle_event by routing through a context call with the subject (IL-15). The button being hidden is not authorization.

Pragmatic rules

  • One component, one job. Pass data in via attr/slot; don't reach into parent assigns. Function components for stateless UI; a LiveComponent only when it owns state.
  • Loading / empty / error states are part of the component, not a later pass (the /design-ux hat will ask for them).
  • Keep markup readable: no deeply nested conditionals in HEEx — compute in the LV, render flat. Extract a function component when a block repeats.
  • Forms use to_form/2 + CoreComponents inputs; show changeset errors (IL-18's sibling: if a save "silently fails", check {:error, changeset} first).

Fixing a reported UI defect

When the user points at something broken on a rendered surface, the fix ships with before/after screenshot proof — and the BEFORE shot comes before any edit (the broken state is unrecoverable once the fix deploys). Shoot the element from the active workspace with ./run shot (--label before), using the current task's screenshots/ directory (create and claim a basic task first when none exists), fix, let ./run serve reload (no image rebuild), re-shoot (--label after, full page + element crop), verify the pixels yourself, then hand the user both paths for review. Full workflow: portal/.agent/kb/rules/design-ui-fix-screenshot-proof.md.

Finish

Run ./run gate portal --changed from the repository root, click-test the happy path + one error path, and confirm lists stream. Before pushing, run the complete ./run gate portal. Before a rendered surface is done, run the design-interface-polish micro-craft pass against design-system §3 — calm on the console, expressive on marketing. Hand UX judgment calls to /design-ux; keep this hat on the implementation.