interaction-patterns
Interaction Patterns
Codifiable UI interaction patterns that prevent common UX failures. Covers loading states, content pagination, disclosure patterns, overlays, drag-and-drop, tab overflow, and notification systems — all with accessibility baked in.
Quick Reference
| Rule | File | Impact | When to Use |
|---|---|---|---|
| Skeleton Loading | rules/interaction-skeleton-loading.md |
HIGH | Content-shaped placeholders for async data |
| Infinite Scroll | rules/interaction-infinite-scroll.md |
CRITICAL | Paginated content with a11y and keyboard support |
| Progressive Disclosure | rules/interaction-progressive-disclosure.md |
HIGH | Revealing complexity based on user need |
| Modal / Drawer / Inline | rules/interaction-modal-drawer-inline.md |
HIGH | Choosing overlay vs inline display patterns |
| Drag & Drop | rules/interaction-drag-drop.md |
CRITICAL | Reorderable lists with keyboard alternatives |
| Tabs Overflow | rules/interaction-tabs-overflow.md |
MEDIUM | Tab bars with 7+ items or dynamic tabs |
| Toast Notifications | rules/interaction-toast-notifications.md |
HIGH | Success/error feedback and notification stacking |
| Cognitive Load Thresholds | rules/interaction-cognitive-load-thresholds.md |
HIGH | Enforcing Miller's Law, Hick's Law, and Doherty Threshold with numeric limits |
| Form UX | rules/interaction-form-ux.md |
HIGH | Target sizing, label placement, error prevention, and smart defaults |
| Persuasion Ethics | rules/interaction-persuasion-ethics.md |
HIGH | Detecting dark patterns and applying ethical engagement principles |
Total: 10 rules across 6 categories
Decision Table — Loading States
| Scenario | Pattern | Why |
|---|---|---|
| List/card content loading | Skeleton | Matches content shape, reduces perceived latency |
| Form submission | Spinner | Indeterminate, short-lived action |
| File upload | Progress bar | Measurable operation with known total |
| Image loading | Blur placeholder | Prevents layout shift, progressive reveal |
| Route transition | Skeleton | Preserves layout while data loads |
| Background sync | None / subtle indicator | Non-blocking, low priority |
Quick Start
Skeleton Loading
function CardSkeleton() {
return (
<div className="animate-pulse space-y-3">
<div className="h-48 w-full rounded-lg bg-muted" />
<div className="h-4 w-3/4 rounded bg-muted" />
<div className="h-4 w-1/2 rounded bg-muted" />
</div>
)
}
function CardList({ items, isLoading }: { items: Item[]; isLoading: boolean }) {
if (isLoading) {
return (
<div className="grid grid-cols-3 gap-4">
{Array.from({ length: 6 }).map((_, i) => (
<CardSkeleton key={i} />
))}
</div>
)
}
return (
<div className="grid grid-cols-3 gap-4">
{items.map((item) => <Card key={item.id} item={item} />)}
</div>
)
}
Infinite Scroll with Accessibility
function InfiniteList({ fetchNextPage, hasNextPage, items }: Props) {
const sentinelRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => { if (entry.isIntersecting && hasNextPage) fetchNextPage() },
{ rootMargin: "200px" }
)
if (sentinelRef.current) observer.observe(sentinelRef.current)
return () => observer.disconnect()
}, [fetchNextPage, hasNextPage])
return (
<div role="feed" aria-busy={isFetching}>
{items.map((item) => (
<article key={item.id} aria-posinset={item.index} aria-setsize={-1}>
<ItemCard item={item} />
</article>
))}
<div ref={sentinelRef} />
{hasNextPage && (
<button onClick={() => fetchNextPage()}>Load more items</button>
)}
<div aria-live="polite" className="sr-only">
{`Showing ${items.length} items`}
</div>
</div>
)
}
Rule Details
Skeleton Loading
Content-shaped placeholders that match the final layout. Use skeleton for lists, cards, and text blocks.
Load:
rules/interaction-skeleton-loading.md
Infinite Scroll
Accessible infinite scroll with IntersectionObserver, screen reader announcements, and "Load more" fallback.
Load:
rules/interaction-infinite-scroll.md
Progressive Disclosure
Reveal complexity progressively: tooltip, accordion, wizard, contextual panel.
Load:
rules/interaction-progressive-disclosure.md
Modal / Drawer / Inline
Choose the right overlay pattern: modal for confirmations, drawer for detail views, inline for simple toggles.
Load:
rules/interaction-modal-drawer-inline.md
Drag & Drop
Drag-and-drop with mandatory keyboard alternatives using @dnd-kit/core.
Load:
rules/interaction-drag-drop.md
Tabs Overflow
Scrollable tab bars with overflow menus for dynamic or numerous tabs.
Load:
rules/interaction-tabs-overflow.md
Toast Notifications
Positioned, auto-dismissing notifications with ARIA roles and stacking.
Load:
rules/interaction-toast-notifications.md
Cognitive Load Thresholds
Miller's Law (max 7 items per group), Hick's Law (max 1 primary CTA), and Doherty Threshold (400ms feedback) with specific, countable limits.
Load:
rules/interaction-cognitive-load-thresholds.md
Form UX
Fitts's Law touch targets (44px mobile), top-aligned labels, Poka-Yoke error prevention with blur-only validation, and smart defaults.
Load:
rules/interaction-form-ux.md
Persuasion Ethics
13 dark pattern red flags to detect and reject, the Hook Model ethical test (aware, reversible, user-benefits), and EU DSA Art. 25 compliance.
Load:
rules/interaction-persuasion-ethics.md
Key Principles
- Keyboard parity — Every mouse interaction MUST have a keyboard equivalent. No drag-only, no hover-only.
- Skeleton over spinner — Use content-shaped placeholders for data loading; reserve spinners for indeterminate actions.
- Native HTML first — Prefer
<dialog>,<details>,role="feed"over custom implementations. - Progressive enhancement — Features should work without JS where possible, then enhance with interaction.
- Announce state changes — Use
aria-liveregions to announce dynamic content changes to screen readers. - Respect scroll position — Back navigation must restore scroll position; infinite scroll must not lose user's place.
Anti-Patterns (FORBIDDEN)
- Spinner for content loading — Spinners give no spatial hint. Use skeletons matching the content shape.
- Infinite scroll without Load More — Screen readers and keyboard users cannot reach footer content. Always provide a button fallback.
- Modal for browsable content — Modals trap focus and block interaction. Use drawers or inline expansion for browsing.
- Drag-only reorder — Excludes keyboard and assistive tech users. Always provide arrow key + Enter alternatives.
- Toast without ARIA role — Toasts are invisible to screen readers. Use
role="status"for success,role="alert"for errors. - Auto-dismiss error toasts — Users need time to read errors. Never auto-dismiss error notifications.
Detailed Documentation
| Resource | Description |
|---|---|
| references/loading-states-decision-tree.md | Decision tree for skeleton vs spinner vs progress bar |
| references/interaction-pattern-catalog.md | Catalog of 15+ interaction patterns with when-to-use guidance |
| references/keyboard-interaction-matrix.md | Keyboard shortcuts matrix for all interactive patterns (WAI-ARIA APG) |
Related Skills
ork:ui-components— shadcn/ui component patterns and CVA variantsork:animation-motion-design— Motion library and View Transitions APIork:accessibility— WCAG compliance, ARIA patterns, screen reader supportork:responsive-patterns— Responsive layout and container query patternsork:performance— Core Web Vitals and runtime performance optimization