Audit motion
Check new CSS / JSX for animation that runs by default — the project default is `motion: reduced` and any motion must gate on prefers-reduced-motion.From its SKILL.md
npx -y skills add tlennon-ie/neurodock --skill audit-motionAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 11 stars11 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.
SKILL.md
3.8 KB, as published. Nobody here has run it
audit-motion
Pre-flight check for any UI change that adds animation. The profile
default per ADR 0004 is motion: reduced — i.e. no animation runs
unless the user has explicitly opted in. The prefers-reduced-motion
CSS media query is the floor; the profile preference is the ceiling.
Authoritative references:
- ADR 0004:
docs/decisions/0004-profile-schema-design.md— "no animation by default" is one of the lived-experience-led defaults. - Profile schema:
packages/core/schemas/profile.schema.json(preferences.motion: reduced | system | full).
When to use
- Before merging any PR that changes
.css,.scss,.tsx, or.jsxinpackages/extension-browser/or any docs site source. - When adding a new component that uses animation libraries (framer, gsap, motion).
- Before an extension release — failing this check means a regression ships to users.
What it does
- Runs
git diff origin/main...HEAD --name-onlyand filters to.css,.scss,.tsx,.jsx,.ts,.jsunderpackages/extension-browser/,docs/, and any other site source. - For each file, scans added lines for animation tokens:
transition:/transition-(Tailwind)animation:/animation-(Tailwind)@keyframesframer-motionimportsgsap/ScrollTriggerimportsanimate()/<motion.JSX usage
- For each hit, verifies that either the surrounding context is
inside an
@media (prefers-reduced-motion: no-preference)block (CSS) or the component readsuseReducedMotion()/ the profile'spreferences.motionbefore triggering motion (JSX). - Reports unguarded hits.
How to invoke
There is no scripted runner yet. Suggested invocation:
git diff origin/main...HEAD --name-only \
| grep -E '\.(css|scss|tsx|jsx|ts|js)$' \
| xargs -I{} grep -nHE \
'transition[:-]|animation[:-]|@keyframes|framer-motion|from .gsap|<motion\.|animate\(' \
{}
Then for each hit, confirm guard:
- CSS: a parent
@media (prefers-reduced-motion: no-preference) { ... } - JSX: a check against
useReducedMotion()or the profile preference
The required guard pattern
CSS
.card {
/* Static styles always run */
opacity: 1;
}
@media (prefers-reduced-motion: no-preference) {
.card {
transition: opacity var(--duration-normal) var(--ease-out-expo);
}
}
React / JSX
const reduced = useReducedMotion();
// ...
<div style={{ transition: reduced ? "none" : "opacity 300ms" }}>
Or, when honouring the profile preference (skill-side):
const { motion } = useProfilePreferences();
if (motion === "reduced") return <Static />;
return <Animated />;
Output format
packages/extension-browser/src/popup/Welcome.tsx:42: <motion.div> — no useReducedMotion / profile guard
packages/extension-browser/src/styles/card.css:18: transition: opacity 200ms — no prefers-reduced-motion media query
Exit non-zero on any unguarded hit.
Limitations
- Heuristic regex; will produce false positives on string literals that
contain the word
transitionoutside CSS context. - Does not detect motion implemented via canvas / WebGL / SVG SMIL — review those by hand.
- Does not enforce the profile preference at runtime — that is the
responsibility of the component and the skill SDK
(
@neurodock/skill-sdk/neurodock-skill).
Voice
When flagging an unguarded animation, name the user impact in one line: "this animates on first load for every ND user with the default profile." Do not lecture about vestibular sensitivity; the contributor either already knows or will look it up.