Wnb compose animation
Skill wenubey/claude-android-skills/skills/wnb-compose-animation
Reusable Claude Code skills for Android — Kotlin, Jetpack Compose, unit + UI testing, animations
npx -y skills add wenubey/claude-android-skills --skill wnb-compose-animationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 15 days oldThe repository was created 15 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use this skill when a Jetpack Compose UI is about to gain an animation, or when reviewing existing motion. Enforces the decision-aware gate — first ask "should this UI animate at all?"; only if yes, then pick the smallest API (AnimatedVisibility, animate*AsState, rememberTransition, AnimatedContent, Animatable). Requires every animation to earn its frame budget, carry a label, respect reduce-motion accessibility, and stay off scroll-adjacent recomposition paths. Triggers on "add animation", "animate", "fade in", "make it smooth", "smooth transition", "fancy transition", "AnimatedVisibility", "animate*AsState", "AnimatedContent", "rememberTransition", "Animatable", "Crossfade", "animateContentSize", "should this animate", "reduce motion".
SKILL.md
7.8 KB, as published. Nobody here has run it
Jetpack Compose — decision-aware animation
Most animation bugs in Compose are not "wrong API" bugs — they are "did not need to animate in the first place" bugs. This skill's job is to make the first decision explicit before any code is written. Only after the "should we animate?" gate passes does API selection matter.
For deep API selection (which primitive to reach for), this skill is intentionally thin — see [[compose-animations]] (chrisbanes/skills) linked at the end. This skill focuses on the gate.
Non-negotiables
- Every animation earns its frame budget. If a reviewer cannot answer "what does this animation communicate?" in one sentence, remove it.
- The gate runs first, always. Before typing
AnimatedVisibilityoranimate*AsState, answer the three "should we animate?" questions below. If any answer is no, no motion. - Every animation gets a
label.animate*AsState(label = "fabWidth"),rememberTransition(label = "phase"),AnimatedContent(label = "profile-content"). Enables the Compose Animation preview and tooling — no exceptions. - Respect reduce-motion. On Android, wrap animation opt-in on
LocalAccessibilityManager.current?.getRecommendedTimeoutMillis(...)or the platform'sSettings.Global.TRANSITION_ANIMATION_SCALE. If motion is disabled, jump to the end state directly. - No animated value on the recomposition-hot path. Reading a frame-updating
Statein a composable body causes recomposition every frame. ForModifier.offset,Modifier.graphicsLayer, andModifier.background, use the block-lambda form and read the animated value inside — see[[wnb-compose-ui-test]]and Chris Banes'compose-state-deferred-reads. - Do not fight Navigation Compose transitions. If the animation is between destinations, use Nav's built-in transition APIs, not
AnimatedContentlayered on top. - Test animated UI with
mainClock.advanceTimeBy(ms), neverThread.sleep. Compose has virtual time in tests. See[[wnb-compose-ui-test]].
The gate — should this UI animate?
Answer all three. If any is no, do not animate.
1. Does the change carry meaning that motion clarifies?
Motion should encode one of:
- Continuity — the outgoing element represents the same concept as the incoming (loading → content, expand → collapse). The user should not think "a new thing appeared."
- Hierarchy / spatial cues — a container growing to reveal children, a sheet sliding in from an edge that implies "this is on top of what was there."
- Feedback — a tap ripple, a button press depression. The animation confirms the input landed.
If the change is none of those (a static label swap, an initial page render, an incoming push notification), do not animate.
2. Would a still-image transition feel broken?
Cover the UI transition with your hand and swap the before/after state instantly. If it feels fine — leave it instant. Animation is a fix for a specific perceived discontinuity, not a default polish layer.
3. Will the motion still work under adversity?
- Reduce-motion enabled? Skip the animation gracefully — don't render a frozen half-frame.
- Slow device? A 300ms
springon aModifier.offsetinside a scrolling list will jank. Move it off the hot path or drop it. - Repeated interaction? If the user can trigger the same animation 5×/sec (rapid toggle), does the animation interrupt cleanly? Use
Animatablefor interruptible motion;animate*AsStatehandles simple retargeting but not multi-gesture handoff.
If the gate passes: pick the smallest API
Quick-pick for the 80% case. For the full API selection tree, see [[compose-animations]] (chrisbanes/skills).
| Situation | API |
|---|---|
| Show/hide a subtree, leave composition after exit | AnimatedVisibility |
| One value glides to a new target from state | animate*AsState (animateFloatAsState, animateDpAsState, animateColorAsState, …) |
| Several values driven by one state, kept in sync | rememberTransition + transition.animate* |
| Swap between different composable trees in the same slot | AnimatedContent (add contentKey if state is a wrapper like UiState) |
| Gesture-driven or interruptible motion | Animatable |
| Layout size change (text wrap, chip expand) | Modifier.animateContentSize() |
Common mistakes
- Adding an animation because the screen "felt static" without answering the gate — animation as decoration is drag. Ship it plain; add motion only if a user report says the transition is confusing.
- Animating the initial state on first composition — the first render should not fade in. That's not a state transition; that's an appearance. If the loading indicator is followed by content, the transition point is loading→content, not nothing→loading.
animate*AsStateon alpha and expecting the child to stop laying out — the child stays in composition and layout. UseAnimatedVisibilityfor enter/exit semantics.- Three parallel
animateDpAsStatecalls that must stay in sync — onerememberTransitionbinds them to a single state. Parallel calls drift. AnimatedContent(targetState = uiState)whereuiStateis a sealed class with adatapayload — every payload change triggers an animation. AddcontentKeymapped to the visual shape ("loading","content","error").- Ignoring reduce-motion — the accessibility setting exists. Users have vestibular sensitivity. Test with the system setting toggled.
- Adding a
Crossfadebetween destinations Navigation is already animating — you get a double transition. Delete theCrossfade, keep Nav's transition. - Missing
label— the animation shows up as "Unlabelled" in the Compose Animation preview. Debugging suffers.
Testing animated UI (one-liner)
Use composeTestRule.mainClock.advanceTimeBy(500) after the trigger, then assert on the final visual state. Do NOT try to intercept frames mid-flight — assert on the settled state. See [[wnb-compose-ui-test]] for the full test skeleton.
If the SUT uses AnimatedContent, remember: after advanceTimeBy, the enter/exit content may both be in the tree briefly — filter your finder with hasParent(...) or a testTag on the target content.
Related skills
[[wnb-compose-ui-test]]— how to test animated screens without flake.[[wnb-viewmodel-udf]]— the state-driven contract animations render against.- External:
chrisbanes/skills→compose-animations— deep API selection (AnimatedContent.contentKey,SeekableTransitionState,Animatablegestures, performance-hot-path handling). If the gate passes and you need the full API tree, load it.
Attribution
The API-selection philosophy ("pick the smallest API that matches the problem") is the framing from Chris Banes' compose-animations skill. This skill deliberately keeps the API section thin and refers to Chris's work for depth; the value added here is the upstream decision gate — "should this UI animate at all?" — which the referenced work does not enforce.
Gives 0 of the 12 instructions most css styling skills give
Counted across 586 of the 596 authors here whose files we hold, read 2026-08-06
- avoid excessive centered layoutsin 55 of 586, across 12 files
- bundle code into single HTML filein 54 of 586, across 14 files
- Respect prefers-reduced-motion user settingsin 52 of 586, across 35 files
- avoid purple gradientsin 51 of 586, across 11 files
- avoid uniform rounded cornersin 51 of 586, across 11 files
- avoid Inter fontin 51 of 586, across 11 files
- edit generated files to develop artifactin 50 of 586, across 10 files
- animate only transform and opacity propertiesin 43 of 586
- Make touch targets at least 44x44 pixelsin 41 of 586, across 15 files
- Ensure minimum color contrast of 4.5:1in 39 of 586, across 10 files
- use tailwind cssin 39 of 586, across 24 files
- Use SVG icons instead of emojisin 38 of 586, across 11 files
Said here and by no other author read
- Answer the three animation gate questions before writing code
- Remove animation if it communicates nothing
- Skip animation if instant state swap feels fine
- Jump to end state if reduce-motion is enabled
- Read animated values inside modifier lambdas
- Use Navigation Compose transitions between destinations
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.