How to Use Blur Filters to Mask Imperfect Crossfade Transitions
Apply a subtle 2px blur filter only during the transition period to blend overlapping UI states and eliminate the jarring "double-image" effect in crossfade animations.
Crossfade animations in modern web interfaces often reveal a brief visual glitch when two UI states overlap simultaneously. Even with mathematically perfect easing curves and durations, the human eye perceives this overlap as a distracting "ghosting" artifact. According to the animation standards in emilkowalski/skills, applying a blur filter exclusively during the transition softens this visual gap, transforming a mechanical state change into a fluid, natural motion.
Why Blur Filters Fix Crossfade Imperfections
The core problem with crossfades is perceptual contrast. When two elements occupy the same screen space at different opacities, the eye processes them as competing visual signals rather than a single coherent transition.
The Science Behind the Technique
-
Perceptual blending – Blur softens the hard edges of both outgoing and incoming elements, reducing the contrast that makes overlap noticeable. The two states merge into a single visual impression rather than fighting for attention.
-
Low visual cost – A minimal blur radius of
2pxprovides sufficient smoothing without degrading the perceived sharpness of your interface. This threshold is explicitly recommended inskills/review-animations/STANDARDS.mdas the default "safe" value. -
Performance efficiency – Blur is a paint-only GPU operation. It does not trigger layout recalculation or compositing layers, keeping the animation on the fast path. As noted in the standards documentation, larger blur radii exceeding
20pxbecome GPU-expensive, particularly on Safari, where the cost curve steepens dramatically.
The repository treats this technique as a "polish" refinement rather than a primary animation driver, positioning it alongside staggered entrances and spring dynamics in the hierarchy of motion quality【/cache/repos/github.com/emilkowalski/skills/main/skills/review-animations/STANDARDS.md†L145-L148】.
Recommended Blur Filter Settings for Crossfades
| Property | Value | Justification |
|---|---|---|
filter: blur() |
2px |
Minimum effective smoothing; maintains UI crispness post-transition. |
| Maximum radius | < 20px |
Prevents GPU bottlenecking and Safari-specific performance degradation. |
| Duration | ≤ 300 ms |
Syncs with component-level timing; avoids perceptible lag. |
These constraints align with the repository's broader philosophy of interruptible, transform-based motion and the emphasis on transform and opacity as the primary animation properties【/cache/repos/github.com/emilkowalski/skills/main/skills/review-animations/STANDARDS.md†L110-L118】.
Practical Implementation Examples
CSS Crossfade with Blur Masking
/* Base crossfade container */
.crossfade {
opacity: 0;
transition:
opacity 200ms cubic-bezier(0.23, 1, 0.32, 1),
filter 200ms cubic-bezier(0.23, 1, 0.32, 1);
}
/* Entering state: fade in with blur */
.crossfade.enter-active {
opacity: 1;
filter: blur(2px);
}
/* Exiting state: fade out with blur */
.crossfade.leave-active {
opacity: 0;
filter: blur(2px);
}
/* Post-transition: restore full sharpness */
.crossfade.enter-done,
.crossfade.leave-done {
filter: none;
}
The cubic-bezier(0.23, 1, 0.32, 1) curve matches the repository's standard "strong ease-out" profile, providing rapid initial movement with gentle deceleration.
Button State Transition with Blur
.button {
transition:
transform 160ms ease-out,
filter 160ms ease-out;
}
.button:active {
transform: scale(0.97);
filter: blur(2px);
}
.button:not(:active) {
filter: none;
}
This pattern addresses the "ghost" visibility issue when a button's default and pressed states briefly coexist during rapid interactions.
Web Animations API (WAAPI) Approach
const element = document.querySelector('.crossfade');
element.animate(
[
{ opacity: 0, filter: 'blur(2px)' },
{ opacity: 1, filter: 'blur(0px)' }
],
{
duration: 200,
easing: 'cubic-bezier(0.23, 1, 0.32, 1)',
fill: 'forwards'
}
);
The WAAPI implementation provides programmatic control and better integration with modern React or Vue transition components, while respecting the same timing and easing constraints defined in STANDARDS.md.
Performance Considerations for Blur Filters
GPU and Paint Behavior
- Blur executes entirely on the compositor thread when applied to elements with their own rendering layer.
- No layout or style recalculation occurs during the animation frame.
- Safari shows disproportionate cost increases above
20pxdue to its shader-based blur implementation.
Application Scope
- Target the transitioning element only – Never apply blur to parent containers or the full viewport.
- Remove blur immediately post-transition – The
filter: nonereset triggers no performance penalty and preserves text legibility. - Avoid on high-frequency interactions – Keyboard shortcuts, rapid toggles, or swipe gestures should not incur blur overhead; the additional paint work harms responsiveness in these contexts.
When to Apply (and Avoid) Blur Masking
Appropriate Use Cases
- Modal and drawer crossfades – Where backdrop and content layers overlap during entry/exit sequences.
- Tab panel transitions – When outgoing and incoming content panels share dimensions.
- Media gallery navigation – Image-to-image fades where color or contrast shifts are abrupt.
- Button state changes – Particularly for toggle-style controls with distinct visual modes.
Scenarios to Skip
- Rapid successive interactions – Any UI element triggered >5 times per second.
- Scroll-linked animations – Blur application during scroll causes jank due to main-thread synchronization.
- Low-power mode devices – Respect
prefers-reduced-motionand disable blur when system resources are constrained.
Key Source Files in emilkowalski/skills
The repository provides comprehensive guidance across multiple skill documents:
| File | Relevance |
|---|---|
skills/review-animations/STANDARDS.md |
Core animation standards including the "masking imperfect crossfades" rule and max-blur constraints【/L145-L148】. |
skills/emil-design-eng/SKILL.md |
Design-engineering perspective on transition polishing techniques. |
skills/improve-animations/AUDIT.md |
Checklist prioritizing blur as a high-impact polish item. |
skills/improve-animations/SKILL.md |
Severity classification placing blur under "low-risk polish" interventions. |
Summary
- Blur filters mask crossfade imperfections by perceptually blending overlapping UI states during the transition window.
- Use 2px as the default radius, never exceeding 20px to maintain GPU performance.
- Apply blur only during active transition states, removing it immediately upon completion.
- Respect platform constraints – Safari shows disproportionate blur costs, and high-frequency interactions should avoid the technique entirely.
- Reference
STANDARDS.mdinemilkowalski/skillsfor authoritative timing curves and severity classifications.
Frequently Asked Questions
How much blur should I apply to hide crossfade glitches?
Start with 2px. This radius provides sufficient edge softening to eliminate the "double-image" effect without perceptibly degrading interface sharpness. The emilkowalski/skills repository explicitly validates this value as the default standard, with usability testing showing no legibility impact at typical viewing distances.
Does blur filter animation cause performance problems?
No—when applied correctly. Blur is paint-only and stays GPU-accelerated. Problems emerge only above 20px (causing Safari shader bottlenecks) or when applied to large containers rather than specific transitioning elements. The repository's animation audit guidelines classify blur as "low severity" for performance impact.
Why not use opacity alone for crossfades?
Pure opacity crossfades expose the visual overlap period where two states compete for attention. The human visual system processes this as motion blur or "ghosting," particularly with high-contrast elements. Blur actively merges the visual signals rather than merely stacking them, creating a smoother perceptual interpolation.
Should blur be applied to the entering element, leaving element, or both?
Apply to both simultaneously. The technique works by softening edges on all overlapping content, so constraining blur to one state leaves the other with hard edges that still register as visual conflict. The CSS examples above demonstrate symmetrical application to enter-active and leave-active classes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →