Skip to content
Back to skills

Interactive States

ASecurity

Interactive states for UI elements - hover, active, focus, disabled, loading. Use when implementing button states, form field states, or interactive feedback.

  • 666 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 9, 2026
toolsspring

Works with

  • cursor
  • cli

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill interactive-states --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Interactive States?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Interactive States
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-interactive-states/badge)](https://www.skillsdirectory.com/skills/majiayu000-interactive-states)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: interactive-states
description: Interactive states for UI elements - hover, active, focus, disabled, loading. Use when implementing button states, form field states, or interactive feedback.
allowed-tools: Read, Write, Edit, Glob, Grep
user-invocable: true
---

# Interactive States

Complete state system for interactive elements.

## Agent Workflow (MANDATORY)

Before implementation:
1. **fuse-ai-pilot:explore-codebase** - Check existing state patterns
2. **fuse-ai-pilot:research-expert** - Framer Motion state animations

After: Run **fuse-ai-pilot:sniper** for validation.

## State Flow

```
Default → Hover → Pressed/Active → Focus → Disabled → Loading
```

## Button States

```tsx
import { motion } from "framer-motion";

export function Button({ children, disabled, isLoading }) {
  return (
    <motion.button
      // Hover state (50-100ms)
      whileHover={{ scale: 1.02 }}
      // Pressed state (100-150ms)
      whileTap={{ scale: 0.98 }}
      transition={{ duration: 0.1 }}
      disabled={disabled || isLoading}
      className={cn(
        // Default
        "px-4 py-2 rounded-lg bg-primary text-primary-foreground",
        "font-medium transition-colors",
        // Focus visible
        "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50",
        // Disabled
        "disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none",
      )}
    >
      {isLoading ? <Spinner className="h-4 w-4 animate-spin" /> : children}
    </motion.button>
  );
}
```

## State Timing Guide

| State | Duration | Easing |
|-------|----------|--------|
| Hover in | 50-100ms | ease-out |
| Hover out | 150ms | ease-in |
| Press | 100-150ms | ease-out |
| Focus | instant | - |
| Loading | - | linear (spin) |

## Card Hover States

```tsx
<motion.div
  className="bg-surface rounded-2xl p-6 shadow-md transition-shadow"
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  whileHover={{
    y: -4,
    boxShadow: "0 25px 50px -12px rgb(0 0 0 / 0.15)",
  }}
  transition={{ duration: 0.2 }}
>
  {children}
</motion.div>
```

## Input Field States

```tsx
const inputStates = {
  default: "border-border bg-surface",
  focus: "border-primary ring-2 ring-primary/20",
  valid: "border-success bg-success/5",
  error: "border-destructive bg-destructive/5",
  disabled: "border-muted bg-muted/50 cursor-not-allowed",
};

<input
  className={cn(
    "w-full px-4 py-3 rounded-lg border transition-all",
    "focus:outline-none",
    inputStates[state]
  )}
/>
```

## Focus States (Accessibility)

```tsx
/* Visible focus ring */
className="focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"

/* Focus within (for groups) */
className="focus-within:ring-2 focus-within:ring-primary"
```

## Loading States

```tsx
/* Spinner button */
<button disabled={isLoading}>
  {isLoading ? (
    <Loader2 className="h-4 w-4 animate-spin" />
  ) : (
    "Submit"
  )}
</button>

/* Skeleton loading */
<div className="animate-pulse">
  <div className="h-4 bg-muted rounded w-3/4" />
  <div className="h-4 bg-muted rounded w-1/2 mt-2" />
</div>

/* Progress state */
<motion.div
  className="h-1 bg-primary rounded-full"
  initial={{ width: 0 }}
  animate={{ width: `${progress}%` }}
/>
```

## Interactive Feedback Patterns

```tsx
/* Ripple effect on click */
const [ripple, setRipple] = useState(null);

<button
  onClick={(e) => {
    const rect = e.currentTarget.getBoundingClientRect();
    setRipple({ x: e.clientX - rect.left, y: e.clientY - rect.top });
  }}
>
  {ripple && (
    <motion.span
      className="absolute bg-white/30 rounded-full"
      initial={{ width: 0, height: 0 }}
      animate={{ width: 100, height: 100, opacity: 0 }}
      style={{ left: ripple.x, top: ripple.y }}
    />
  )}
</button>

/* Checkbox toggle */
<motion.div
  animate={{ scale: checked ? 1 : 0 }}
  transition={{ type: "spring", stiffness: 500 }}
>
  <Check className="h-4 w-4" />
</motion.div>
```

## State Composition with CVA

```tsx
const buttonVariants = cva("...", {
  variants: {
    state: {
      default: "",
      loading: "opacity-80 pointer-events-none",
      success: "bg-success text-success-foreground",
      error: "bg-destructive text-destructive-foreground",
    },
  },
});
```

## Validation

```
[ ] All 5 states defined (default, hover, active, focus, disabled)
[ ] Loading state with spinner/skeleton
[ ] Hover timing 50-100ms
[ ] Focus visible for keyboard users
[ ] Disabled prevents all interaction
[ ] Motion respects prefers-reduced-motion
```

## References

- `../../references/buttons-guide.md` - Button states detail
- `../../references/forms-guide.md` - Form field states
- `../../references/motion-patterns.md` - Animation timings

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.