interaction-design

Par elophanto · elophanto

Concevez et implémentez des microinteractions, du motion design, des transitions et des patterns de feedback utilisateur. À utiliser pour peaufiner les interactions UI, implémenter des états de chargement ou créer des expériences utilisateur engageantes.

npx skills add https://github.com/elophanto/elophanto --skill interaction-design

Conception des Interactions

Créez des interactions engageantes et intuitives grâce au mouvement, aux retours visuels et aux transitions d'état réfléchies qui améliorent l'utilisabilité et ravissent les utilisateurs.

Déclencheurs

  • interaction
  • microinteraction
  • hover effect
  • click feedback
  • transition
  • gesture
  • scroll animation
  • loading state
  • skeleton
  • toast
  • notification
  • user feedback

Quand Utiliser Cette Compétence

  • Ajouter des microinteractions pour améliorer le retour utilisateur
  • Implémenter des transitions fluides de pages et de composants
  • Concevoir des états de chargement et des skeleton screens
  • Créer des interactions basées sur les gestes
  • Construire des systèmes de notifications et toasts
  • Implémenter des interfaces drag-and-drop
  • Ajouter des animations déclenchées par le scroll
  • Concevoir des états hover et focus

Principes Fondamentaux

1. Mouvement Intentionnel

Le mouvement doit communiquer, non décorer :

  • Retour : Confirmer que les actions utilisateur ont eu lieu
  • Orientation : Montrer d'où viennent les éléments et où ils vont
  • Focus : Diriger l'attention vers les changements importants
  • Continuité : Maintenir le contexte pendant les transitions

2. Directives de Timing

Durée Cas d'Usage
100-150ms Micro-retours (hovers, clics)
200-300ms Petites transitions (toggles, dropdowns)
300-500ms Transitions moyennes (modals, changements de page)
500ms+ Animations chorégraphiées complexes

3. Fonctions d'Easing

/* Common easings */
--ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* Decelerate - entering */
--ease-in: cubic-bezier(0.55, 0, 1, 0.45); /* Accelerate - exiting */
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* Both - moving between */
--spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* Overshoot - playful */

Démarrage Rapide : Microinteraction de Bouton

import { motion } from "framer-motion";

export function InteractiveButton({ children, onClick }) {
  return (
    <motion.button
      onClick={onClick}
      whileHover={{ scale: 1.02 }}
      whileTap={{ scale: 0.98 }}
      transition={{ type: "spring", stiffness: 400, damping: 17 }}
      className="px-4 py-2 bg-blue-600 text-white rounded-lg"
    >
      {children}
    </motion.button>
  );
}

Modèles d'Interaction

1. États de Chargement

Skeleton Screens : Préserver la mise en page pendant le chargement

function CardSkeleton() {
  return (
    <div className="animate-pulse">
      <div className="h-48 bg-gray-200 rounded-lg" />
      <div className="mt-4 h-4 bg-gray-200 rounded w-3/4" />
      <div className="mt-2 h-4 bg-gray-200 rounded w-1/2" />
    </div>
  );
}

Indicateurs de Progression : Afficher la progression déterminée

function ProgressBar({ progress }: { progress: number }) {
  return (
    <div className="h-2 bg-gray-200 rounded-full overflow-hidden">
      <motion.div
        className="h-full bg-blue-600"
        initial={{ width: 0 }}
        animate={{ width: `${progress}%` }}
        transition={{ ease: "easeOut" }}
      />
    </div>
  );
}

2. Transitions d'État

Toggle avec transition fluide :

function Toggle({ checked, onChange }) {
  return (
    <button
      role="switch"
      aria-checked={checked}
      onClick={() => onChange(!checked)}
      className={`
        relative w-12 h-6 rounded-full transition-colors duration-200
        ${checked ? "bg-blue-600" : "bg-gray-300"}
      `}
    >
      <motion.span
        className="absolute top-1 left-1 w-4 h-4 bg-white rounded-full shadow"
        animate={{ x: checked ? 24 : 0 }}
        transition={{ type: "spring", stiffness: 500, damping: 30 }}
      />
    </button>
  );
}

3. Transitions de Page

Animations layout Framer Motion :

