Individual Transform Properties vs the transform Shorthand
Part of Motion Path & Individual Transforms in Core CSS Animation Fundamentals.
The problem
A card has an entrance animation that lifts it into place, and a hover state that lifts it a little further. Both are expressed as transform, so the second one erases the first: hovering during the entrance snaps the card to the hover offset, and leaving the hover snaps it back to nothing rather than to the entrance position.
The usual workaround is structural — wrap the card in a div, let the outer element own the entrance and the inner one own the hover — and it works, at the cost of a DOM node per effect and a component that is harder to lay out. The individual transform properties remove the conflict at the source.
Root cause: one property, one value
transform holds an ordered list of functions, and CSS has no mechanism for two rules to each contribute part of a list. The cascade picks a winner for the whole declaration. Two rules that both want to translate the element are not merged; the more specific one wins outright, and the other’s contribution disappears.
This is also why transform function order is such a common source of confusion. translateX(40px) scale(2) moves 40 px and then doubles; scale(2) translateX(40px) doubles and then moves 80 px in the scaled coordinate space. When two authors each write half the list, the result depends on which of them wrote it — which is not a stable foundation.
The longhands make each function its own property with its own cascade resolution and its own interpolation. Composition order is fixed by the specification — translate, rotate, scale, then transform — so two rules can safely own different slots without coordinating. That is the entire benefit, and it is a large one.
Decision matrix
Production code pattern
/* Intent: three independent effects on one card, no wrapper elements,
each of them separately gateable for reduced motion. */
.card {
/* Slot 1 — owned by the reveal state. */
translate: 0 14px;
/* Slot 2 — owned by pointer feedback. */
scale: 1;
/* Slot 3 — owned by the drag handle only. */
rotate: 0deg;
opacity: 0;
transition:
translate 320ms cubic-bezier(0, 0, 0.2, 1),
scale 150ms cubic-bezier(0.2, 0, 0, 1),
rotate 150ms linear,
opacity 320ms linear;
}
.card.is-revealed { translate: 0 0; opacity: 1; }
.card:hover { scale: 1.03; } /* leaves translate alone */
.card.is-dragging { rotate: 1.5deg; } /* leaves both others alone */
/* Accessibility gate: remove the travel, keep the feedback that tells a
user their pointer is over the control. */
@media (prefers-reduced-motion: reduce) {
.card {
transition: opacity 120ms linear, scale 100ms linear;
translate: 0 0; /* no rise */
rotate: 0deg; /* no tilt */
}
.card:hover { scale: 1.01; } /* smaller, but still present */
}
Rendering Impact: composite only. Three transitions on three properties still produce one matrix per frame; the compositor cost is identical to a single
transformtransition, and no wrapper element is laid out.
The reduced-motion block is where the split pays off most visibly. With a single transform list, removing the rise means restating the whole list without it — including the hover scale, which then has to be duplicated. With separate slots, the gate neutralises exactly the property that travels and leaves the rest of the component alone.
Verification checklist
Constraints and trade-offs
- There are no longhands for
skew(),matrix(),perspective()or the 3D functions; those stay in the shorthand. transform-originapplies to the combined result, so two effects that need different origins still need different boxes.- A
transform: nonedeclaration does not reset the longhands, and atranslate: nonedoes not reset the shorthand — resetting an element fully means naming all four. transition: allnow covers four properties instead of one, which makes an accidental transition on an unrelated change more likely; name the properties explicitly.- Very old engines ignore the longhands entirely and render the element untransformed, so author the resting position as the base style rather than as the animated value.
Frequently asked questions
In what order are the individual properties applied?
Always translate, then rotate, then scale, and then whatever the transform shorthand declares. The order is fixed by the specification rather than by declaration order, which is precisely what makes the properties safe for two independent authors to use on the same element.
Do the longhands override the transform shorthand?
No, they compose with it. If an element has both, the browser builds the matrix from the longhands first and then appends the shorthand’s function list. What looks like an override is normally the same offset being expressed twice.
Is there a performance difference between the two forms?
No. Both resolve to a single transformation matrix per frame and both animate on the compositor. Two transitions on two longhands cost the same as one transition on the shorthand.
Interaction with keyframe animations
Everything above concerns transitions, where each property resolves independently through the cascade. Keyframe animations add one wrinkle worth knowing before a refactor.
A @keyframes rule can animate the longhands exactly as it animates the shorthand, and the composition order is unchanged — translate, rotate, scale, then transform. What changes is the interaction between two animations on the same element. Two keyframe animations that both write transform contend for one property and resolve by animation order; two that write different longhands do not contend at all, which removes most of the cases where animation composition used to be necessary.
That has a practical consequence for entrance effects. An entrance animation that writes translate and an idle loop that writes rotate can run simultaneously with no coordination, no animation-composition: add, and no risk that a browser without composition support renders only one of them. Composition is still the answer when two effects genuinely need to contribute to the same function — two independent translations, say — but that turns out to be rarer than it looks.
The one caveat is transform-origin, which applies to the combined matrix rather than per property. An entrance that rotates about the top-left and a hover that scales about the centre still need two boxes, because there is only one origin to go around.
Related
- CSS Motion Path & Individual Transform Properties — the parent guide, including how the offset family composes with these slots
- CSS Animation Composition & Layering — the other answer to two effects on one property, for cases the slots cannot separate
- Hardware-Accelerated Properties — why every member of the transform family stays on the compositor