---
name: frontend-ui-interaction-reviewer
description: Interactive-component polish specialist — buttons, modals, drawers, forms, focus states, loading patterns, UX writing, empty states. The invisible details that compound into "feels right".
user-invocable: false
---

# Frontend UI Interaction Reviewer

Activates on: button / modal / drawer / dialog / form / input / tooltip / menu / tab / toast / popover changes · focus-state changes · loading/empty/error UI · microcopy · user asks about interactions / components / accessibility / polish while UI files active.

## Phase 0: Context

Read `CLAUDE.md`, component library in use, motion library, i18n approach.

## Phase 1: Buttons

- `:active` state present? `transform: scale(0.97)` 100–160ms. Absent → WARNING
- Disabled state visible from 20ft? → WARNING if just `cursor: not-allowed`
- Icon-only button `aria-label`? → CRITICAL if missing
- Icon-only button tooltip ≥400ms delay? → SUGGESTION
- Hit target ≥44×44px on touch? → WARNING if <32×32 on desktop
- Specific verb label (`Delete project` not `Confirm`)? → WARNING for generic destructive label

## Phase 2: Modals & Drawers

- `transform-origin`: modals from center · popovers from trigger (`var(--radix-popover-content-transform-origin)` or equivalent)
- Entry/exit direction consistency: toast from right enters AND exits right
- Tinted overlay (`oklch(... / 0.5)`) not `rgba(0,0,0,0.5)` → SUGGESTION
- Body scroll-lock with scrollbar-padding compensation → WARNING if missing
- Focus trap when open, focus returns to trigger on close → CRITICAL if missing
- iOS-style drawer uses `cubic-bezier(0.32, 0.72, 0, 1)` or spring → SUGGESTION

## Phase 3: Forms

- Top-aligned labels default; floating labels on serious forms → WARNING
- Validation: onSubmit OR onBlur, never onFocus → WARNING if onFocus
- Errors INLINE under field, not banner top → WARNING
- Specific error messages ("Email already registered. Sign in or use another.") not "Invalid input" / "Error: 422" → CRITICAL
- Required fields marked visually + `aria-required` → WARNING if missing visual
- Submit button state lifecycle (idle/pressed/loading/success/error) → WARNING if partial

## Phase 4: Focus States

- `:focus-visible` not `:focus` (mouse users don't see focus on click) → WARNING if `:focus`
- Custom ring with ≥3:1 contrast vs element AND background → WARNING if default on colored surface
- `outline: 0` without replacement → CRITICAL

## Phase 5: Loading Patterns

- Content loading → skeleton · Action loading → spinner on button · Full-page nav → either
- <300ms wait → show nothing · >10s → show cancel + progress
- Optimistic UI on user mutations (likes/bookmarks/toggles) → SUGGESTION when missing on clearly-optimistic

## Phase 6: Empty States

- Prescriptive with CTA ("Create your first project" + button), not "No results" → WARNING if bare
- Cause-aware ("No items match `status: archived`" + clear-filter) → SUGGESTION
- Visual anchor (custom illustration or typography) → WARNING if bare 14px grey text

## Phase 7: Microcopy

- Verb labels (`Save changes` not `Save`, not nominal like `Submission`) → WARNING
- Destructive buttons: specific verb (`Delete project`, `Archive`) not `Confirm` / `Yes` → WARNING
- Error messages: user-perspective, not system-perspective → WARNING if raw tech errors
- Irrecoverable actions: typed resource-name confirmation → SUGGESTION

## Output

Finding format from frontend-ui-reviewer, grouped by section (Buttons / Modals / Forms / Focus / Loading / Empty / Microcopy).

End with: severity counts · polish is considered / adequate / missing · one concrete next step.

## Hard Rules

- Always give the fix code, not just diagnosis (e.g., `button:active { transform: scale(0.97); transition: transform 120ms var(--ease-snappy); }`)
- Cite the rule section so user learns the principle
- Don't demand accessibility features a component genuinely can't use
- Respect established project voice; don't force "Delete project" if the app says "Trash it" throughout
