Skip to content
Back to skills

Micro Interaction

ASecurity

Use when designing and engineering interactive component micro-motion, tactile button feedback, spring toggles, draw-on checkboxes, toasts, drawer gestures, and shared-element transitions.

  • 5 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 27, 2026
ai-agentsgobashreactnodespringgit

Works with

  • cursor
  • cli

Security analysis

A100/100

Scanned September 27, 2026

npx -y skills add Harmitx7/tribunal-kit --skill micro-interaction --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Micro Interaction?

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

Security grade badge for Micro Interaction
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/harmitx7-micro-interaction-tribunal-kit/badge)](https://www.skillsdirectory.com/skills/harmitx7-micro-interaction-tribunal-kit)

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: micro-interaction
description: "Use when designing and engineering interactive component micro-motion, tactile button feedback, spring toggles, draw-on checkboxes, toasts, drawer gestures, and shared-element transitions."
version: 5.0.0
last-updated: 2026-09-13
skills:
  - delight
  - better-ui
  - 60fps-animation
  - review-animations
tools: Read, Grep, Glob, Bash, Edit, Write
scripts-binding:
  - .agent/scripts/lint_runner.js
  - .agent/scripts/verify_all.js
---

# Micro-Interaction — Tactile Component Physics & Interactive State Synthesis

---

## 🛠️ Technical Architecture & Reference Recipes

Micro-interactions bridge intention and confirmation. When a button compresses beneath a pointer or a toggle thumb squashes as it slides across the track, digital interfaces cross the chasm from static abstractions to physical, responsive objects. Poor micro-interactions feel floaty, sluggish (>250ms), or introduce layout displacement. Master-grade micro-interactions operate under a strict sub-180ms execution budget, maintain volume mass conservation, anchor transform origins directly to interaction loci, and fire subtle tactile feedback.

---

---

---

---

## 🛠️ Technical Architecture & Reference Recipes

### 1. The Micro-Interaction Timing & Easing Matrix

| Component                    | Target Duration | Recommended Curve / Physics             | Perceived Feel                       |
| :--------------------------- | :-------------- | :-------------------------------------- | :----------------------------------- |
| **Button Press (`:active`)** | 80ms–120ms      | `cubic-bezier(0.2, 0, 0, 1)`            | Crisp, immediate tactile resistance  |
| **Toggle Switch**            | 160ms–200ms     | `spring(stiffness: 420, damping: 26)`   | Snappy with slight organic overshoot |
| **Checkbox Tick**            | 140ms–180ms     | `stroke-dashoffset` linear deceleration | Decisive, pencil-drawn precision     |
| **Toast Pop-In**             | 180ms–240ms     | `cubic-bezier(0.16, 1, 0.3, 1)`         | Smooth magnetic arrival              |
| **Popover / Dropdown**       | 140ms–180ms     | `scale(0.96) -> 1` + origin anchoring   | Anchored physical unfold             |

---

### 2. Five Production-Grade Implementation Recipes

#### Recipe 1: Tactile Elastic Button Press (React + Framer Motion)

```tsx
import React from 'react';
import { motion } from 'framer-motion';

export function TactileButton({
  children,
  onClick,
  className = '',
}: {
  children: React.ReactNode;
  onClick?: () => void;
  className?: string;
}) {
  const handleClick = () => {
    if (typeof window !== 'undefined' && 'vibrate' in navigator) {
      navigator.vibrate(8); // Subtle 8ms tactile micro-pulse
    }
    onClick?.();
  };

  return (
    <motion.button
      type="button"
      onClick={handleClick}
      whileHover={{ scale: 1.02, y: -1 }}
      whileTap={{ scale: 0.96, y: 1 }}
      transition={{ type: 'spring', stiffness: 500, damping: 30 }}
      className={`inline-flex items-center justify-center px-4 py-2 font-medium text-sm rounded-xl bg-neutral-900 dark:bg-white text-white dark:text-neutral-900 shadow-sm hover:shadow active:shadow-inner touch-manipulation focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500 ${className}`}
    >
      {children}
    </motion.button>
  );
}
```

---

#### Recipe 2: Organic Squash-and-Stretch Toggle Switch

```tsx
import React from 'react';
import { motion } from 'framer-motion';

export function ElasticToggle({
  checked,
  onChange,
  label,
}: {
  checked: boolean;
  onChange: (checked: boolean) => void;
  label: string;
}) {
  return (
    <label className="inline-flex items-center gap-3 cursor-pointer select-none">
      <span className="text-sm font-medium text-neutral-700 dark:text-neutral-300">{label}</span>
      <button
        type="button"
        role="switch"
        aria-checked={checked}
        onClick={() => onChange(!checked)}
        className={`relative w-12 h-7 rounded-full p-0.5 transition-colors duration-200 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500 ${
          checked ? 'bg-indigo-600' : 'bg-neutral-300 dark:bg-neutral-700'
        }`}
      >
        <motion.div
          animate={{
            x: checked ? 20 : 0,
            scaleX: [1, 1.25, 0.95, 1], // Squash as it pushes off, stretches in transit, snaps into place
            scaleY: [1, 0.85, 1.05, 1],
          }}
          transition={{
            x: { type: 'spring', stiffness: 480, damping: 28 },
            scaleX: { duration: 0.22 },
            scaleY: { duration: 0.22 },
          }}
          className="w-6 h-6 rounded-full bg-white shadow-md"
        />
      </button>
    </label>
  );
}
```

---

#### Recipe 3: Animated SVG Draw-On Checkbox

```tsx
import React from 'react';
import { motion } from 'framer-motion';

export function DrawOnCheckbox({
  checked,
  onChange,
  label,
}: {
  checked: boolean;
  onChange: (c: boolean) => void;
  label: string;
}) {
  return (
    <label className="inline-flex items-center gap-3 cursor-pointer select-none">
      <button
        type="button"
        role="checkbox"
        aria-checked={checked}
        onClick={() => onChange(!checked)}
        className={`w-5 h-5 rounded-md border flex items-center justify-center transition-colors duration-150 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500 ${
          checked
            ? 'bg-indigo-600 border-indigo-600 text-white'
            : 'border-neutral-400 dark:border-neutral-600 bg-white dark:bg-neutral-900'
        }`}
      >
        <svg
          viewBox="0 0 16 16"
          className="w-3.5 h-3.5 stroke-current fill-none stroke-[2.4] stroke-linecap-round stroke-linejoin-round"
        >
          <motion.path
            d="M 3.5 8.5 L 6.5 11.5 L 12.5 4.5"
            initial={false}
            animate={{ pathLength: checked ? 1 : 0, opacity: checked ? 1 : 0 }}
            transition={{ duration: 0.16, ease: [0.16, 1, 0.3, 1] }}
          />
        </svg>
      </button>
      <span className="text-sm text-neutral-800 dark:text-neutral-200">{label}</span>
    </label>
  );
}
```

---

#### Recipe 4: Dynamic Directional Toast with Spring Layout

```tsx
import React from 'react';
import { motion, AnimatePresence } from 'framer-motion';

interface ToastProps {
  id: string;
  message: string;
  onDismiss: (id: string) => void;
}

export function ToastContainer({
  toasts,
  onDismiss,
}: {
  toasts: ToastProps[];
  onDismiss: (id: string) => void;
}) {
  return (
    <div className="fixed bottom-6 right-6 z-50 flex flex-col gap-2 pointer-events-none">
      <AnimatePresence mode="popLayout">
        {toasts.map(toast => (
          <motion.div
            key={toast.id}
            layout
            initial={{ opacity: 0, y: 24, scale: 0.94 }}
            animate={{ opacity: 1, y: 0, scale: 1 }}
            exit={{ opacity: 0, scale: 0.92, transition: { duration: 0.14 } }}
            transition={{ type: 'spring', stiffness: 420, damping: 28 }}
            className="pointer-events-auto flex items-center justify-between min-w-[280px] p-4 rounded-xl bg-neutral-900 text-white shadow-xl border border-neutral-800"
          >
            <span className="text-sm font-medium">{toast.message}</span>
            <button
              type="button"
              onClick={() => onDismiss(toast.id)}
              className="ml-3 text-xs text-neutral-400 hover:text-white transition-colors"
            >
              Dismiss
            </button>
          </motion.div>
        ))}
      </AnimatePresence>
    </div>
  );
}
```

---

#### Recipe 5: Context-Aware Popover with Dynamic `transform-origin`

```css
/* Popover Unfold from Anchor Coordinate */
.popover-bubble {
  transform-origin: var(--radix-popover-content-transform-origin, top center);
  animation: popover-unfold 160ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes popover-unfold {
  from {
    opacity: 0;
    transform: scale(0.94) translateY(-4px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}
```

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…