Animate a change between states
A transition interpolates an animatable property when its computed value changes. Specify the properties deliberately rather than using transition:all, which can accidentally animate later layout changes.
.button {
background: #1d4ed8;
color: white;
transform: translateY(0);
transition: background-color 160ms ease, transform 160ms ease;
}
.button:hover { background: #1e40af; transform: translateY(-2px); }
.button:focus-visible { outline: 3px solid #f59e0b; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
.button { transition: none; }
}Hover changes the background and lifts the button slightly. The focus outline remains immediate and visible. Reduced-motion users receive the state change without the animated movement.
Duration, timing and delay
Duration is how long interpolation takes. The timing function controls progress through that duration. Delay postpones the start and can make essential feedback feel unresponsive if overused. In a shorthand, the first time is duration and the second time, if supplied, is delay.
Property support matters
Not every pair of values interpolates the way a number does. Historically common examples such as height:0 to height:auto need special handling or newer features with support checks; do not assume a basic height transition will always work. Use a design that remains usable if the animation does not run.
Performance and layout
Transform and opacity often avoid layout work, but are not guarantees of zero cost or GPU acceleration in every situation. Animating width, height or position offsets can require repeated layout and paint. Profile a real interaction before claiming it is optimized.
Practice
- Compare linear and ease timing at the same duration.
- Add a long delay and observe the usability cost, then remove it.
- Emulate reduced motion in developer tools and confirm movement stops.
- Tab to the button and verify the focus indicator does not depend on hover.