All skills
mblode avatar

/ui-design

@e7db33d
by Matthew Blodemblode/agent-skills136 stars
12

Designs and builds React/Next/Tailwind UI and audits visual and interaction defects. Use when asked to "build a landing page", "extract our design system", "add dark mode", "make this responsive", "remove UI slop", or "audit this component". For product decisions use product-design; for browser measurements use ui-verification; for motion use ui-animation.

Use this Skill: https://skilld.dev/gh/mblode/agent-skills/ui-design

This session only. Nothing lands on disk.

rulesasync-out-of-order-responses.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Out-of-order async responses (stale results)

Type "ca" then "cat" quickly: two requests fly. On a slow network the "ca" response can land after "cat", so the UI shows "ca" results while the input says "cat." Fix: cancel in-flight requests with AbortController, or drive the request key with useDeferredValue so React collapses to the latest value.

What goes wrong

A search input fetches on every keystroke over a jittery network. The "iphon" response lands after "iphone", overwriting the iphone results with iphon results. It looks broken, yet intermittently looks correct, so it is hard to reproduce.

Detection

Surfaces: search (highest priority), list filter, form async-validation, dashboard with a date-range picker that refetches.

Static signals:

  1. rg 'onChange|onInput' --type=ts -A 5: find handlers containing fetch(, useQuery, or any async call.
  2. For each, confirm one of:
    • AbortController created, signal passed to fetch, aborted on the next call or in useEffect cleanup.
    • useDeferredValue drives the request key (React debounces).
    • A query library (TanStack Query, SWR) keys the request so the latest wins.
  3. Flag handlers that fetch without any of the above.

Concrete commands:

# onChange/onInput handlers that fetch
rg -A 8 'onChange=|onInput=' --type=ts | rg -B 2 'fetch\(|useQuery\(|axios\.'

# Files using fetch in inputs without AbortController
rg 'onChange|onInput' --type=ts -l | while read f; do
  rg -q 'AbortController|useDeferredValue|signal:' "$f" || echo "$f: input fetch with no cancellation"
done

# useEffect with fetch but no cleanup
rg -l 'useEffect\(' --type=ts src/ app/ | while read f; do
  rg -A 10 'useEffect\(' "$f" | rg -q 'fetch\(' \
    && ! rg -q 'return \(\) =>|abort\(\)' "$f" \
    && echo "$f: useEffect fetch without cleanup or abort"
done

False-positive guards:

  • Skip handlers whose only fetch is a fire-and-forget mutation (POST without rendering the response).
  • Skip when a query library is in use and the query key includes the input value (TanStack Query, SWR keep the freshest).
  • Skip files annotated // ui-audit-ignore:async-out-of-order-responses.

Fix

Two canonical approaches.

A. AbortController in useEffect, best for plain fetch driven by state:

// before: race condition
function Search({ q }: { q: string }) {
  const [results, setResults] = useState<Item[]>([]);
  useEffect(() => {
    fetch(`/api/search?q=${q}`).then((r) => r.json()).then(setResults);
  }, [q]);
  return <Results items={results} />;
}

// after: abort on re-render or unmount
function Search({ q }: { q: string }) {
  const [results, setResults] = useState<Item[]>([]);
  useEffect(() => {
    const controller = new AbortController();
    fetch(`/api/search?q=${q}`, { signal: controller.signal })
      .then((r) => r.json())
      .then(setResults)
      .catch((err) => {
        if (err.name !== 'AbortError') throw err;
      });
    return () => controller.abort();
  }, [q]);
  return <Results items={results} />;
}

B. useDeferredValue + Suspense, React keeps the latest:

function SearchPage({ q }: { q: string }) {
  const deferred = useDeferredValue(q);
  return (
    <Suspense fallback={<Results.Skeleton />}>
      <Results key={deferred} q={deferred} />
    </Suspense>
  );
}

Docs:

Default tier and overrides

Defaults to: release-blocker for search; fix-this-sprint elsewhere.

Surface overrides:

Surface Tier
Search / Combobox release-blocker
Async form validation fix-this-sprint
Dashboard date-range refetch fix-this-sprint
Marketing backlog

Defer-to (when this is another tool's job)

  • TanStack Query / SWR: built-in cancellation; verify it's wired up rather than re-implementing.
  • ESLint react-hooks/exhaustive-deps for missing cleanup.
  • Lighthouse / Vercel Speed Insights for INP impact (out-of-order also wastes bandwidth).

Suppression

{/* ui-audit-ignore:async-out-of-order-responses, backend is idempotent and last-wins */}

Source: SKILL.md on GitHub

No alerts7d5 checks · Risk SAFE
  • Gen Agent Trust Hub7d

    A comprehensive UI design and auditing skill that uses standard developer tools and trusted services like Tailwind CSS and placeholder asset APIs to build and verify React/Next.js interfaces. It includes explicit guardrails to prevent instruction following from analyzed project files.

  • Socket7d

    No alerts

  • Snyk7d

    Risk: LOW · No issues

  • Runlayer6mo

    4/5 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at e7db33d. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 31 minutes ago.

Activeupdated last week

README badge

README badge for mblode/agent-skills/ui-design