Communitygithub.com

pafelab/stretchy-morph-ui

Transforme une interface web en UI morphing élastique, où les transitions s'étirent, se compriment, rebondissent et s'installent sur des ressorts plutôt que des courbes d'easing.

Qu'est-ce que stretchy-morph-ui ?

Ce skill transforme n'importe quelle interface web en une interface utilisateur vivante et élastique, où chaque changement d'état se traduit par un morphing naturel : un indicateur d'onglet s'étire comme un ver vers l'onglet suivant, un bouton se compresse en cercle de chargement puis regonfle, une image de produit se soulève de sa carte, rétrécit en pastille et tombe dans le panier. Il est basé sur un moteur de ressort (oscillateur harmonique amorti intégré en semi-implicite d'Euler) et 24 composants de référence fonctionnels dans `assets/showcase.html`. Le skill analyse d'abord le code existant de l'utilisateur avec un scanner (`scripts/audit_ui.py`) pour identifier les candidats au morphing (onglets, toggles, boutons, modales, dropdowns, accordéons, carrousels, toasts, barres latérales, sélecteurs de date, ajout au panier, etc.), liste les opportunités et demande une confirmation avant toute modification. Il inclut des techniques de déformation variées : paires d'arêtes (un bord arrive avant l'autre pour créer l'étirement), préservation de volume (l'axe opposé se dilate pour compenser), étirement par vélocité (allongement dans le sens du mouvement), effets gooey (flou + seuil alpha), séquences morphing via formes intermédiaires porteuses de sens, et amortissement élastique. Chaque composant reçoit une 'phrase de morphing' qui décrit le déclencheur, la transformation de forme A en forme B, et la cause de l'étirement, garantissant que chaque déformation a un sens physique plutôt qu'un rebondissement aléatoire. Une checklist d'accessibilité (respect du mouvement réduit, aria-*, inert, gestion du focus) et des tests automatisés (scripts de vérification de page, captures d'écran des animations) assurent une qualité robuste.

Compatible avec✓Claude Code~Codex CLI~Cursor✓Antigravity
npx skills add https://github.com/pafelab/stretchy-morph-ui/tree/HEAD

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Interface utilisateur morphing élastique

Ce skill produit des interfaces où chaque changement d'état morphe : un indicateur d'onglet s'étire comme un ver en route vers l'onglet suivant, un bouton se compresse en cercle de chargement puis regonfle, une image de produit se soulève de sa carte, rétrécit en pastille et tombe dans le panier. Il a été distillé à partir de la construction d'une vitrine de 24 composants (dans assets/showcase.html) et des bugs et corrections de design découverts en cours de route.

L'apparence repose sur une idée : l'étirement a une cause. Un élément se déforme parce que ses parties ne sont pas d'accord (un bord arrive avant l'autre), parce qu'il se déplace rapidement (il s'allonge dans sa trajectoire), parce qu'il est comprimé (il gonfle de l'autre côté pour conserver son volume), ou parce qu'il vient d'être touché (il oscille). Un rebondissement aléatoire en boucle semble bon marché ; un étirement avec une cause ressemble à un matériau.

Étape 0, à chaque fois : auditer le code de l'utilisateur et confirmer

