feat: Complete fleet — 94 skills across 10+ domains
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>
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
---
|
||||
title: Prefer Animating Transform and Opacity for Performance
|
||||
impact: MEDIUM
|
||||
impactDescription: Animating layout-triggering properties like height or margin causes expensive reflows and janky animations
|
||||
type: best-practice
|
||||
tags: [vue3, transition, animation, performance, css, transform, opacity]
|
||||
---
|
||||
|
||||
# Prefer Animating Transform and Opacity for Performance
|
||||
|
||||
**Impact: MEDIUM** - When creating Vue transitions, you should prefer animating `transform` and `opacity` properties. These are GPU-accelerated and do not trigger expensive CSS layout recalculations. Animating properties like `height`, `width`, `margin`, `padding`, or `top`/`left` causes the browser to recalculate layout on every frame, resulting in janky, low-performance animations.
|
||||
|
||||
## Task Checklist
|
||||
|
||||
- [ ] Use `transform` for position and size animations (translate, scale, rotate)
|
||||
- [ ] Use `opacity` for fade effects
|
||||
- [ ] Avoid animating `height`, `width`, `margin`, `padding`, `top`, `left`, `right`, `bottom`
|
||||
- [ ] If you must animate height, consider using `max-height` with a generous value or JavaScript-based solutions
|
||||
- [ ] Use `will-change` sparingly and only when needed
|
||||
|
||||
**Problematic Code:**
|
||||
```css
|
||||
/* BAD: Animating height triggers layout recalculation every frame */
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-enter-from,
|
||||
.slide-leave-to {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slide-enter-to,
|
||||
.slide-leave-from {
|
||||
height: 200px;
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
/* BAD: Animating margin causes layout thrashing */
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: margin-top 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-enter-from,
|
||||
.slide-leave-to {
|
||||
margin-top: -100%;
|
||||
}
|
||||
```
|
||||
|
||||
**Correct Code:**
|
||||
```css
|
||||
/* GOOD: Using transform and opacity - GPU accelerated */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
/* GOOD: Using transform for slide animations */
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-enter-from {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-leave-to {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
/* GOOD: Using scale instead of width/height */
|
||||
.scale-enter-active,
|
||||
.scale-leave-active {
|
||||
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.scale-enter-from,
|
||||
.scale-leave-to {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
```
|
||||
|
||||
## When You Must Animate Height
|
||||
|
||||
If you absolutely need to animate height (e.g., accordion), consider these alternatives:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- Option 1: max-height trick (simple but not precise) -->
|
||||
<Transition name="expand">
|
||||
<div v-if="expanded" class="content">
|
||||
<slot />
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.expand-enter-active,
|
||||
.expand-leave-active {
|
||||
transition: max-height 0.3s ease, opacity 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.expand-enter-from,
|
||||
.expand-leave-to {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.expand-enter-to,
|
||||
.expand-leave-from {
|
||||
max-height: 500px; /* Set to expected max content height */
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
// Option 2: JavaScript hooks for precise height animation
|
||||
function onEnter(el, done) {
|
||||
el.style.height = '0'
|
||||
el.style.overflow = 'hidden'
|
||||
// Force reflow
|
||||
el.offsetHeight
|
||||
el.style.transition = 'height 0.3s ease'
|
||||
el.style.height = el.scrollHeight + 'px'
|
||||
|
||||
el.addEventListener('transitionend', () => {
|
||||
el.style.height = ''
|
||||
el.style.overflow = ''
|
||||
done()
|
||||
}, { once: true })
|
||||
}
|
||||
|
||||
function onLeave(el, done) {
|
||||
el.style.height = el.scrollHeight + 'px'
|
||||
el.style.overflow = 'hidden'
|
||||
el.offsetHeight
|
||||
el.style.transition = 'height 0.3s ease'
|
||||
el.style.height = '0'
|
||||
|
||||
el.addEventListener('transitionend', done, { once: true })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition @enter="onEnter" @leave="onLeave">
|
||||
<div v-if="expanded">
|
||||
<slot />
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Performance Comparison
|
||||
|
||||
| Property | Layout | Paint | Composite | Performance |
|
||||
|----------|--------|-------|-----------|-------------|
|
||||
| `transform` | No | No | Yes | Excellent |
|
||||
| `opacity` | No | No | Yes | Excellent |
|
||||
| `background-color` | No | Yes | Yes | Good |
|
||||
| `width`/`height` | Yes | Yes | Yes | Poor |
|
||||
| `margin`/`padding` | Yes | Yes | Yes | Poor |
|
||||
| `top`/`left` | Yes | Yes | Yes | Poor |
|
||||
|
||||
## Reference
|
||||
- [Vue.js Transition Documentation](https://vuejs.org/guide/built-ins/transition.html)
|
||||
- [CSS Triggers](https://csstriggers.com/) - Reference for which properties trigger layout/paint
|
||||
- [High Performance Animations](https://web.dev/animations-guide/)
|
||||
Reference in New Issue
Block a user