Geschmeidige Morph-UI
Diese Skill erzeugt Oberflächen, bei denen jeder Zustandswechsel morphiert: Ein Tab-Indikator streckt sich wie ein Wurm auf dem Weg zum nächsten Tab, eine Schaltfläche quetscht sich in einen Ladekreis und schwillt zurück, ein Produktbild hebt sich von seiner Karte ab, schrumpft zu einem Chip und fällt in den Warenkorb. Sie wurde aus dem Bau einer 24-Komponenten-Showcase (in assets/showcase.html) destilliert, inklusive aller Bugs und Designkorrekturen, die dabei aufgetreten sind.
Das Erscheinungsbild basiert auf einer Idee: Dehnung wird durch etwas verursacht. Ein Element verformt sich, weil seine Teile uneins sind (ein Rand kommt vor dem anderen an), weil es sich schnell bewegt (es verlängert sich entlang seines Pfades), weil es zusammengedrückt wird (es wölbt sich in die andere Richtung, um sein Volumen zu erhalten), oder weil es gerade getroffen wurde (es wackelt). Ein zufälliges Wackeln in einer Schleife wirkt billig; Dehnung mit einer Ursache wirkt wie Material.
Schritt 0, jedes Mal: Überprüfen Sie den Code des Benutzers und bestätigen Sie
Bevor Sie etwas bauen oder bearbeiten, finden Sie heraus, was in der eigenen Oberfläche des Benutzers geschmeidig werden kann, und lassen Sie ihn wählen. Details, Bewertungskriterien und Nachrichtenvorlagen finden Sie in references/audit.md; die Kurzfassung:
- Finden Sie den Code: eingefügten Code, angehängte Dateien (auf claude.ai, listen Sie
/mnt/user-data/uploadsauf), das Arbeitsverzeichnis oder Repository, einen Artefakt-Link (lesen Sie ihn mit dem Artefakt-Tool) oder eine Quelle, auf die der Benutzer verweist. Wenn es keine gibt, sagen Sie, wonach Sie gesucht haben, und bieten Sie zwei Wege an: Teilen Sie den Code, oder bauen Sie etwas Neues von Grund auf. - Scannen Sie ihn:
python scripts/audit_ui.py <pfade>meldet den Stack, vorhandene Animationen, fehlende Reduced-Motion-Behandlung und Kandidaten pro UI-Kategorie mitdatei:zeile, jeweils abgebildet auf eine Showcase-Komponente und Technik. - Lesen Sie jeden Treffer im Kontext und behalten Sie nur echte Kandidaten. Notieren Sie für jeden, wo er ist, was jetzt passiert, den Morph-Satz, den er werden würde, den Aufwand (klein, mittel, groß) und etwaige Risiken (Komponentenbibliothek-Markup, eine vorhandene Animationsbibliothek, ein natives Steuerelement, dessen mobiler Picker verloren ginge, sehr lange Listen).
- Priorisieren Sie nach Sichtbarkeit × Häufigkeit × Passgenauigkeit, gruppieren Sie Duplikate (alle Schaltflächen, die eine gemeinsame
Button-Klasse verwenden, sind ein Element), begrenzen Sie die Liste auf etwa zehn, und listen Sie alles auf, wovon Sie abraten, mit Begründung. - Fragen Sie nach Bestätigung in der Sprache des Benutzers: die nummerierten Vorschläge, dann eine Frage, die ihn einlädt, Nummern auszuwählen, alle empfohlenen zu nehmen oder zusätzliche Teile einzugeben, die geschmeidig sein sollen und nicht auf der Liste stehen. Verwenden Sie ein tippbares Auswahlwerkzeug, falls die Umgebung eines bietet, andernfalls Klartext. Beenden Sie den Zug damit; nehmen Sie keine Änderungen vor der Antwort vor.
- Bauen Sie, was sie gewählt haben, plus alles, was sie hinzugefügt haben, befolgen Sie dann den Workflow und berichten Sie pro Element.
Wenn der Benutzer die gewünschten Teile bereits genannt hat, überprüfen Sie trotzdem: Setzen Sie seine Teile an die erste Stelle, bieten Sie den Rest als optional an und halten Sie die Frage kurz.
Dateien
Alle Pfade sind relativ zum Ordner dieser Skill (dem Ordner, der diese SKILL.md enthält), nicht zum Projekt des Benutzers. Lösen Sie sie gegen den Skill-Ordner auf, wenn Sie sie lesen oder ausführen.
| Pfad | Was es ist | Wann öffnen |
|---|---|---|
assets/stretchy-core.js | Die Feder-Engine plus Hilfsfunktionen (Randpaare, segmentierter Indikator, Volumenskala, gewölbte SVG-Ränder, FLIP-Neuordnung, geworfene Objekte) | In jede Seite einfügen, die Sie bauen. Einmal lesen, um die Hilfsfunktionen zu kennen. |
assets/starter.html | Seiten-Grundgerüst: Hell/Dunkel-Tokens, Thai-fähige variable Schriftart, Reduced-Motion-Handhabung, die eingebundene Engine, eine Beispielkomponente | Eine neue Seite davon starten |
assets/showcase.html | Alle 24 funktionierenden Komponenten des Originalbaus. Jeder JS-Abschnitt beginnt mit einem Banner wie /* ---------- tabs ---------- */ | Wenn eine Anfrage zu einer Katalogkomponente passt, grep -n "/\* ---------- " assets/showcase.html und nur diesen Abschnitt plus sein CSS lesen |
references/audit.md | Wie man den Code des Benutzers findet, scannt, bewertet und priorisiert, sowie die Bestätigungsnachrichtenvorlagen (Thai und Englisch) | Bei Schritt 0, jedes Mal |
references/techniques.md | Die 11 Dehnungstechniken im Detail, mit Code und Abstimmungswerten | Bevor Sie etwas bauen, das Sie noch nicht gebaut haben |
references/components.md | Katalog: Rezept jeder Komponente, Federwerte und Fallstricke | Wenn die Anfrage eine Katalogkomponente nennt oder ihr ähnelt |
references/new-components.md | Wie man eine geschmeidige Version jeder Komponente entwirft, plus 14 ausgearbeitete Designs, die nicht im Showcase sind | Immer wenn die Anfrage außerhalb des Katalogs liegt |
scripts/audit_ui.py | Scanner für den Code des Benutzers: Stack, vorhandene Animationen und geschmeidige Kandidaten mit datei:zeile | Bei Schritt 0, jedes Mal |
scripts/check_page.py | Headless-Browser-Prüfung: JS-Fehler, seitlicher Überlauf auf Telefon und Desktop, unsichtbare Elemente, die Klicks stehlen, und Kontaktbögen von Animationsframes | Nach jedem Bau, bevor Sie etwas übergeben |
Wenn eine visuelle Design-Skill verfügbar ist (z.B. frontend-design oder ui-ux-pro-max), lesen Sie sie ebenfalls. Diese Skill entscheidet, wie die Seite aussieht (Palette, Schriftart, Layout); diese hier entscheidet, wie sie sich bewegt. Bewegungspolitur kann eine generisch aussehende Seite nicht retten.
Workflow (nach Bestätigung durch den Benutzer)
- Schreiben Sie den Morph-Satz für jede Komponente. "Wenn Auslöser, wird Form A zu Form B, und sie dehnt sich, weil Ursache." Beispiel: "Wenn ein Tab angeklickt wird, bewegt sich der Indikator zu ihm, und er dehnt sich, weil seine Vorderkante steifer ist als seine Hinterkante." Wenn Sie die Ursache nicht ausfüllen können, wählen Sie eine aus der Tabelle unten. Dieser Satz ist das Design; alles andere ist Implementierung.
- Wählen Sie die Technik(en) aus der Tabelle. Die meisten Komponenten verwenden eine primäre Dehnung plus ein kleines Rückmeldungswackeln.
- Definieren Sie Ruhezustände als Zahlen: Breiten, Höhen, Radien, Positionen, Fortschritt 0 bis 1. Federn animieren Zahlen und Sie schreiben sie in
styleimonUpdate. CSS-Übergänge behandeln nur Farbe und Deckkraft (und kurze Inhaltsüberblendungen). - Planen Sie den Inhalt. Text und Symbole dürfen nicht umfließen oder gequetscht werden, während der Container morphiert. Geben Sie jedem Inhaltszustand seine eigene Ebene in seiner endgültigen Größe, verankert (normalerweise zentriert), und überblenden Sie: schnell ausblenden (≈150 ms), nach einer Verzögerung von ≈100–150 ms mit leichtem Skalieren oder Weichzeichner einblenden. Das
overflow:hiddendes Containers gibt den Inhalt frei, während er wächst. - Bauen Sie von
assets/starter.htmlmit der eingebundenen Engine. Schreiben Sie Styles direkt in Feder-Callbacks; lösen Sie niemals ein Framework-Neu-Rendering pro Frame aus. - Barrierefreiheit und Unterbrechungsprüfung (Checkliste unten).
- Testen und anschauen. Führen Sie
scripts/check_page.pyaus, erfassen Sie dann Frame-Kontaktbögen der wichtigen Interaktionen und schauen Sie sie sich tatsächlich an. Die Add-to-Cart-Animation im Originalbau "funktionierte", sah aber billig aus, bis die Frames inspiziert wurden; die Korrekturen befinden sich in "Damit es gut aussieht".
Woher die Dehnung kommt
| Situation | Technik (Details in references/techniques.md) | Zu sehen in |
|---|---|---|
| Etwas bewegt sich zwischen Plätzen | Randpaar: zwei Federn für Vorder- und Hinterkante, Vorderkante steif, je nach Richtung getauscht | Tabs, Karussell-Punkte, Schalter, Auswahlchips, Radio-Punkt, Schrittanzeige, Dropdown-Hervorhebung, Bewertungskapsel, Seitenleisten-Navigation |
| Ein Container ändert Größe oder Form | Geteilte Achsen: separate Federn für Breite, Höhe, Radius mit unterschiedlicher Steifigkeit, sodass eine Achse führt | Dynamic Island, FAB zu Formular, Modal aus Schaltfläche, Dropdown, Add-to-Cart-Schaltfläche |
| Etwas wird schnell schmaler oder breiter | Volumenerhaltung: Skalieren Sie die andere Achse um die Geschwindigkeit, scaleY = clamp(1 − v/k) | Senden-Schaltfläche, Suchfeld, Dropdown-Breite |
| Etwas bewegt sich schnell | Geschwindigkeitsdehnung: Verlängern in Bewegungsrichtung, quer dazu verschmälern | Schieberegler-Griff, geworfenes Warenkorb-Element, Karussell-Karten (Schrägstellung), Tooltip |
| Eine große Fläche bewegt sich | Ausbeulender Rand: Zeichnen Sie die Fläche als SVG-Pfad, dessen Rand-Kontrollpunkt um −Geschwindigkeit versetzt ist | Seitenleiste, Bottom Sheet |
| Ein Teil hängt an einem sich bewegenden Teil | Nachhinkender Folger: eine weichere Feder jagt der Hauptfeder hinterher; verwenden Sie den Abstand für Neigung oder Versatz | Schieberegler-Werte-Blase, Akkordeon-Innentext |
| Rückmeldung für einen Tipp oder ein Eintreffen | Kick: Geschwindigkeit zu einer Feder hinzufügen, die bei 0 ruht, sie wackelt und beruhigt sich | Checkbox, Chips, Warenkorb, Sterne, Abzeichen, Mengenlimit-Schütteln |
| Klumpen verschmelzen oder teilen sich | Goo-Filter: Weichzeichner plus Alpha-Schwelle, scharfe Quelle darüber komponiert | Goo-Menü |
| Ein Element wird zu einem anderen Element | Container-Transformation: Starten Sie beim Rechteck des Auslösers, federn Sie zum Zielrechteck, überblenden Sie den Inhalt | Modal, FAB, Warenkorb-Wurf |
| Die Änderung benötigt eine Pause dazwischen | Sequenzierter Morph durch eine Zwischenform, die Bedeutung trägt (Spinner, Häkchen) | Senden-Schaltfläche, Add-to-Cart-Schaltfläche |
| Ziehen über ein Limit hinaus oder eine Liste wird neu geordnet | Gummiband (Überscrollen durch ≈3 teilen) und FLIP mit Federn, die die Geschwindigkeit beibehalten | Karussell, Blatt, sortierte Produktliste |
Die Engine in einem Absatz
Spring ist ein gedämpfter harmonischer Oszillator, integriert mit semi-implizitem Euler in 8 Unterschritten pro Frame (Frame-Delta auf 1/30 s begrenzt, damit ein Hintergrund-Tab sie nicht explodieren lässt). to(ziel) setzt das Ziel neu ohne die Geschwindigkeit zurückzusetzen, was schnelle Klicks kontinuierlich statt neustartend wirken lässt. set(x) springt ohne Animation, drag(x, v) folgt einem Zeiger, während die Geschwindigkeit aufgezeichnet wird, kick(v) injiziert einen Impuls. Die Dämpfung wird bei jedem Tick mit einem globalen bounce-Faktor multipliziert, sodass ein einziger Steuerelement auf Seitenebene (weich 1.8, hüpfend 1, dehnbar 0.5) jede Komponente live nachstimmt. Unter prefers-reduced-motion: reduce springt to() sofort zum Ziel und feuert onRest sofort, sodass Logik, die auf eine Animation wartet, trotzdem läuft.
Dämpfungsverhältnis ζ = c / (2√k). UI-Zustandsänderungen fühlen sich richtig an bei ζ 0.6–0.8; spielerisches Feedback bei 0.3–0.5; über 1 überschießt nichts. Die Voreinstellungen im gesamten Showcase:
| Rolle | k | c | Anmerkungen |
|---|---|---|---|
| Vorderkante eines Randpaars | 420–520 | 30–34 | Kommt zuerst an |
| Hinterkante | 140–170 | 17–19 | Hinkt hinterher, was die Dehnung erzeugt |
| Container-Breite (Morph) | 240–320 | 18–24 | |
| Container-Höhe (Morph) | 150–170 | 15–17 | Langsamer als Breite, sodass sich die Form zuerst seitlich dehnt |
| Zeiger-Folger (Schieberegler) | 650 | 42 | Straff, aber immer noch mit ablesbarer Geschwindigkeit |
| Nachhinkender Folger (Blase) | 150 | 11 | Absichtlich locker |
| Kick-Feedback | 380–420 | 12–16 | Kick mit 6–10 |
| Höhe mit sichtbarem Überschwingen (Akkordeon) | 210 | 14 |
Faustregeln
- Jede Verformung begrenzen. Steuerelemente: Volumenerhaltung innerhalb ±7–15 %. Kleine schnelle Dinge (ein geworfener Chip, ein Schieberegler-Griff): bis zu 35–70 % Dehnung. Größen immer auf ein positives Minimum begrenzen, da eine unterdämpfte Feder unter Null unterschreiten kann.
- Steifigkeit nach Richtung tauschen. In einem Randpaar bekommt der Rand, der der Bewegungsrichtung zugewandt ist, die steife Feder. Ohne den Tausch dehnt sich der Indikator in eine Richtung korrekt und staucht in die andere.
- Gegenteiliges Timing auf dem Rückweg. Wenn die Breite beim Öffnen der Höhe voraus ist, lassen Sie die Höhe beim Schließen der Breite voraus sein. Die Form zeichnet dann einen anderen Pfad nach, was sich wie elastisch statt wie ein Zurückspulen liest.
- Federn für Zustandsänderungen, zeitbasierte Kurven für Pfade, deren Form wichtig ist. Ein geworfenes Objekt braucht einen vorhersagbaren Bogen (quadratische Bézier, Ease-In-Out-Fortschritt, etwa 800 ms). Unabhängige x/y-Federn ergeben wandernde Pfade. Leiten Sie die Dehnung in beiden Fällen aus der Geschwindigkeit von Frame zu Frame ab.
- Inhalt aufrecht halten. Um etwas entlang einer beliebigen Richtung zu dehnen, ohne sein Bild oder seinen Text zu drehen, verwenden Sie
rotate(a) scale(1+s, 1−s·0.45) rotate(−a). - Eine kühne Bewegung pro Komponente. Der Rest der Komponente bleibt ruhig: ein Farbwechsel, eine kurze Einblendung.
- Ruhig im Leerlauf. Nichts wackelt von selbst. Höchstens ein orchestrierter Moment beim Seitenladen (die Showcase öffnet die Insel einmal nach 900 ms).
Damit es gut aussieht (Lehren aus dem Bau)
Die erste Add-to-Cart-Version ließ eine einfarbige Kugel entlang zweier unabhängiger Federn fliegen, schrumpfte die Schaltfläche zu einer leeren violetten Ellipse und quetschte den Warenkorb. Der Benutzer sagte, es sei nicht hübsch. Was es behoben hat, verallgemeinert sich:
- Bewegen Sie das echte Ding. Klonen Sie das Produktbild, heben Sie es leicht mit einem Schatten an, schrumpfen Sie es dann zu einem runden Chip, der das Bild immer noch zeigt, während es fliegt. Abstrakte Punkte lesen sich wie Debug-Ausgabe.
- Zwischenformen müssen Bedeutung tragen. Eine geschrumpfte Schaltfläche ist ein leerer Klecks, es sei denn, sie zeigt etwas: einen Spinner, ein Häkchen, das sich selbst zeichnet, eine Zählung. Die Hinzufügen-Schaltfläche wird jetzt zu einem grünen Kreis mit einem Häkchen, dann wächst sie zum − n + Schrittschalter.
- Subtiles Volumen bei Steuerelementen. Eine ±18 % vertikale Wölbung bei einer Schaltfläche sah aus wie ein pillenförmiges Ei. ±7 % liest sich wie weiches Material.
- Ankunft braucht einen Empfänger. Das Warenkorb-Symbol neigt sich und kippt, ein Ring pulsiert heraus, das Abzeichen poppt auf. Etwas muss sichtbar fangen, was geworfen wurde.
- Statusmarken nicht abschneiden. Ein Abzeichen innerhalb einer morphierenden
overflow:hidden-Pille wird abgeschnitten; setzen Sie es in einen Wrapper außerhalb des Clips. - Informationen nicht wiederholen. Die Warenkorb-Pille zeigte "3 ชิ้น 195 บาท" neben einem Abzeichen mit 3. Sie zeigt jetzt nur noch die Gesamtsumme.
- Illustrationen mit festem Seitenverhältnis zeichnen. Prozentual positionierte CSS-Formen innerhalb einer Kachel dehnen sich, wenn die Kachel breiter wird. Verwenden Sie ein Inline-SVG mit einer viewBox, zentriert in der Kachel.
Barrierefreiheit und Robustheits-Checkliste
- Reduced Motion respektieren (die Engine tut es; stellen Sie sicher, dass CSS-Keyframes und -Übergänge ebenfalls in einer
prefers-reduced-motionMedia Query ausgeschnitten werden). - Echte
button,input,role="switch",role="tab",role="slider"usw. mit dem richtigenaria-*-Zustand verwenden, aktualisiert im selben Aufruf, der die Animation startet. - Inhalt, der durch einen Morph verborgen wird, bekommt
inert(und darf keine Klicks abfangen: unsichtbaren Ebenenpointer-events:nonegeben, dann mit dem sichtbaren Zustand wieder einschalten). - Fokus absichtlich verschieben, wenn das fokussierte Steuerelement in einem Morph verschwindet: zum Ersatz-Steuerelement, zurück zum Auslöser, nachdem ein Dialog geschlossen wurde, und erst, nachdem das Element wieder sichtbar ist (ein
visibility:hidden-Element kann nicht fokussiert werden).