Avant de construire ou de modifier quoi que ce soit, découvrez ce qui, dans l'interface de l'utilisateur, peut devenir élastique, et laissez-le choisir. Les détails, critères de jugement et modèles de messages se trouvent dans references/audit.md ; la version courte :

  1. Trouvez le code : code collé, fichiers joints (sur claude.ai, listez /mnt/user-data/uploads), le répertoire de travail ou le dépôt, un lien vers une artifact (lisez-le avec l'outil artifact), ou une source indiquée par l'utilisateur. S'il n'y en a pas, dites ce que vous avez cherché et proposez deux chemins : partager le code, ou construire quelque chose de nouveau à partir de zéro.
  2. Scannez-le : python scripts/audit_ui.py <chemins> rapporte la pile, le mouvement existant, la gestion manquante du mouvement réduit, et les candidats par catégorie d'interface avec fichier:ligne, chacun associé à un composant de la vitrine et une technique.
  3. Lisez chaque résultat dans son contexte et ne conservez que les vrais candidats. Pour chacun, notez où il se trouve, ce qui se passe maintenant, la phrase de morphing qu'il deviendrait, l'effort (petit, moyen, grand) et tout risque (markup de bibliothèque de composants, une bibliothèque d'animation existante, un contrôle natif dont le sélecteur mobile serait perdu, des listes très longues).
  4. Priorisez par visibilité × fréquence × adéquation, regroupez les doublons (tous les boutons utilisant un Bouton partagé ne forment qu'un seul élément), limitez la liste à environ dix éléments, et listez tout ce que vous déconseillez avec la raison.
  5. Demandez confirmation dans la langue de l'utilisateur : les propositions numérotées, puis une question les invitant à choisir les numéros, à prendre toutes les recommandations, ou à taper les parties supplémentaires qu'ils souhaitent voir élastiques et qui ne sont pas dans la liste. Utilisez un outil de choix cliquable si l'environnement en a un, sinon du texte brut. Terminez votre tour ; ne faites aucune modification avant la réponse.
  6. Construisez ce qu'ils ont choisi plus tout ce qu'ils ont ajouté, puis suivez le Flux de travail ci-dessous et faites un rapport par élément.

Si l'utilisateur a déjà nommé les parties qu'il souhaite, auditez quand même : mettez ses parties en premier, proposez le reste en option, et gardez la question courte.

Fichiers

Tous les chemins sont relatifs au dossier de ce skill (le dossier contenant ce SKILL.md), pas au projet de l'utilisateur. Résolvez-les par rapport au dossier du skill lorsque vous les lisez ou les exécutez.

CheminCe que c'estQuand l'ouvrir
assets/stretchy-core.jsLe moteur de ressort plus les helpers (paires d'arêtes, indicateur segmenté, échelle de volume, arêtes SVG gonflantes, réordonnancement FLIP, objets lancés)À coller dans chaque page que vous construisez. Lisez-le une fois pour connaître les helpers.
assets/starter.htmlSquelette de page : tokens clair/sombre, police variable compatible thaï, gestion du mouvement réduit, le coeur intégré, un exemple de composantPour démarrer une nouvelle page
assets/showcase.htmlLes 24 composants fonctionnels de la construction originale. Chaque section JS commence par une bannière comme /* ---------- onglets ---------- */Quand une demande correspond à un composant du catalogue, faites grep -n "/\* ---------- " assets/showcase.html et lisez juste cette section plus son CSS
references/audit.mdComment trouver, scanner, juger et prioriser le code de l'utilisateur, et les modèles de messages de confirmation (thaï et anglais)À l'étape 0, à chaque fois
references/techniques.mdLes 11 techniques d'étirement en détail, avec code et réglagesAvant de construire quelque chose que vous n'avez jamais construit auparavant
references/components.mdCatalogue : la recette de chaque composant, valeurs de ressort et piègesQuand la demande nomme ou ressemble à un composant du catalogue
references/new-components.mdComment concevoir une version élastique de n'importe quel composant, plus 14 designs travaillés non présents dans la vitrineQuand la demande est en dehors du catalogue
scripts/audit_ui.pyScanner pour le code de l'utilisateur : pile, mouvement existant, et candidats élastiques avec fichier:ligneÀ l'étape 0, à chaque fois
scripts/check_page.pyVérification en navigateur sans tête : erreurs JS, débordement latéral sur téléphone et bureau, éléments invisibles volant les clics, et feuilles de contact des images d'animationAprès chaque construction, avant de remettre quoi que ce soit

Si un skill de design visuel est disponible (par exemple frontend-design ou ui-ux-pro-max), lisez-le aussi. Ce skill décide de l'apparence de la page (palette, typographie, mise en page) ; celui-ci décide de son mouvement. Un polissage du mouvement ne peut pas sauver une page d'apparence générique.

Flux de travail (après la confirmation de l'utilisateur)

  1. Écrivez la phrase de morphing pour chaque composant. "Quand déclencheur, forme A devient forme B, et elle s'étire parce que cause." Exemple : "Quand un onglet est cliqué, l'indicateur se déplace vers lui, et il s'étire parce que son bord avant est plus rigide que son bord arrière." Si vous ne pouvez pas remplir la cause, choisissez-en une dans le tableau ci-dessous. Cette phrase est le design ; tout le reste est implémentation.
  2. Choisissez la/les technique(s) dans le tableau. La plupart des composants utilisent un étirement principal plus un petit rebond de feedback.
  3. Définissez les états de repos sous forme de nombres : largeurs, hauteurs, rayons, positions, progression de 0 à 1. Les ressorts animent des nombres et vous les écrivez dans style dans onUpdate. Les transitions CSS ne gèrent que la couleur et l'opacité (et les fondus courts de contenu).
  4. Planifiez le contenu. Le texte et les icônes ne doivent pas se redistribuer ou se comprimer pendant que le conteneur morphe. Donnez à chaque état de contenu sa propre couche à sa taille finale, ancrée (généralement centrée), et faites un fondu : sortie rapide (≈150 ms), entrée après un délai de ≈100-150 ms avec une légère mise à l'échelle ou un flou. Le overflow:hidden du conteneur révèle le contenu en grandissant.
  5. Construisez à partir de assets/starter.html avec le coeur intégré. Écrivez les styles directement dans les callbacks de ressort ; ne déclenchez jamais un rendu de framework par image.
  6. Passez en revue l'accessibilité et l'interruption (liste de contrôle ci-dessous).
  7. Testez et regardez. Exécutez scripts/check_page.py, puis capturez des feuilles de contact des interactions importantes et regardez-les réellement. L'animation d'ajout au panier dans la construction originale "fonctionnait" mais avait l'air bon marché jusqu'à ce que les images soient inspectées ; les correctifs sont dans "Pour un rendu de qualité".

D'où vient l'étirement

SituationTechnique (détails dans references/techniques.md)Observé dans
Quelque chose se déplace entre des emplacementsPaire d'arêtes : deux ressorts pour les bords avant et arrière, le bord avant rigide, inversé selon la directionOnglets, points de carrousel, toggles, pastilles de choix, point radio, stepper, surlignage de dropdown, capsule d'évaluation, nav de barre latérale
Un conteneur change de taille ou de formeAxes séparés : ressorts distincts pour la largeur, la hauteur, le rayon avec différentes rigidités, de sorte qu'un axe mèneÎle dynamique, FAB vers formulaire, modale depuis un bouton, dropdown, bouton d'ajout au panier
Quelque chose devient plus étroit ou plus large rapidementPréservation de volume : mettre à l'échelle l'autre axe par la vélocité, scaleY = clamp(1 − v/k)Bouton Envoyer, champ de recherche, largeur de dropdown
Quelque chose se déplace rapidementÉtirement par vélocité : allonger dans la direction du mouvement, amincir perpendiculairementCurseur de slider, élément de panier lancé, cartes de carrousel (inclinaison), infobulle
Une grande surface se déplaceBords gonflants : dessiner la surface comme un chemin SVG dont le point de contrôle du bord est décalé par −vélocitéBarre latérale, feuille inférieure
Une partie pend d'une partie en mouvementSuiveur en retard : un ressort plus mou chasse le principal ; utiliser l'écart pour l'inclinaison ou le décalageBulle de valeur de slider, texte intérieur d'accordéon
Feedback pour un tap ou une arrivéeCoup de pied : ajouter de la vélocité à un ressort au repos à 0, il oscille et s'installeCase à cocher, pastilles, panier, étoiles, badge, secousse de limite de quantité
Des blobs se rejoignent ou se séparentFiltre Gooey : flou plus seuil alpha, source nette composée par-dessusMenu Gooey
Un élément devient un autre élémentTransformation de conteneur : commencer au rectangle du déclencheur, ressortir vers le rectangle cible, fondu de contenuModale, FAB, lancer dans le panier
Le changement nécessite une pause entreMorphing séquencé à travers une forme intermédiaire porteuse de sens (spinner, coche)Bouton Envoyer, bouton d'ajout au panier
Glissement au-delà d'une limite, ou réorganisation de listeBande élastique (diviser le dépassement par ≈3) et FLIP avec ressorts qui conservent la vélocitéCarrousel, feuille, liste de produits triée

Le moteur en un paragraphe

Spring est un oscillateur harmonique amorti intégré avec Euler semi-implicite en 8 sous-étapes par image (delta d'image plafonné à 1/30 s pour qu'un onglet en arrière-plan ne puisse pas l'exploser). to(cible) recible sans réinitialiser la vélocité, ce qui rend les clics rapides continus au lieu de redémarrer. set(x) saute sans animation, drag(x, v) suit un pointeur tout en enregistrant la vélocité, kick(v) injecte une impulsion. L'amortissement est multiplié par un facteur bounce global à chaque tick, de sorte qu'un seul contrôle au niveau de la page (doux 1.8, rebondissant 1, élastique 0.5) re-règle chaque composant en direct. Sous prefers-reduced-motion: reduce, to() saute à la cible et déclenche onRest immédiatement, donc la logique qui attend une animation s'exécute quand même.

Rapport d'amortissement ζ = c / (2√k). Les changements d'état de l'interface se sentent bien autour de ζ 0.6-0.8 ; feedback ludique à 0.3-0.5 ; au-dessus de 1 rien ne dépasse. Les préréglages utilisés dans la vitrine :

RôlekcNotes
Bord avant d'une paire d'arêtes420-52030-34Arrive en premier
Bord arrière140-17017-19Retarde, ce qui crée l'étirement
Largeur du conteneur (morphing)240-32018-24
Hauteur du conteneur (morphing)150-17015-17Plus lent que la largeur, donc la forme s'étire latéralement d'abord
Suiveur de pointeur (slider)65042Serré, mais a encore de la vélocité à lire
Suiveur en retard (bulle)15011Volontairement lâche
Feedback par coup de pied380-42012-16Coup de pied avec 6-10
Hauteur avec dépassement visible (accordéon)21014

Règles empiriques

  • Limitez chaque déformation. Contrôles : préservation de volume dans ±7-15 %. Petits objets rapides (une pastille lancée, un curseur de slider) : jusqu'à 35-70 % d'étirement. Taille toujours fixée à un minimum positif, car un ressort sous-amorti peut descendre en dessous de zéro.
  • Échangez la rigidité selon la direction. Dans une paire d'arêtes, le bord faisant face à la direction du déplacement reçoit le ressort rigide. Sans l'échange, l'indicateur s'étire correctement dans un sens et se comprime dans l'autre.
  • Minuterie opposée au retour. Si la largeur mène la hauteur à l'ouverture, laissez la hauteur mener la largeur à la fermeture. La forme retrace alors un chemin différent, ce qui se lit comme élastique plutôt que comme un rembobinage.
  • Ressorts pour les changements d'état, courbes temporelles pour les chemins dont la forme compte. Un objet lancé a besoin d'un arc prévisible (Bézier quadratique, progression ease-in-out, environ 800 ms). Des ressorts x/y indépendants donnent des chemins errants. Dérivez l'étirement de la vélocité image par image dans les deux cas.
  • Gardez le contenu droit. Pour étirer quelque chose dans une direction arbitraire sans faire pivoter son image ou son texte, utilisez rotate(a) scale(1+s, 1−s·0.45) rotate(−a).
  • Un mouvement audacieux par composant. Le reste du composant reste calme : un changement de couleur, un court fondu.
  • Calme au repos. Rien n'oscille tout seul. Au plus un moment orchestré au chargement de la page (la vitrine ouvre l'île une fois après 900 ms).

Pour un rendu de qualité (leçons de la construction)

La première version d'ajout au panier faisait voler une boule de couleur unie le long de deux ressorts indépendants, rétrécissait le bouton en un ovale violet vide et écrasait le panier. L'utilisateur a dit que ce n'était pas joli. Ce qui l'a corrigé se généralise :

  • Déplacez la vraie chose. Clonez l'image du produit, soulevez-la légèrement avec une ombre, puis rétrécissez-la en une pastille ronde qui montre encore l'image pendant qu'elle vole. Des points abstraits ressemblent à une sortie de débogage.
  • Les formes intermédiaires doivent porter un sens. Un bouton rétréci est un blob vide à moins qu'il n'affiche quelque chose : un spinner, une coche qui se dessine, un compteur. Le bouton d'ajout devient maintenant un cercle vert avec une coche, puis grandit en stepper − n +.
  • Volume subtil sur les contrôles. Un renflement vertical de ±18 % sur un bouton ressemblait à un œuf en forme de pilule. ±7 % se lit comme un matériau souple.
  • L'arrivée a besoin d'un receveur. L'icône du panier plonge et s'incline, un anneau pulse vers l'extérieur, le badge surgit. Quelque chose doit visiblement attraper ce qui a été lancé.
  • Ne coupez pas les indicateurs d'état. Un badge à l'intérieur d'une pilule overflow:hidden en morphing est coupé ; mettez-le dans un wrapper à l'extérieur du clip.
  • Ne répétez pas l'information. La pilule du panier affichait "3 ชิ้น 195 บาท" à côté d'un badge indiquant 3. Elle n'affiche plus que le total.
  • Dessinez les illustrations à un format fixe. Les formes CSS positionnées en pourcentage à l'intérieur d'une tuile s'étirent lorsque la tuile s'élargit. Utilisez un SVG en ligne avec un viewBox, centré dans la tuile.

Liste de contrôle pour l'accessibilité et la robustesse

  • Respectez le mouvement réduit (le coeur le fait ; assurez-vous que les keyframes et transitions CSS sont également coupés dans une requête média prefers-reduced-motion).
  • Utilisez de vrais button, input, role="switch", role="tab", role="slider", etc. avec le bon état aria-*, mis à jour dans le même appel qui démarre l'animation.
  • Le contenu caché par un morphing reçoit inert (et ne doit pas intercepter les clics : donnez aux couches invisibles pointer-events:none, puis réactivez-le avec l'état visible).
  • Déplacez le focus délibérément lorsque le contrôle ciblé disparaît dans un morphing : vers le contrôle de remplacement, de retour au déclencheur après la fermeture d'une boîte de dialogue, et seulement après que l'élément est à nouveau visible (un élément visibility:hidden ne peut pas recevoir le focus).
  • Testez avec le clavier : Tab, Entrée, Échap doivent fonctionner à chaque étape.
  • Vérifiez avec scripts/check_page.py en mode mobile (320 px large) et bureau (1280 px).

Skills associés