NOASSERTIONupdated 1mo ago
Let be the absolute plugin directory two levels above this SKILL.md. Resolve that path before running commands or opening shared references. When these instructions say swiftui-ctx, invoke /scripts/swiftui-ctx; do not assume the command is on PATH.
What can you do with Audit Swiftui Animation Motion?
name: audit-swiftui-animation-motion description: Audit macOS SwiftUI animation motion for correctness, current APIs, and production conventions. Use for that domain or as part of a full audit.
Bundled resource root
Let <swiftui-plugin-root> be the absolute plugin directory two levels above this SKILL.md. Resolve that path before running commands or opening shared references. When these instructions say swiftui-ctx, invoke <swiftui-plugin-root>/scripts/swiftui-ctx; do not assume the command is on PATH.
Audit SwiftUI Animation & Motion
AUDIT-ONLY Β· macOS-only Β· SwiftUI-only. Run this on a finished or in-progress macOS SwiftUI
project to detect β and where certain, flag β every way animation goes wrong: the deprecated implicit
.animation(_), a missing/wrong value:, raw duration curves where spring presets fit, ungated
macOS-14 spring presets / symbolEffect, hand-rolled loops that PhaseAnimator/KeyframeAnimator
replace, missing contentTransition, mis-wired matchedGeometryEffect hero transitions, and motion
that ignores Reduce Motion. Findings are written to disk in the toolkit's unified schema. This is never
a from-scratch animation generator.
Boundary / seam note (stay in lane)
- The Liquid Glass morph itself (
glassEffectID,GlassEffectContainerwiring) belongs toaudit-swiftui-liquid-glass. This skill flags only a genericmatchedGeometryEffectused on glassed views (anim-09) andcross_refs there β it does not audit glass-morph mechanics. - Render cost of motion (per-frame redraw,
.drawingGroup()) belongs toaudit-swiftui-view-performance. This skill owns the UX-restraint verdict on.repeatForever(anim-11) andcross_refs the cost there. - "The app ignores Reduce Motion entirely" across all surfaces belongs to
audit-swiftui-accessibility; this skill owns the motion-specific missing reduced path (anim-10) andcross_refs it. - Gesture-driven animation coupling belongs to
audit-swiftui-pointer-gestures;TimelineView-as-clock drawing belongs toaudit-swiftui-drawing-canvas. AppKit/Core Animation is out of scope entirely.
Domain rules (the spine)
- Scope every implicit animation.
.animation(_:value:)(orwithAnimation) β never the deprecated single-arg.animation(_), which animates every upstream change. - Prefer the native primitive over the hand-rolled loop. Spring presets over raw durations (macOS 14),
PhaseAnimator/KeyframeAnimatoroverTimer/repeatForeverchains (macOS 14),symbolEffectover a hand-spun SF Symbol,contentTransitionon value changes (macOS 13). - Motion is opt-out. Continuous/large motion must read
accessibilityReduceMotionand degrade. - A hero transition needs all four wires β one shared
@Namespace, matchingid, a single transaction, oneisSource.
Defect index (anim-01 β¦ anim-11)
id Β· tell Β· severity Β· fix Β· open reference. Severities: hard-fail (availability break),
warning (compiles but deprecated/non-native), advisory (judgment / restraint). flag = show the
β
, dev applies. No defect here is a mechanical single-answer rewrite, so all are fix_mode: flag-only
(the animated value / curve / primitive is always a judgment).
| id | One-line tell | Sev | Fix | Reference |
|---|---|---|---|---|
| anim-01 | single-arg .animation(x) (deprecated implicit form, macOS 12) |
warning | flag | animation-currency-and-springs.md |
| anim-02 | .animation(_:value:) on a constant, or withAnimation over no observed change |
warning | flag | animation-currency-and-springs.md |
| anim-03 | raw .easeInOut(duration:)/.linear(duration:) where a spring preset fits |
advisory | flag | animation-currency-and-springs.md |
| anim-04 | spring preset .bouncy/.smooth/.snappy ungated under a < macOS 14 floor (locator: warn; a real availability break only when the deployment floor is < 14 β the agent escalates per the reference) |
warning | flag | animation-currency-and-springs.md |
| anim-05 | Timer/.repeatForever/repeatCount loop where PhaseAnimator/KeyframeAnimator fits |
advisory | flag | phase-keyframe-and-symbol.md |
| anim-06 | .symbolEffect ungated under < macOS 14, or a hand-animated SF Symbol |
warning | flag | phase-keyframe-and-symbol.md |
| anim-07 | a value change (numeric Text, swapped symbol) animates without .contentTransition |
advisory | flag | phase-keyframe-and-symbol.md |
| anim-08 | matchedGeometryEffect missing shared @Namespace / matching id / one transaction |
warning | flag | hero-transitions-and-matched-geometry.md |
| anim-09 | generic matchedGeometryEffect morphing glassed views (should be glassEffectID) |
advisory | flag | hero-transitions-and-matched-geometry.md |
| anim-10 | continuous/large motion ignores accessibilityReduceMotion (no reduced path) |
warning | flag | reduce-motion-and-restraint.md |
| anim-11 | .repeatForever / always-on motion β UX restraint |
advisory | flag | reduce-motion-and-restraint.md |
The spring-preset floor is UNVERIFIED-LOOKING but reconciled to macOS 14 β the shipped DocC renders
macOS 10.15 (a type-property-inheritance quirk); the WWDC23 provenance is the truth. Carry macOS 14
for .bouncy/.smooth/.snappy (anim-04), never the rendered 10.15.
The real API, at a glance
Real (modern, prefer): animation(_:value:) (macOS 10.15+), withAnimation(_:_:),
matchedGeometryEffect(id:in:β¦) (11+), contentTransition(_:) (13+), PhaseAnimator / KeyframeAnimator
/ symbolEffect(_:options:value:) / Animation.bouncyΒ·.smoothΒ·.snappy (all 14+),
EnvironmentValues.accessibilityReduceMotion (10.15+). Deprecated: animation(_:) single-arg
(deprecated at macOS 12 β animation(_:value:) / withAnimation).
Floor values are the reconciled truth in
<swiftui-plugin-root>/references/_shared/floors-master.md and the invented-name list in
<swiftui-plugin-root>/references/_shared/hallucination-blacklist.md β read, never restate them. Full
βββ
rewrites live in this skill's references/*.md.
The 8-step audit workflow (execute verbatim)
-
ORIENT.
tree/findthe SwiftUI sources. Read the deployment target (project.pbxprojMACOSX_DEPLOYMENT_TARGET, orPackage.swiftplatforms:). Load-bearing: anim-04 (spring presets) and anim-06 (symbolEffect) fire only when the floor is below macOS 14. Record it. -
LOCATE. Run the shared hybrid lint runner:
bash <swiftui-plugin-root>/scripts/swiftui-lint.sh --skill audit-swiftui-animation-motion --dir <sources> --json /tmp/anim.json --sarif /tmp/anim.sarif. It runs this skill's tier-1 grep tells (lint/grep-tells.tsv) + the tier-2 structural ast-grep rule (lint/ast-grep/anim-01-implicit-animation.ymlβ single-arg arity grep can't express), plus a per-file parse probe, and emits unified JSON + SARIF. Read itsparse_warningsβ a flagged file did not fully parse; READ those by hand. The runner only LOCATES β never treat a hit as a finding. Engine + rule-file format + degradation:<swiftui-plugin-root>/references/_shared/lint-architecture.md. -
READ. Open every located file in full β never pattern-match-and-patch blind. The
value:binding, the@Namespaceidentity, the gate scope, and whether awithAnimationbody mutates observed state are invisible to grep. Build a per-file inventory: each animated view + its trigger state + its floor gate + its motion role. -
DETECT. Apply the index. Assign each candidate a confidence; report a finding only at 100 % certainty (a single-arg
.animation, an ungated preset under a <14 floor, a hero transition with two different namespaces). -
VERIFY. For anything β€ ~70 % confidence run both sources. (a) Practice β
bash <swiftui-plugin-root>/scripts/swiftui-ctx lookup <api> --json(e.g.animationβconsensus92 %(_, value)/ 7 %(_),symbolEffectβintroduced_macos14,matchedGeometryEffectβ 93 %(id, in)); for the deprecation rule alsoswiftui-ctx deprecated <api>. Readconsensus,deprecated+replacement,recommendedpermalink,introduced_macos,co_occurs_with. (b) Spec β confirm via Sosumi (curl -sSL https://sosumi.ai/<apple-path>; neverWebFetchdeveloper.apple.com); protocol in<swiftui-plugin-root>/references/_shared/sosumi-reference.md. Cross-checkintroduced_macosagainstfloors-master.md. The CLI contract is<swiftui-plugin-root>/references/_shared/swiftui-ctx-reference.md. Promote with the citation or discard.- Deeper corpus evidence (the real Animation vocabulary). Before flagging a raw curve where a
preset fits (anim-03) or backing a preset β
(anim-04), run
bash <swiftui-plugin-root>/scripts/swiftui-ctx valueBuilders ease+lookup bouncy/smooth/snappyfor the real spring presets, durations, and overloads β e.g..bouncyships in 405 uses across 92 repos (incl. the(duration, extraBounce)overload, jellyfin/Swiftfin), so it is consensus, not exotic. For a custom transition (anim-08) runbash <swiftui-plugin-root>/scripts/swiftui-ctx conformances ViewModifier(orTransition) for real conformers + permalinks (stable envelope,next_actionsβfile <id>); pair withswiftui-ctx lookup animation/examples withAnimation --shape.
- Deeper corpus evidence (the real Animation vocabulary). Before flagging a raw curve where a
preset fits (anim-03) or backing a preset β
(anim-04), run
-
REPORT. Write each confirmed finding (output contract below). One finding per file, zero-padded, ordered. Write the run's
_index.md. -
FIX. This domain is all
fix_mode: flag-onlyβ leave each findingopenwith the β in## Correct. The β is not a hand-written snippet β it is the swiftui-ctx consensus shape backed by a real macOS-26 example fetched withbash <swiftui-plugin-root>/scripts/swiftui-ctx file <recommended.id> --smartwhose GitHub permalink (plus the Sosumidoc:) goes in## Source. The verified canonical.animation(_:value:)β β the live swiftui-ctx consensus shape(_, value)(92 %), grounded in real corpus code, not a placeholder:// β Correct β swiftui-ctx `lookup animation` consensus `(_, value)`; real example // sindresorhus/Gifski `ex_7f40920aa8` (author_authority 1,013,769, 8,409β ) Label("Copy", systemImage: "doc.on.doc") .opacity(isShowingSuccess ? 0 : 1) .disabled(isShowingSuccess) .animation(.easeInOut(duration: 0.3), value: isShowingSuccess) // permalink: https://github.com/sindresorhus/Gifski/blob/7f873856e2acd8b52e6681dee3aec31e6cab23e4/Gifski/Utilities.swift#L4974 // doc: https://sosumi.ai/documentation/swiftui/view/animationObserve the fix-safety protocol (
<swiftui-plugin-root>/references/_shared/fix-safety-protocol.md); never weaken a check. -
DOUBLE-CHECK. Re-confirm every citation still resolves and still says the floor you reported. If a recommended rewrite would introduce a new tell (e.g. a spring preset now needs a macOS-14 gate), loop that file back to DETECT.
Confidence gating (load-bearing)
Report a finding only at 100 % certainty. Anything β€ ~70 % goes to VERIFY (step 5) before it can
become a finding β never emit a speculative finding. Everything here is fix_mode: flag-only.
Output contract
Inherits the toolkit's unified contract (full schema + body sections + frontmatter keys:
<swiftui-plugin-root>/references/_shared/finding-schema.md β do not restate it). Specialized:
- Findings:
swiftui-audits/animation-motion/<context>/NN-slug.md(one finding per file, zero-padded, ordered). Per-run index:swiftui-audits/animation-motion/_index.md. domain: animation-motion. Additive fieldmotion_role(the animation's role β e.g.state-feedback,hero-transition,attention,ambient) perfinding-schema.mdΒ§4.fix_modeisflag-onlyfor every defect.availabilityreads fromfloors-master.md.sourceis an Apple URL + access date (via Sosumi) orverify against Xcode 26 SDK. Emitcross_refon anim-09 (liquid-glass), anim-10 (accessibility), anim-11 (view-performance).
Starter <context> folders (file here whenβ¦):
<context> |
File a finding here when⦠|
|---|---|
implicit-and-currency/ |
a deprecated single-arg .animation, a dead value:, or a no-op withAnimation (anim-01, anim-02) |
spring-and-curves/ |
a raw curve where a preset fits, or an ungated spring preset under a <14 floor (anim-03, anim-04) |
phase-keyframe-symbol/ |
a hand-rolled loop, an ungated/avoidable symbolEffect, or a missing contentTransition (anim-05, anim-06, anim-07) |
hero-transitions/ |
a mis-wired matchedGeometryEffect, or a generic effect that should be a glass morph (anim-08, anim-09) |
reduce-motion-and-restraint/ |
motion that ignores Reduce Motion, or always-on .repeatForever (anim-10, anim-11) |
New-folder rule: if a finding does not fit any existing context folder, create a new one under
swiftui-audits/animation-motion/ with a lowercase-hyphen slug and note it in the run's _index.md.
Prefer an existing folder when the fit is reasonable. Two runs over the same code produce structurally
identical trees.
Reference routing
| File | Open when |
|---|---|
references/animation-currency-and-springs.md |
the deprecated .animation(_), dead value:, raw-curve-vs-preset, the spring-preset floor quirk (anim-01/02/03/04) |
references/phase-keyframe-and-symbol.md |
hand-rolled loops vs PhaseAnimator/KeyframeAnimator, symbolEffect floor/use, missing contentTransition (anim-05/06/07) |
references/hero-transitions-and-matched-geometry.md |
the four hero-transition wires, and the glass-morph seam (anim-08/09) |
references/reduce-motion-and-restraint.md |
the Reduce-Motion path and .repeatForever restraint (anim-10/11) |
lint/grep-tells.tsv + lint/ast-grep/*.yml |
step LOCATE β this skill's declarative rule set fed to the shared runner (tier-1 grep + tier-2 single-arg structural rule); tune detection here |
Shared toolkit references (point in, never restate):
| Shared file | For |
|---|---|
<swiftui-plugin-root>/references/_shared/floors-master.md |
every floor/availability value (the reconciled truth; the spring-preset macOS-14 correction) |
<swiftui-plugin-root>/references/_shared/hallucination-blacklist.md |
the canonical invented-name list |
<swiftui-plugin-root>/references/_shared/macos-arm-gating.md |
the macOS-arm gating rule + wrong-arm failure (anim-04, anim-06) |
<swiftui-plugin-root>/references/_shared/finding-schema.md |
the unified finding schema + the motion_role additive field |
<swiftui-plugin-root>/references/_shared/fix-safety-protocol.md |
the fix-safety protocol (step 7) |
<swiftui-plugin-root>/references/_shared/sosumi-reference.md |
the Apple-doc spec fetch protocol (step 5 VERIFY) |
<swiftui-plugin-root>/references/_shared/swiftui-ctx-reference.md |
the practice-corpus CLI contract β lookup/deprecated/file --smart for the consensus shape + permalinked example (steps 5 VERIFY Β· 7 FIX) |
<swiftui-plugin-root>/references/_shared/cross-ref-graph.md |
seam ownership + cross_ref targets (anim-09/10/11) |
Detection accelerator
bash <swiftui-plugin-root>/scripts/swiftui-lint.sh --skill audit-swiftui-animation-motion --dir <files-or-dir> [--json out.json] [--sarif out.sarif] β the toolkit's one shared hybrid lint engine, fed this skill's
declarative rules: tier-1 grep tells (lint/grep-tells.tsv, anim-01β¦anim-11) + the tier-2 ast-grep
structural rule (lint/ast-grep/anim-01-implicit-animation.yml β single-argument .animation arity grep
cannot express). It runs a per-file parse probe, emits unified JSON + SARIF, emits warnings/advisories only
(no hard-fail tells β nothing blocks the gate), and degrades to grep-only with a notice if ast-grep is unreachable
(npx --package @ast-grep/cli ast-grep; faster: brew install ast-grep). It only LOCATES β always READ
each hit in full before reporting (step 3). The thin scripts/anim-lint.sh forwards to this runner. Engine
- rule-file format + JSON/SARIF shape + safety rails:
<swiftui-plugin-root>/references/_shared/lint-architecture.md.
Install
Add Audit Swiftui Animation Motion to your client. Pick the one you use.
npx skills add yigitkonur/plugin-swiftuiInstalls every skill in the repository, then prompts for which to keep.
/plugin marketplace add yigitkonur/plugin-swiftuiAdds the repository as a plugin marketplace; install individual plugins with `/plugin install`.
git clone https://github.com/yigitkonur/plugin-swiftui
cp -r plugins/swiftui/skills/audit-swiftui-animation-motion ~/.claude/skills/A skill is a plain directory. Copy it into `.claude/skills/` in a project or in your home directory.
Score
80 / 100
Excellent