apply-transitions
85 linesFinds state changes, conditional renders and hover states that happen with no transition, classifies each by interaction type, and applies the matching duration and easing token. Use as a phase 5 pass of design-system, or when an app changes state abruptly.
Apply Transitions
Find UI changes that happen instantly and apply the motion tokens to them.
Prerequisite
grep -A6 '^## Motion' DESIGN.md || echo "MISSING"
Stop if the motion section is missing. Applying durations without tokens produces the inconsistency this pass exists to remove.
Step 1: Find changes with no transition
You are searching for the absence of a property, so grep for the construct and then check each result.
Conditional renders:
# Svelte
grep -rn '{#if' src --include="*.svelte" | head -40
# React and JSX
grep -rn -E '\{\s*\w+\s*&&\s*<|\{\s*\w+\s*\?\s*<' src --include="*.tsx" --include="*.jsx" | head -40
# Vue
grep -rn 'v-if=' src --include="*.vue" | head -40
For each, check whether the element has transition:, <AnimatePresence> or <Transition>. Record the ones that do not. Modals, dropdowns, toasts, tooltips, accordions, tab panels, error messages and loading states are the common cases.
Interactive elements:
grep -rn -E 'hover:|focus:|active:|aria-expanded|data-state' src --include="*.svelte" --include="*.tsx" | grep -v 'transition' | head -40
A hover:bg-* with no transition-colors changes instantly.
Dynamic inline styles:
grep -rn -E 'style=.*(height|width|opacity|transform).*\$?\{' src --include="*.svelte" --include="*.tsx" | head -20
Step 2: Classify, then assign
Assign a duration by interaction class, not per element.
| Class | Token | Applies to |
|---|---|---|
| Micro | duration-150 |
hover, focus, press, checkbox, toggle |
| Standard | duration-250 |
dropdown, tooltip, popover, accordion, tab |
| Large | duration-400 |
modal, drawer, route change, full-page |
Take the easing pair from DESIGN.md. Enter uses the ease-out curve, exit uses ease-in.
Step 3: Constraints
Animate transform and opacity only. Animating width, height, top or margin triggers layout on every frame. For a height transition, animate a grid-template-rows value or a scale transform instead.
Add reduced-motion handling with every change:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Keep opacity transitions and remove movement. Translation and scale are the vestibular triggers; fades are not.
Do not add entrance animations to elements that are present on load. Animate elements when they appear or change, not when the page renders.
Step 4: Report
Per file: the construct found, the class assigned, the token applied. Then the count by class.
List anything you left without a transition and why. Some state changes should be instant.