ansible-automation-eng…
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
<!-- Loaded by combat-effects-upgrade when task involves card tilt, backface-visibility, CSS perspective, or Framer Motion + CSS 3D integration -->
$ npx -y skills add notque/vexjoy-agent --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
Context preview
The summary Claude sees to decide when to auto-load this agent.
<!-- Loaded by combat-effects-upgrade when task involves card tilt, backface-visibility, CSS perspective, or Framer Motion + CSS 3D integration -->
<!-- Loaded by combat-effects-upgrade when task involves card tilt, backface-visibility, CSS perspective, or Framer Motion + CSS 3D integration -->
CSS 3D transforms: tilt toward cursor on hover, `backface-visibility: hidden` for flip reveals. GPU-composited only (`transform`, `opacity`). Universal browser support (Chrome 36+, Firefox 16+, Safari 9+).
rotateY = (mouseX - cardCenterX) / cardWidth * MAX_TILT_DEG rotateX = -(mouseY - cardCenterY) / cardHeight * MAX_TILT_DEG
---
// src/components/FramedCard.tsx
import { motion, useMotionValue, useSpring, useTransform } from 'motion/react';
import { useRef, useCallback, useState } from 'react';
const MAX_TILT_DEG = 15;
const SPRING_CONFIG = { stiffness: 300, damping: 30 } as const;
interface FramedCardProps {
card: Card;
onClick?: () => void;
}
export function FramedCard({ card, onClick }: FramedCardProps) {
const cardRef = useRef<HTMLDivElement>(null);
const [isTouchDevice] = useState(
() => window.matchMedia('(hover: none)').matches
);
// Raw motion values — these update on every mousemove WITHOUT triggering re-render
const rawRotateX = useMotionValue(0);
const rawRotateY = useMotionValue(0);
// Springs smooth the raw values — tilt follows cursor with a slight lag
const rotateX = useSpring(rawRotateX, SPRING_CONFIG);
const rotateY = useSpring(rawRotateY, SPRING_CONFIG);
// Subtle glare effect: opacity follows rotateY
const glareOpacity = useTransform(rotateY, [-MAX_TILT_DEG, 0, MAX_TILT_DEG], [0.15, 0, 0.15]);
const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
if (isTouchDevice || !cardRef.current) return;
const rect = cardRef.current.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
rawRotateY.set((e.clientX - centerX) / (rect.width / 2) * MAX_TILT_DEG);
rawRotateX.set(-((e.clientY - centerY) / (rect.height / 2)) * MAX_TILT_DEG);
}, [isTouchDevice, rawRotateX, rawRotateY]);
const handleMouseLeave = useCallback(() => {
// Spring back to flat — spring physics handles the animation
rawRotateX.set(0);
rawRotateY.set(0);
}, [rawRotateX, rawRotateY]);
return (
// Perspective on the container — children share one vanishing point
<div className="card-perspective-container" ref={cardRef}>
<motion.div
className="framed-card"
style={{
rotateX, // MotionValue — no re-renders, compositor-only updates
rotateY,
transformStyle: 'preserve-3d',
}}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
onClick={onClick}
// Existing hover scale
whileHover={isTouchDevice ? undefined : { scale: 1.06 }}
whileTap={{ scale: 0.97 }}
transition={{ type: 'spring', stiffness: 400, damping: 28 }}
>
{/* Card face */}
<div className="card-face card-face--front">
<CardArtwork card={card} />
<CardStats card={card} />
</div>
{/* Glare overlay — follows tilt direction */}
<motion.div
className="card-glare"
style={{ opacity: glareOpacity }}
aria-hidden="true"
/>
</motion.div>
</div>
);
}---
/* ─────────────────────────────────────────────
Card perspective container
perspective goes on the PARENT — gives children
a shared vanishing point
───────────────────────────────────────────── */
.card-perspective-container {
perspective: 1000px;
perspective-origin: center center;
/* Contain so tilt doesn't affect sibling layout */
isolation: isolate;
}
/* ─────────────────────────────────────────────
Card body
───────────────────────────────────────────── */
.framed-card {
position: relative;
width: 160px;
height: 240px;
border-radius: 12px;
transform-style: preserve-3d;
cursor: pointer;
/* GPU layer hint — apply dynamically via JS, or only in hover state */
}
.framed-card:hover {
will-change: transform;
}
/* Remove will-change when not hovering */
.framed-card:not(:hover) {
will-change: auto;
}
/* ─────────────────────────────────────────────
Card faces — used for flip reveal
───────────────────────────────────────────── */
.card-face {
position: absolute;
inset: 0;
border-radius: 12px;
backface-visibility: hidden; /* Hide when rotated 180deg */
-webkit-backface-visibility: hidden; /* Safari */
}
.card-face--front {
/* Front is naturally visible (0deg rotation) */
}
.card-face--back {
/* Back starts flipped — visible when card rotates 180deg */
transform: rotateY(180deg);
background: url('/card-back.png') center/cover;
}
/* ─────────────────────────────────────────────
Glare overlay
───────────────────────────────────────────── */
.card-glare {
position: absolute;
inset: 0;
border-radius: 12px;
background: linear-gradient(
105deg,
transparent 40%,
rgba(255, 255, 255, 0.15) 50%,
transparent 60%
);
pointer-events: none;
/* translateZ pushes it above the card face in 3D space */
transform: translateZ(1px);
}---
Used when a face-down card is revealed (e.g. drawing from deck, enemy showing intent).
// src/components/FramedCard.tsx — flip variant
import { motion, AnimatePresence } from 'motion/react';
import { useState } from 'react';
interface FlippableCardProps {
card: Card;
isRevealed: boolean;
}
export function FlippableCard({ card, isRevealedEssays and writing behind this toolkit live at vexjoy.com. VexJoy Agent connects plain-English requests to specialist agents, skills, and workflows. /do selects the knowledge and tools needed for your task.
Repo: notque/vexjoy-agent
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**:…
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ /…
Universal rules injected by /do at dispatch. Each agent's .md file supplies domain rules.
**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what to detect and how to fix…
Zero-dependency combat visual upgrades: CSS particle replacement, Framer Motion combat juice, CSS 3D card transforms.