import { AnimatePresence, motion } from "framer-motion";

function PageTransition({ children, key }) {
  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={key}
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        exit={{ opacity: 0, y: -20 }}
        transition={{ duration: 0.3 }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  );
}

4. Modèles de Retour

Effet de ripple au clic :

function RippleButton({ children, onClick }) {
  const [ripples, setRipples] = useState([]);

  const handleClick = (e) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const ripple = {
      x: e.clientX - rect.left,
      y: e.clientY - rect.top,
      id: Date.now(),
    };
    setRipples((prev) => [...prev, ripple]);
    setTimeout(() => {
      setRipples((prev) => prev.filter((r) => r.id !== ripple.id));
    }, 600);
    onClick?.(e);
  };

  return (
    <button onClick={handleClick} className="relative overflow-hidden">
      {children}
      {ripples.map((ripple) => (
        <span
          key={ripple.id}
          className="absolute bg-white/30 rounded-full animate-ripple"
          style={{ left: ripple.x, top: ripple.y }}
        />
      ))}
    </button>
  );
}

5. Interactions Gestuelles

Swipe pour rejeter :

function SwipeCard({ children, onDismiss }) {
  return (
    <motion.div
      drag="x"
      dragConstraints={{ left: 0, right: 0 }}
      onDragEnd={(_, info) => {
        if (Math.abs(info.offset.x) > 100) {
          onDismiss();
        }
      }}
      className="cursor-grab active:cursor-grabbing"
    >
      {children}
    </motion.div>
  );
}

Modèles d'Animation CSS

Animations Keyframe

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.5;
  }
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.animate-fadeIn {
  animation: fadeIn 0.3s ease-out;
}
.animate-pulse {
  animation: pulse 2s ease-in-out infinite;
}
.animate-spin {
  animation: spin 1s linear infinite;
}

Transitions CSS

.card {
  transition:
    transform 0.2s ease-out,
    box-shadow 0.2s ease-out;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
}

Considérations d'Accessibilité

/* Respect des préférences de mouvement de l'utilisateur */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
function AnimatedComponent() {
  const prefersReducedMotion = window.matchMedia(
    "(prefers-reduced-motion: reduce)",
  ).matches;

  return (
    <motion.div
      animate={{ opacity: 1 }}
      transition={{ duration: prefersReducedMotion ? 0 : 0.3 }}
    />
  );
}

Bonnes Pratiques

  1. Performance d'Abord : Utiliser transform et opacity pour un rendu fluide à 60fps
  2. Support Reduced Motion : Toujours respecter prefers-reduced-motion
  3. Timing Cohérent : Utiliser une échelle de timing cohérente dans l'app
  4. Physique Naturelle : Préférer les animations spring au linéaire
  5. Interruptible : Permettre aux utilisateurs d'annuler les longues animations
  6. Progressive Enhancement : Fonctionner sans animations JS
  7. Tester sur les Appareils : Les performances varient considérablement

Problèmes Courants

  • Animations Saccadées : Éviter d'animer width, height, top, left
  • Sur-animation : Trop de mouvement cause la fatigue
  • Interactions Bloquées : Ne jamais bloquer l'entrée utilisateur pendant les animations
  • Fuites Mémoire : Nettoyer les écouteurs d'animation au démontage
  • Flash de Contenu : Utiliser will-change avec parcimonie pour l'optimisation

Vérifier

  • Le changement a été rendu dans un navigateur/simulateur et une capture d'écran ou un snapshot DOM a été capturé, pas seulement examiné en code
  • La mise en page a été vérifiée aux points de rupture que le guide de conception des interactions précise (mobile + desktop minimum) ; des preuves de chacun sont attachées
  • Les valeurs de couleur, typographie et espacement utilisées proviennent des tokens de conception/thème du projet, non des valeurs codées en dur ad-hoc
  • La navigation au clavier et l'ordre du focus ont été testés sur chaque élément interactif introduit
  • Les variantes reduced-motion / dark-mode (quand supportées) ont été vérifiées, pas supposées hériter
  • Aucune erreur console ou avertissement d'hydratation n'a été émis pendant le rendu de vérification

Ressources

Skills similaires