diff --git a/SOURCES.md b/SOURCES.md index 19bb931..89db2ef 100644 --- a/SOURCES.md +++ b/SOURCES.md @@ -19,10 +19,13 @@ Local-only skills need just an origin note. - [emilkowalski/skills](https://github.com/emilkowalski/skills/tree/d23d7f88a2e21c9e4b1418c7abe420f5c1052ba7/skills/animate) — `skills/animate/SKILL.md` and `RECIPES.md`; reference import commit `d23d7f88a2e21c9e4b1418c7abe420f5c1052ba7`. The original parent-skill import baseline remains unknown. MIT notice in `licenses/emilkowalski-animate.txt`. - [Jakubantalik/transitions.dev](https://github.com/Jakubantalik/transitions.dev/tree/598d3d6ad89dabb4bdf742fd2e887ca53914a888/skills) — `skills/transitions-dev/` and `skills/transitions-polish/`; commit `598d3d6ad89dabb4bdf742fd2e887ca53914a888`. No license file found in this revision; see `THIRD_PARTY_NOTICES.md`. -- One automatically discoverable skill owns motion decisions, implementation, - tuning, and checks. Its main file links directly to task-based pattern guides. +- One automatically discoverable cross-platform skill owns motion decisions, + implementation, tuning, and checks. Its entrypoint holds the shared motion gate + and routes web work to the imported task-based pattern guides and SwiftUI or + UIKit work to guidance grounded in current Apple documentation. Origin notes and source-based groups are absent from the skill. Current project - tokens, component behavior, and measured results take precedence over examples. + tokens, platform conventions, component behavior, and measured results take + precedence over examples. - `transitions-dev/01-*.md` through `32-*.md` map to `references/patterns/.md` with numeric prefixes removed. Exceptions: `18-texts-reveal.md` maps to `stagger.md`; `29-reasoning-stream.md` maps to diff --git a/skills/animate/SKILL.md b/skills/animate/SKILL.md index d3c0c2c..d477dd4 100644 --- a/skills/animate/SKILL.md +++ b/skills/animate/SKILL.md @@ -1,21 +1,24 @@ --- name: animate -description: "Use when a task concerns interface motion: deciding whether to animate, adding or tuning transitions and enter or exit effects, shaping gesture or input feedback, or supporting reduced motion." +description: "Design, implement, and tune web, SwiftUI, and UIKit interface motion. Use for motion on those platforms when deciding whether to animate, building or reviewing transitions, shaping gesture-driven motion, or honoring reduced-motion preferences." --- # Animate -Build the requested animation. Make each decision in order; an instant state change is a valid result when motion would add delay without meaning. +Build the requested motion on its real platform. An instant state change is a valid result when motion would add delay without meaning. ## Process -1. Inspect the real interaction, nearby motion, design tokens, browser targets, input methods, use rate, state changes, and runnable surface. Finish when each animated element in scope has this evidence. -2. Gate the motion. Name its purpose and frequency tier below. If it fails the gate, keep the state change instant, test that state on the real surface, and explain why. -3. Choose the simplest tool, properties, curve, timing, interruption behavior, and exit path that fit the interaction. -4. Read the matching pattern below and [implementation checks](references/implementation.md). Use the current design system and motion tools. Include reduced-motion and input-specific behavior in the same change. For timing or feel changes, read [tuning](references/tuning.md). -5. Test enter, exit, rapid repeat, interruption, keyboard, pointer, touch, reduced motion, and a busy page. Slow playback when timing or coordination needs closer study. +1. Inspect the real interaction, nearby motion, design tokens, target platform and supported versions, input methods, use rate, state changes, and runnable surface. Finish when every animated element in scope has this evidence. +2. Apply the motion gate below. Name the purpose and frequency tier. If motion fails the gate, keep the state change instant and test that state on the real surface. +3. Read the platform branch before choosing APIs or timing: + - For websites and web apps, read [web motion](references/web.md). Read only the pattern guides that match the interaction. + - For SwiftUI or UIKit, read [Apple-platform motion](references/apple-platforms.md). +4. Choose the smallest mechanism that fits the interaction. Use the product's current components, motion system, and tokens before adding another abstraction or dependency. +5. Implement the reduced-motion path with the primary behavior. Preserve useful feedback and the final state when movement is reduced or removed. +6. Test entry, exit, rapid repeat, interruption, affected input methods, and reduced motion on the target platform. Slow playback or frame-step when timing and coordination need closer study. -When motion passes the gate, the work is done when it has a stated purpose, uses the product's own system, affected keyboard, pointer, touch, and reduced-motion paths pass, and any unavailable device check is named. When motion fails the gate, the work is done when the reason is clear and the instant or static state passes the same affected paths. +When motion passes the gate, the work is done when it has a stated purpose, follows the product and platform conventions, survives interruption, and passes the affected input and reduced-motion paths. When motion fails the gate, the work is done when the instant or static state passes those same affected paths. ## Motion gate @@ -23,7 +26,7 @@ When motion passes the gate, the work is done when it has a stated purpose, uses | --- | --- | | Very frequent or keyboard-led | Instant | | Frequent, such as hover or list movement | None or very short | -| Occasional, such as a modal, drawer, or toast | Standard UI motion | +| Occasional, such as a modal, drawer, or toast | Standard interface motion | | Rare, explanatory, or celebratory | More room for delight | Motion needs one job: @@ -35,130 +38,23 @@ Motion needs one job: - Teach a rare flow. - Add delight to a rare moment without blocking the task. -Keep content steady while someone reads or acts on it. Decorative motion must not move useful data or its controls. +Keep content steady while someone reads or acts on it. Decorative motion must not move useful data or its controls. If none of the jobs applies, stop at the gate. -If none applies, stop at the gate. +## Shared behavior -## Tool choice - -Use the first tool that meets the need: - -| Need | Tool | -| --- | --- | -| Hover, press, color, or class-driven state | CSS transition | -| Entry on mount without extra state, when browser targets support it | CSS `@starting-style` | -| Fixed sequence or loop | CSS keyframes | -| Programmatic playback without a library | Web Animations API | -| Gesture, spring, layout, or interruptible value | The product's existing motion library | - -Apply motion to the project's existing accessible components. Preserve focus management, keyboard behavior, and state semantics; a motion recipe does not supply a complete modal, menu, or control. - -Extend current tokens before adding new curves or times. Add a library only when the existing stack cannot express the required behavior. - -## Motion choices - -- Prefer `transform` and `opacity`; measure before accepting layout-heavy properties. -- Name every transitioned property. `transition: all` can animate later changes by mistake. -- Enter near the final size, such as `scale(0.95)` with opacity, rather than growing from `scale(0)`. -- Set a trigger-anchored surface's transform origin to its trigger. Keep an unanchored modal centered. -- Use percentages when travel should track the animated element's own size. -- Enter or exit: start with a strong ease-out. -- Move or morph on screen: start with ease-in-out. -- Hover or color change: start with ease. -- Continuous motion: use linear. -- UI motion should usually finish within 300ms. Start near 100–160ms for press feedback, 125–200ms for tooltips and small popovers, 150–250ms for menus, and 200–500ms for large panels. -- Treat values as test points. Distance, size, content, and product tone change what feels right. - -## Interruption and gestures - -- Use transitions for state changes that must retarget and springs for gesture-driven values. -- Start an interruption from the live on-screen value, not the previous target. -- Carry release velocity into gesture motion and project it toward the likely resting point. -- Keep dragged content attached to the pointer, preserve the grab offset, and capture the pointer through the gesture. -- Apply rising resistance past a drag boundary rather than a hard stop. -- Enter and exit along paths that preserve spatial meaning. Tune their times separately when the system response should be faster. - -## Access and input - -- Honor `prefers-reduced-motion`. Replace large movement, zoom, parallax, and bounce with a short fade, color change, or instant state change. -- Gate hover-only motion with `@media (hover: hover) and (pointer: fine)`. -- Keep controls usable while decorative motion runs. -- Test gesture work on a real touch device when possible. - -## Patterns - -Choose by the interaction and state change after motion passes the gate. Read only the relevant guides, including their variables, state hooks, CSS, JavaScript, and limits. Each guide combines techniques for one interaction; choose the variant that fits the existing component. If none fits, build from the rules above. - -### Surfaces and layout - -| Need | Guide | -| --- | --- | -| Resize a container | [Card resize](references/patterns/card-resize.md) | -| Open a trigger-anchored menu or popover | [Menu dropdown](references/patterns/menu-dropdown.md) | -| Open a centered dialog and backdrop | [Modal open / close](references/patterns/modal.md) | -| Reveal a panel inside a region | [Panel reveal](references/patterns/panel-reveal.md) | -| Move between screens | [Page side-by-side](references/patterns/page-side-by-side.md) | -| Open an edge sheet or drawer | [Drawer or sheet](references/patterns/drawer.md) | -| Expand a disclosure | [Accordion expand](references/patterns/accordion.md) | -| Show or dismiss a notification | [Toast open / close](references/patterns/toast.md) | -| Stack notices and expand them on hover | [Banner stacking](references/patterns/banner-stacking.md) | - -### Controls and gestures - -| Need | Guide | -| --- | --- | -| Confirm a press | [Button press](references/patterns/button-press.md) | -| Drag, flick, interrupt, and settle a surface | [Drag to dismiss](references/patterns/drag-to-dismiss.md) | -| Move a tab indicator or reveal active labels | [Tabs sliding](references/patterns/tabs-sliding.md) | -| Show hints and move between nearby triggers | [Tooltip open/close](references/patterns/tooltip.md) | -| Turn a trigger into its menu | [Plus to menu morph](references/patterns/plus-menu-morph.md) | -| Check or clear a checkbox | [Checkbox check](references/patterns/checkbox-check.md) | -| Move a switch thumb | [Toggle](references/patterns/toggle.md) | -| Show deliberate hold progress | [Hold to confirm](references/patterns/hold-to-confirm.md) | - -### Feedback and changing content - -| Need | Guide | -| --- | --- | -| Reveal a badge or dot | [Notification badge](references/patterns/notification-badge.md) | -| Update digits with a short entry | [Number pop-in](references/patterns/number-pop-in.md) | -| Roll digits through a counter | [Spinning counter](references/patterns/spinning-counter.md) | -| Replace text in place | [Text states swap](references/patterns/text-states-swap.md) | -| Replace an icon | [Icon swap](references/patterns/icon-swap.md) | -| Confirm completion | [Success check](references/patterns/success-check.md) | -| Signal a validation error | [Error state shake](references/patterns/error-state-shake.md) | -| Animate a cleared input | [Input clear with dissolve](references/patterns/input-clear-dissolve.md) | -| Confirm a like | [Like button](references/patterns/like-button.md) | -| Blend overlapping content states | [Crossfade with overlap](references/patterns/crossfade.md) | - -### Loading and streams - -| Need | Guide | -| --- | --- | -| Replace a placeholder with content | [Skeleton loader and reveal](references/patterns/skeleton-reveal.md) | -| Animate an in-progress label | [Shimmer text](references/patterns/shimmer-text.md) | -| Switch status labels | [Thinking states](references/patterns/thinking-states.md) | -| Advance real log or status entries | [Log stream](references/patterns/log-stream.md) | -| Reveal arriving words | [Streaming text](references/patterns/streaming-text.md) | -| Show a dot-matrix loading state | [Matrix dot loader](references/patterns/matrix-loader.md) | - -### Hover and entry - -| Need | Guide | -| --- | --- | -| Lift nearby items in a row | [Avatar group hover](references/patterns/avatar-group-hover.md) | -| Tilt a decorative card toward the pointer | [Card hover tilt](references/patterns/card-tilt.md) | -| Turn a chevron into an arrow | [Learn more hover](references/patterns/learn-more-hover.md) | -| Reveal lines, lists, or grids in sequence | [Texts reveal](references/patterns/stagger.md) | -| Reveal occasional content on entering the viewport | [Scroll reveal](references/patterns/scroll-reveal.md) | -| Control playback with the Web Animations API | [Programmatic animation](references/patterns/programmatic-animation.md) | +- Prefer the platform's standard components and transitions. Add custom motion only when it communicates something the system behavior does not. +- Make feedback immediate, brief, and proportional to the state change. Frequent actions should feel faster and quieter than rare ones. +- Preserve spatial meaning between entry and exit. Tune them separately when the system response should be faster on dismissal. +- Start interrupted motion from its live value. Gesture-driven motion should track input directly, preserve the grab point, and settle from the release state rather than restart from an old target. +- Keep controls usable while decorative motion runs. Important information needs a non-motion cue. +- Treat durations, curves, and spring values as test points. Geometry, content, input, platform, and product tone determine what feels right. ## Handoff -Deliver the implemented code first. When the gate rejects motion, deliver the no-motion decision and its instant or static alternative instead. Then state: +Deliver the implemented result first. When the gate rejects motion, deliver the instant or static alternative instead. Then state: - The motion gate result and purpose. -- When motion passes the gate: the chosen tool, properties, curve, and time or spring. When it fails: the instant or static mechanism. -- Any feel check that still needs slow motion, frame stepping, or device testing. +- The platform mechanism and reduced-motion behavior. +- Any feel check that still needs slow motion, frame stepping, Simulator, or device testing. Keep the note brief; the tested interaction is the result. diff --git a/skills/animate/references/apple-platforms.md b/skills/animate/references/apple-platforms.md new file mode 100644 index 0000000..45ca76a --- /dev/null +++ b/skills/animate/references/apple-platforms.md @@ -0,0 +1,49 @@ +# Apple-platform motion + +Use this branch for SwiftUI and UIKit. Prefer the app's existing framework, system components, navigation, transitions, and animation tokens. System components already provide familiar motion and can adapt to accessibility settings and input methods; custom motion must earn the extra behavior. See Apple's [Motion](https://developer.apple.com/design/human-interface-guidelines/motion) guidance. + +## SwiftUI + +Check the project's deployment target and installed SDK before choosing an API. Use an availability-compatible transition or state lifecycle instead of raising the deployment target for motion alone. + +Model motion as a state change: + +- Use `withAnimation` when one action should animate the state changes it performs. +- Use `animation(_:value:)` when one view should animate in response to one explicit value. Keep the modifier close to the affected view so unrelated changes do not inherit the animation. +- Use `transition(_:)` for insertion and removal, and `contentTransition(_:)` when existing content changes in place. +- Use `Transaction` when a subtree needs a different animation, no animation, or an animation completion. Keep lifecycle work tied to state and completion APIs rather than guessed delays. +- Reach for custom `Animatable` data only when built-in animatable modifiers and transitions cannot express the interpolation. + +These choices follow Apple's [SwiftUI animation overview](https://developer.apple.com/documentation/swiftui/animations), [`animation(_:value:)`](https://developer.apple.com/documentation/swiftui/view/animation(_:value:)), and [`Transaction`](https://developer.apple.com/documentation/swiftui/transaction) documentation. + +For gesture-driven motion, keep transient gesture state separate from durable model state. Let direct manipulation track the gesture without lag, then animate only the settle or dismissal after release. `GestureState` resets when the gesture ends and is suitable for transient interaction state; see [Adding interactivity with gestures](https://developer.apple.com/documentation/swiftui/adding-interactivity-with-gestures) and [`GestureState`](https://developer.apple.com/documentation/swiftui/gesturestate). + +## UIKit + +Use the app's established transition and animation APIs for ordinary state changes. Use `UIViewPropertyAnimator` when the interaction must pause, reverse, scrub, or continue from a partially completed state. Keep one owner for the animator lifecycle and finish or cancel it when the owning interaction ends. See [`UIViewPropertyAnimator`](https://developer.apple.com/documentation/uikit/uiviewpropertyanimator). + +## Reduce Motion + +In SwiftUI, read the `accessibilityReduceMotion` environment value. In UIKit, read `UIAccessibility.isReduceMotionEnabled` and observe `UIAccessibility.reduceMotionStatusDidChangeNotification` through a lifecycle-owned `NotificationCenter` observation. When notified, read the value again, replace or finish disallowed active motion without delaying useful content, expose the final state, and apply the new preference to later transitions. Remove the observation and cancel owned decorative motion when its owner ends. + +When Reduce Motion is enabled: + +- Remove or replace large travel, zoom, parallax, depth simulation, spinning, and repeated movement. +- Prefer a restrained fade, color or material change, symbol change, or instant state update when it preserves meaning. +- Stop decorative loops and autoplaying motion. Do not make useful content wait on a disabled animation. +- Preserve the final state and communicate important feedback through text, shape, color with sufficient contrast, haptics, or audio as appropriate; motion cannot be the only cue. + +Apple specifically calls out large and three-dimensional movement in [`accessibilityReduceMotion`](https://developer.apple.com/documentation/swiftui/environmentvalues/accessibilityreducemotion) and provides broader evaluation criteria in [Reduced Motion evaluation criteria](https://developer.apple.com/help/app-store-connect/manage-app-accessibility/reduced-motion-evaluation-criteria). + +## Verification + +Run the real interaction in Simulator or on device. Check: + +- Initial presentation and dismissal. +- Rapid repeat, reversal, and interruption. +- Drag tracking, cancellation, release velocity, and settling when gestures apply. +- Touch plus any supported pointer, keyboard, VoiceOver, or Switch Control path affected by the change. +- Reduce Motion both enabled and disabled, including a preference change while the screen is present. +- Text scaling, rotation, safe areas, and content changes that alter the animated geometry. + +Use slow animations or a recording to inspect origin, path, clipping, unexpected layout changes, and competing animations. Profile only when the real interaction drops frames, performs expensive redraws, or shows a measurable rendering problem. diff --git a/skills/animate/references/web.md b/skills/animate/references/web.md new file mode 100644 index 0000000..a7adaf4 --- /dev/null +++ b/skills/animate/references/web.md @@ -0,0 +1,121 @@ +# Web motion + +Use this branch for websites and web apps. Follow [implementation checks](implementation.md) for every web motion change, whether or not a pattern fits. For timing or feel changes, also read [tuning](tuning.md). + +## Tool choice + +Use the first tool that meets the need: + +| Need | Tool | +| --- | --- | +| Hover, press, color, or class-driven state | CSS transition | +| Entry on mount without extra state, when browser targets support it | CSS `@starting-style` | +| Fixed sequence or loop | CSS keyframes | +| Programmatic playback without a library | Web Animations API | +| Gesture, spring, layout, or interruptible value | The product's existing motion library | + +Apply motion to the project's existing accessible components. Preserve focus management, keyboard behavior, and state semantics; a motion recipe does not supply a complete modal, menu, or control. + +Extend current tokens before adding new curves or times. Add a library only when the existing stack cannot express the required behavior. + +## Motion choices + +- Prefer `transform` and `opacity`; measure before accepting layout-heavy properties. +- Name every transitioned property. `transition: all` can animate later changes by mistake. +- Enter near the final size, such as `scale(0.95)` with opacity, rather than growing from `scale(0)`. +- Set a trigger-anchored surface's transform origin to its trigger. Keep an unanchored modal centered. +- Use percentages when travel should track the animated element's own size. +- Enter or exit: start with a strong ease-out. +- Move or morph on screen: start with ease-in-out. +- Hover or color change: start with ease. +- Continuous motion: use linear. +- UI motion should usually finish within 300ms. Start near 100–160ms for press feedback, 125–200ms for tooltips and small popovers, 150–250ms for menus, and 200–500ms for large panels. +- Treat values as test points. Distance, size, content, and product tone change what feels right. + +## Interruption and gestures + +- Use transitions for state changes that must retarget and springs for gesture-driven values. +- Start an interruption from the live on-screen value, not the previous target. +- Carry release velocity into gesture motion and project it toward the likely resting point. +- Keep dragged content attached to the pointer, preserve the grab offset, and capture the pointer through the gesture. +- Apply rising resistance past a drag boundary rather than a hard stop. +- Enter and exit along paths that preserve spatial meaning. Tune their times separately when the system response should be faster. + +## Access and input + +- Honor `prefers-reduced-motion`. Replace large movement, zoom, parallax, and bounce with a short fade, color change, or instant state change. +- Gate hover-only motion with `@media (hover: hover) and (pointer: fine)`. +- Keep controls usable while decorative motion runs. +- Test gesture work on a real touch device when possible. +- Test on a representative busy page or loaded application state, not only an isolated component. +- For JavaScript-driven motion, respond when the media query changes and cancel work that no longer applies. CSS alone does not stop JavaScript timers or animation loops. + +The Web Animations API guidance also recommends a way to pause or disable animation and a complementary experience for reduced-motion users. See [MDN: Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API#accessibility) and [`prefers-reduced-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion). + +## Patterns + +Choose by the interaction and state change after motion passes the gate. Read only the relevant guides, including their variables, state hooks, CSS, JavaScript, and limits. Each guide combines techniques for one interaction; choose the variant that fits the existing component. If none fits, build from the rules above. + +### Surfaces and layout + +| Need | Guide | +| --- | --- | +| Resize a container | [Card resize](patterns/card-resize.md) | +| Open a trigger-anchored menu or popover | [Menu dropdown](patterns/menu-dropdown.md) | +| Open a centered dialog and backdrop | [Modal open / close](patterns/modal.md) | +| Reveal a panel inside a region | [Panel reveal](patterns/panel-reveal.md) | +| Move between screens | [Page side-by-side](patterns/page-side-by-side.md) | +| Open an edge sheet or drawer | [Drawer or sheet](patterns/drawer.md) | +| Expand a disclosure | [Accordion expand](patterns/accordion.md) | +| Show or dismiss a notification | [Toast open / close](patterns/toast.md) | +| Stack notices and expand them on hover | [Banner stacking](patterns/banner-stacking.md) | + +### Controls and gestures + +| Need | Guide | +| --- | --- | +| Confirm a press | [Button press](patterns/button-press.md) | +| Drag, flick, interrupt, and settle a surface | [Drag to dismiss](patterns/drag-to-dismiss.md) | +| Move a tab indicator or reveal active labels | [Tabs sliding](patterns/tabs-sliding.md) | +| Show hints and move between nearby triggers | [Tooltip open/close](patterns/tooltip.md) | +| Turn a trigger into its menu | [Plus to menu morph](patterns/plus-menu-morph.md) | +| Check or clear a checkbox | [Checkbox check](patterns/checkbox-check.md) | +| Move a switch thumb | [Toggle](patterns/toggle.md) | +| Show deliberate hold progress | [Hold to confirm](patterns/hold-to-confirm.md) | + +### Feedback and changing content + +| Need | Guide | +| --- | --- | +| Reveal a badge or dot | [Notification badge](patterns/notification-badge.md) | +| Update digits with a short entry | [Number pop-in](patterns/number-pop-in.md) | +| Roll digits through a counter | [Spinning counter](patterns/spinning-counter.md) | +| Replace text in place | [Text states swap](patterns/text-states-swap.md) | +| Replace an icon | [Icon swap](patterns/icon-swap.md) | +| Confirm completion | [Success check](patterns/success-check.md) | +| Signal a validation error | [Error state shake](patterns/error-state-shake.md) | +| Animate a cleared input | [Input clear with dissolve](patterns/input-clear-dissolve.md) | +| Confirm a like | [Like button](patterns/like-button.md) | +| Blend overlapping content states | [Crossfade with overlap](patterns/crossfade.md) | + +### Loading and streams + +| Need | Guide | +| --- | --- | +| Replace a placeholder with content | [Skeleton loader and reveal](patterns/skeleton-reveal.md) | +| Animate an in-progress label | [Shimmer text](patterns/shimmer-text.md) | +| Switch status labels | [Thinking states](patterns/thinking-states.md) | +| Advance real log or status entries | [Log stream](patterns/log-stream.md) | +| Reveal arriving words | [Streaming text](patterns/streaming-text.md) | +| Show a dot-matrix loading state | [Matrix dot loader](patterns/matrix-loader.md) | + +### Hover and entry + +| Need | Guide | +| --- | --- | +| Lift nearby items in a row | [Avatar group hover](patterns/avatar-group-hover.md) | +| Tilt a decorative card toward the pointer | [Card hover tilt](patterns/card-tilt.md) | +| Turn a chevron into an arrow | [Learn more hover](patterns/learn-more-hover.md) | +| Reveal lines, lists, or grids in sequence | [Texts reveal](patterns/stagger.md) | +| Reveal occasional content on entering the viewport | [Scroll reveal](patterns/scroll-reveal.md) | +| Control playback with the Web Animations API | [Programmatic animation](patterns/programmatic-animation.md) |