Design Engineering Details

Apr 14, 2026

In a world where everyone's software is functional, I've realized that taste and craft are the ultimate differentiators. But what makes an interface feel right? It is rarely a single, loud feature. Instead, it is the compounding effect of a thousand barely audible details singing in tune.

Although I'm not a design engineer, I love design engineering and have been actively learning it. In this post, I want to share the core principles I follow when crafting premium interfaces, heavily inspired by the engineering philosophies of Emil Kowalski and his masterclass animations.dev.

Design Engineering: balancing visual aesthetics, spring physics, and hardware-accelerated code performance.
Design Engineering: balancing visual aesthetics, spring physics, and hardware-accelerated code performance.

The Animation Decision Framework

Before writing a single line of animation code, the first question I always ask is: Should this animate at all?

We often overestimate the value of animations for high-frequency actions. If a user triggers a command palette or keyboard shortcut hundreds of times a day, any transition is a bottleneck. It adds delay, interrupts flow, and detaches the interface from the user's instantaneous command. That's why tools like Raycast have no opening transition—speed is their utility.

The same logic applies to keyboard-initiated navigation. If you are navigating a list via arrow keys, a sliding background highlight animation will feel sluggish and lag behind your keypresses. Removing the animation entirely connects the interface directly to your input.

Keyboard Navigation
LinearApplication
ChatGPT
Cursor
Figma
Obsidian
Select items using arrow keys or click them. Try with/without highlight animation.

But sometimes the purpose of an animation might just be to bring delight.

For instance, the morphing feedback component below helps make the experience more unique and memorable. I find this works well as long as the user will rarely interact with it. It then becomes a pleasant surprise, rather than a daily annoyance.

Feedback received!

Thanks for helping me improve.

Press on the button to see it morph.

If you had to interact with this component multiple times a day, it would quickly become irritating. The initial delight would fade and the animation would slow you down.

How often users will see an animation is a key factor in deciding whether to animate or not. Let's dive deeper into it next.

For actions occurring occasionally like modals opening, toasts sliding in, or dropdown menus expanding, transitions help bridge the spatial gap, preventing jarring layouts. When I build animations, I stick to a few main ideas.

First, I never use ease-in curves. An ease-in curve starts too slow, which introduces a perceived lag right at the moment the user is watching most closely. Instead, I use ease-out (decelerating) for enter states to make them feel highly responsive, and ease-in-out for moving elements around.

Second, I design custom curves because built-in CSS easings lack punch. I like to define strong, distinct curves for different components:

/* Strong ease-out for instant UI response */
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);

/* iOS-like drawer ease (from Ionic/Vaul) */
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
My custom CSS easing curves for snappy and elegant transitions.

Third, I keep durations short. Most UI transitions should live between 150ms and 250ms. A 180ms select dropdown feels snappy and responsive, whereas a 400ms dropdown feels noticeably heavy.

Snappy (180ms)
Edit
Duplicate
Delete
Slow (400ms)
Edit
Duplicate
Delete
Compare drop-down opening speed: 180ms vs 400ms.

To compare the acceleration styles, we can also look at how different curves behave. Notice how the custom cubic-bezier curve snaps forward immediately compared to standard ease-in:

ease-in
ease-out
custom
Press play to compare the easing functions.

Physics-Based Motion

Traditional duration-based animations are rigid: if you click a drawer closed and immediately pull it back open, the timeline resets, creating a visual jump. I prefer springs because they simulate physical forces—mass, stiffness, and damping. They have no fixed duration, allowing them to adapt dynamically.

Spring animations are particularly suited for gestural dismissal (like swiping away a bottom drawer) because they allow interruptibility. If the user changes their mind mid-drag, the spring absorbs the velocity change and transitions smoothly.

import { useSpring } from 'framer-motion';

// Spring values maintain physical velocity when interrupted
const springRotation = useSpring(mouseX * 0.1, {
    stiffness: 100,
    damping: 15,
    mass: 0.8
});
Implementing interruptible spring animation in React.

When I design gestures, I calculate speed to enable momentum-based dismissal rather than just tracking distance thresholds. A rapid swipe should dismiss the element even if it hasn't crossed the center line.

Press the button repeatedly mid-flight to test spring interruptibility.

Component Polish Checklist

Over time, I've compiled a few subtle UI details that I always check when polishing my work. They make a huge difference in how responsive and premium an app feels.

First, press states should feel instant and tactile. Adding a subtle scale down like scale(0.97) on click makes buttons feel physical—almost like you are actually pushing them.

Second, I avoid scaling elements down to zero when closing them. Real-world objects don't emerge or disappear into a single dot. Instead, I start transitions from scale(0.95) and crossfade using opacity.

Third, popovers should scale out from the trigger button that opened them, rather than the middle of the screen. Only full-screen modals should expand from the center of the viewport.

Fourth, tooltips should have a slight delay before showing up so they don't block the screen when scanning around. But once a tooltip is active, hovering over adjacent icons should show theirs instantly with zero animation delay.

No scale
Scale (0.97)
Press on the buttons to feel the difference.

Perception of Speed

Unless I'm working on a marketing site, my animations must be fast. They improve the perceived performance of the app, stay aligned with user actions, and make the interface feel responsive.

For example, a faster-spinning loading indicator makes the application feel like it is loading faster, even if the actual loading duration remains identical. This is a classic trick I use to improve perceived performance.

Slow (2.0s loop)
Fast (0.8s loop)
Which spinner feels like it's working harder to load your data?

Leveraging Blur for Soft Transitions

When crossfading elements—such as text changing state on a button or an active tab indicator moving—a direct opacity transition can look muddy because both text strings are visible at 50% opacity simultaneously. To bypass this, I add a subtle blur during the crossfade.

/* Apply blur to mask overlapping intermediate states */
.transitioning-content {
    transition: filter 180ms ease, opacity 180ms ease;
}

.transitioning-content.active {
    filter: blur(2px);
    opacity: 0.7;
}
Applying temporary blur filters to clean up state swaps.

The eye perceives a single, continuous transformation rather than two separate elements colliding.

Submit Feedback
✓ Feedback Received!
Trigger the transition to observe the blur crossfade.

Performant Implementation

Of course, polish doesn't mean much if the interface lags. When I'm coding animations, I stick to a few simple optimization habits to keep the execution at 60fps.

I try to animate only transform and opacity whenever possible, since these run entirely on the GPU, bypassing heavy browser style recalculation and paint passes.

I also watch out for updating CSS variables dynamically (like drag coordinates) on parent containers. Doing that forces the browser to re-evaluate styles for all child nodes. It's much faster to directly write inline style transforms with JS.

For Framer Motion, shorthand properties like x and y usually run on the main thread. When responsiveness is critical, I force GPU acceleration by animating the full raw transform string, like translateX(100px) directly.

The Takeaway

I always review my animations the next day with fresh eyes. Playing them in slow motion to inspect intermediate states is incredibly helpful. Excellent design engineering isn't about adding decorative flair. It is about eliminating friction, honoring physics, and building components that feel natural to touch.