framer-motion-combat-juice
<!-- Loaded by combat-effects-upgrade when task involves card trajectories, hit-react animations, multi-hit stagger, or layout animations in combat components -->
$ npx -y skills add notque/vexjoy-agent --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
<!-- Loaded by combat-effects-upgrade when task involves card trajectories, hit-react animations, multi-hit stagger, or layout animations in combat components -->
Agent definition
framer-motion-combat-juice.mdFramer Motion Combat Juice Reference
<!-- Loaded by combat-effects-upgrade when task involves card trajectories, hit-react animations, multi-hit stagger, or layout animations in combat components -->
Framer Motion renamed to **Motion** in 2025. Package: `framer-motion` → `motion`, imports: `framer-motion` → `motion/react`. API identical. Check package.json.
import { motion, AnimatePresence, useMotionValue, useSpring } from 'motion/react';
// Legacy: import from 'framer-motion' (same API)Patterns below use `motion/react`. Replace with `framer-motion` if needed.
---
Current Animation Inventory and Upgrades
| # | Component | Current Pattern | Upgraded To | |---|-----------|----------------|-------------| | 1 | FramedCard — hover | scale(1.03) | scale(1.06) + 3D tilt toward cursor | | 2 | FramedCard — play exit | scale(1.3) + slide up | trajectory arc toward target slot | | 3 | PlayerCharacter — hit react | scale(0.85→1) | scale(0.85→1) + rotation wobble + spring overshoot | | 4 | CombatPopups — damage | float up, instant | cascading stagger 100ms per hit | | 5 | EnemyCharacter — idle | 4s breathing scale | add subtle sway (±2deg rotate) | | 6 | CombatArena — screen shake | CSS class toggle | add motion blur via filter | | 7 | CardHand — draw | fade in | slide from draw pile position via layoutId | | 8 | PlayerCharacter — status badge | spring scale pop | add jiggle on value change | | 9 | CardHand — reflow | spring layout | smooth reflow using layout prop |
---
Pattern 1: Card Play Trajectory
Card flies from hand to target slot. `layoutId` measures start/end positions automatically.
// src/components/FramedCard.tsx
import { motion, AnimatePresence } from 'motion/react';
interface FramedCardProps {
card: Card;
isPlaying: boolean;
targetRef: React.RefObject<HTMLDivElement>;
}
export function FramedCard({ card, isPlaying, targetRef }: FramedCardProps) {
return (
<AnimatePresence mode="popLayout">
{!isPlaying && (
<motion.div
key={card.id}
layoutId={`card-${card.id}`}
initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}
exit={{
// Arc trajectory: scale up briefly, fly toward target
scale: [1, 1.15, 0.9],
y: -120,
opacity: 0,
transition: {
duration: 0.35,
ease: [0.25, 0.46, 0.45, 0.94],
},
}}
transition={{
type: 'spring',
stiffness: 400,
damping: 28,
}}
className="framed-card"
>
<CardContent card={card} />
</motion.div>
)}
</AnimatePresence>
);
}---
Pattern 2: Hand Reflow with Layout Animation
Remaining cards auto-animate into new positions. No manual position calculation.
// src/components/CardHand.tsx
import { motion, AnimatePresence } from 'motion/react';
interface CardHandProps {
cards: Card[];
onPlay: (card: Card) => void;
}
export function CardHand({ cards, onPlay }: CardHandProps) {
return (
// layout on the container propagates to children
<motion.div layout className="card-hand">
<AnimatePresence mode="popLayout">
{cards.map((card, index) => (
<motion.div
key={card.id}
layout // automatically animates position changes
initial={{ opacity: 0, scale: 0.8, y: 30 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.7, y: -20 }}
transition={{
type: 'spring',
stiffness: 350,
damping: 30,
// Stagger cards as they enter
delay: index * 0.05,
}}
onClick={() => onPlay(card)}
style={{
// Fan layout uses CSS custom property driven by index
'--card-index': index,
'--card-count': cards.length,
} as React.CSSProperties}
>
<FramedCard card={card} />
</motion.div>
))}
</AnimatePresence>
</motion.div>
);
}---
Pattern 3: Hit React with Spring Overshoot
Character squishes, overshoots past normal, settles. Spring physics handle interruption automatically.
// src/components/PlayerCharacter.tsx
import { motion, useAnimation } from 'motion/react';
export function PlayerCharacter({ isHit, hitType }: PlayerCharacterProps) {
const controls = useAnimation();
useEffect(() => {
if (!isHit) return;
// Fire-and-forget: squish + wobble sequence
void controls.start({
scale: [1, 0.82, 1.08, 0.96, 1],
rotate: [0, hitType === 'left' ? -5 : 5, hitType === 'left' ? 3 : -3, 0],
transition: {
duration: 0.5,
times: [0, 0.15, 0.35, 0.6, 1],
ease: 'easeInOut',
},
});
}, [isHit, hitType, controls]);
return (
<motion.div
animate={controls}
style={{ originX: 0.5, originY: 0.8 }} // pivot from feet
>
<CharacterSprite />
</motion.div>
);
}---
Pattern 4: Multi-Hit Damage Stagger
Each damage number 100ms after previous, so players track individual values.
// src/components/CombatPopups.tsx
import { motion, AnimatePresence } from 'motion/react';
interface DamageEvent {
id: string;
value: number;
x: number;
y: number;
timestamp: number;
}
interface CombatPopupsProps {
damageEvents: DamageEvent[];
}
export function CombatPopups({ damageEvents }: CombatPopupsProps) {
return (
<AnimatePresence>
{damageEvents.map((event, index) => (
<motion.div
key={event.id}
className="damage-popup"
initial={{ opacity: 0, scale: 0.6, y: 0 }}
animate={{ opacity: 1, scale: 1.2, y: -20 }}
exit={{ opacity: 0, scale: 0.8, y: -60 }}
transition={{
// Stagger each hit 100ms from the previous
delay: index * 0.1,Read more
Framer Motion Combat Juice Reference
<!-- Loaded by combat-effects-upgrade when task involves card trajectories, hit-react animations, multi-hit stagger, or layout animations in combat components -->
Framer Motion renamed to **Motion** in 2025. Package: `framer-motion` → `motion`, imports: `framer-motion` → `motion/react`. API identical. Check package.json.
import { motion, AnimatePresence, useMotionValue, useSpring } from 'motion/react';
// Legacy: import from 'framer-motion' (same API)Patterns below use `motion/react`. Replace with `framer-motion` if needed.
---
Current Animation Inventory and Upgrades
| # | Component | Current Pattern | Upgraded To | |---|-----------|----------------|-------------| | 1 | FramedCard — hover | scale(1.03) | scale(1.06) + 3D tilt toward cursor | | 2 | FramedCard — play exit | scale(1.3) + slide up | trajectory arc toward target slot | | 3 | PlayerCharacter — hit react | scale(0.85→1) | scale(0.85→1) + rotation wobble + spring overshoot | | 4 | CombatPopups — damage | float up, instant | cascading stagger 100ms per hit | | 5 | EnemyCharacter — idle | 4s breathing scale | add subtle sway (±2deg rotate) | | 6 | CombatArena — screen shake | CSS class toggle | add motion blur via filter | | 7 | CardHand — draw | fade in | slide from draw pile position via layoutId | | 8 | PlayerCharacter — status badge | spring scale pop | add jiggle on value change | | 9 | CardHand — reflow | spring layout | smooth reflow using layout prop |
---
Pattern 1: Card Play Trajectory
Card flies from hand to target slot. `layoutId` measures start/end positions automatically.
// src/components/FramedCard.tsx
import { motion, AnimatePresence } from 'motion/react';
interface FramedCardProps {
card: Card;
isPlaying: boolean;
targetRef: React.RefObject<HTMLDivElement>;
}
export function FramedCard({ card, isPlaying, targetRef }: FramedCardProps) {
return (
<AnimatePresence mode="popLayout">
{!isPlaying && (
<motion.div
key={card.id}
layoutId={`card-${card.id}`}
initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}
exit={{
// Arc trajectory: scale up briefly, fly toward target
scale: [1, 1.15, 0.9],
y: -120,
opacity: 0,
transition: {
duration: 0.35,
ease: [0.25, 0.46, 0.45, 0.94],
},
}}
transition={{
type: 'spring',
stiffness: 400,
damping: 28,
}}
className="framed-card"
>
<CardContent card={card} />
</motion.div>
)}
</AnimatePresence>
);
}---
Pattern 2: Hand Reflow with Layout Animation
Remaining cards auto-animate into new positions. No manual position calculation.
// src/components/CardHand.tsx
import { motion, AnimatePresence } from 'motion/react';
interface CardHandProps {
cards: Card[];
onPlay: (card: Card) => void;
}
export function CardHand({ cards, onPlay }: CardHandProps) {
return (
// layout on the container propagates to children
<motion.div layout className="card-hand">
<AnimatePresence mode="popLayout">
{cards.map((card, index) => (
<motion.div
key={card.id}
layout // automatically animates position changes
initial={{ opacity: 0, scale: 0.8, y: 30 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.7, y: -20 }}
transition={{
type: 'spring',
stiffness: 350,
damping: 30,
// Stagger cards as they enter
delay: index * 0.05,
}}
onClick={() => onPlay(card)}
style={{
// Fan layout uses CSS custom property driven by index
'--card-index': index,
'--card-count': cards.length,
} as React.CSSProperties}
>
<FramedCard card={card} />
</motion.div>
))}
</AnimatePresence>
</motion.div>
);
}---
Pattern 3: Hit React with Spring Overshoot
Character squishes, overshoots past normal, settles. Spring physics handle interruption automatically.
// src/components/PlayerCharacter.tsx
import { motion, useAnimation } from 'motion/react';
export function PlayerCharacter({ isHit, hitType }: PlayerCharacterProps) {
const controls = useAnimation();
useEffect(() => {
if (!isHit) return;
// Fire-and-forget: squish + wobble sequence
void controls.start({
scale: [1, 0.82, 1.08, 0.96, 1],
rotate: [0, hitType === 'left' ? -5 : 5, hitType === 'left' ? 3 : -3, 0],
transition: {
duration: 0.5,
times: [0, 0.15, 0.35, 0.6, 1],
ease: 'easeInOut',
},
});
}, [isHit, hitType, controls]);
return (
<motion.div
animate={controls}
style={{ originX: 0.5, originY: 0.8 }} // pivot from feet
>
<CharacterSprite />
</motion.div>
);
}---
Pattern 4: Multi-Hit Damage Stagger
Each damage number 100ms after previous, so players track individual values.
// src/components/CombatPopups.tsx
import { motion, AnimatePresence } from 'motion/react';
interface DamageEvent {
id: string;
value: number;
x: number;
y: number;
timestamp: number;
}
interface CombatPopupsProps {
damageEvents: DamageEvent[];
}
export function CombatPopups({ damageEvents }: CombatPopupsProps) {
return (
<AnimatePresence>
{damageEvents.map((event, index) => (
<motion.div
key={event.id}
className="damage-popup"
initial={{ opacity: 0, scale: 0.6, y: 0 }}
animate={{ opacity: 1, scale: 1.2, y: -20 }}
exit={{ opacity: 0, scale: 0.8, y: -60 }}
transition={{
// Stagger each hit 100ms from the previous
delay: index * 0.1,Essays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.
Repo: notque/vexjoy-agent
Other agents on vexjoy-agent.
- ansible-automation-engineer
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
Open agent - modules
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) **Generated**: 2026-04-04 — verify against current Ansible
Open agent - testing
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
Open agent - base-instructions
Universal operational rules injected by /do at agent dispatch. Domain-specific rules live in each agent's .md file.
Open agent - communication-patterns
**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what to detect and how to fix each. **Version range**: all versions **Generated**: 2026-05-11
Open agent - combat-effects-upgrade
Zero-dependency combat visual upgrades: CSS particle replacement, Framer Motion combat juice, CSS 3D card transforms.
Open agent

