Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.2 KiB
5.2 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | |||||
|---|---|---|---|---|---|---|---|---|---|
| Use Class-based Animations for Non-Enter/Leave Effects | LOW | Class-based animations are simpler and more performant for elements that remain in the DOM | best-practice |
|
Use Class-based Animations for Non-Enter/Leave Effects
Impact: LOW - For animations on elements that are not entering or leaving the DOM, use CSS class-based animations triggered by Vue's reactive state. This is simpler than <Transition> and more appropriate for feedback animations like shake, pulse, or highlight effects.
Task Checklist
- Use class-based animations for elements staying in the DOM
- Use
<Transition>only for enter/leave animations - Combine CSS animations with Vue's class bindings (
:class) - Consider using
setTimeoutto auto-remove animation classes
When to Use Class-based Animations:
- User feedback (shake on error, pulse on success)
- Attention-grabbing effects (highlight changes)
- Hover/focus states that need more than CSS transitions
- Any animation where the element stays mounted
When to Use Transition Component:
- Elements entering/leaving the DOM (v-if/v-show)
- Route transitions
- List item additions/removals
Basic Pattern
<template>
<div :class="{ shake: showError }">
<button @click="submitForm">Submit</button>
<span v-if="showError">This feature is disabled!</span>
</div>
</template>
<script setup>
import { ref } from 'vue'
const showError = ref(false)
function submitForm() {
if (!isValid()) {
// Trigger shake animation
showError.value = true
// Auto-remove class after animation completes
setTimeout(() => {
showError.value = false
}, 820) // Match animation duration
}
}
</script>
<style>
.shake {
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
transform: translate3d(0, 0, 0); /* Enable GPU acceleration */
}
@keyframes shake {
10%, 90% { transform: translate3d(-1px, 0, 0); }
20%, 80% { transform: translate3d(2px, 0, 0); }
30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }
40%, 60% { transform: translate3d(4px, 0, 0); }
}
</style>
Common Animation Patterns
Pulse on Success
<template>
<button
@click="save"
:class="{ pulse: saved }"
>
{{ saved ? 'Saved!' : 'Save' }}
</button>
</template>
<script setup>
import { ref } from 'vue'
const saved = ref(false)
async function save() {
await saveData()
saved.value = true
setTimeout(() => saved.value = false, 1000)
}
</script>
<style>
.pulse {
animation: pulse 0.5s ease-in-out;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
</style>
Highlight on Change
<template>
<div
:class="{ highlight: justUpdated }"
>
Value: {{ value }}
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const value = ref(0)
const justUpdated = ref(false)
watch(value, () => {
justUpdated.value = true
setTimeout(() => justUpdated.value = false, 1000)
})
</script>
<style>
.highlight {
animation: highlight 1s ease-out;
}
@keyframes highlight {
0% { background-color: yellow; }
100% { background-color: transparent; }
}
</style>
Bounce Attention
<template>
<div
:class="{ bounce: needsAttention }"
@animationend="needsAttention = false"
>
<BellIcon />
</div>
</template>
<script setup>
import { ref } from 'vue'
const needsAttention = ref(false)
function notifyUser() {
needsAttention.value = true
// No setTimeout needed - using animationend event
}
</script>
<style>
.bounce {
animation: bounce 0.5s ease;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
</style>
Using animationend Event
Instead of setTimeout, use the animationend event for cleaner code:
<template>
<div
:class="{ animate: isAnimating }"
@animationend="isAnimating = false"
>
Content
</div>
</template>
<script setup>
import { ref } from 'vue'
const isAnimating = ref(false)
function triggerAnimation() {
isAnimating.value = true
// Class is automatically removed when animation ends
}
</script>
Composable for Reusable Animations
// composables/useAnimation.js
import { ref } from 'vue'
export function useAnimation(duration = 500) {
const isAnimating = ref(false)
function trigger() {
isAnimating.value = true
setTimeout(() => {
isAnimating.value = false
}, duration)
}
return {
isAnimating,
trigger
}
}
<script setup>
import { useAnimation } from '@/composables/useAnimation'
const shake = useAnimation(820)
const pulse = useAnimation(500)
</script>
<template>
<button
:class="{ shake: shake.isAnimating.value }"
@click="shake.trigger()"
>
Shake me
</button>
<button
:class="{ pulse: pulse.isAnimating.value }"
@click="pulse.trigger()"
>
Pulse me
</button>
</template>