An off-canvas menu animated with left stutters on a phone and the same menu animated with transform does not. The usual explanation — “it uses the GPU” — is close enough to be useless. The real difference is how much of the rendering pipeline each property forces the browser to re-run, sixty times a second.
/* every frame: layout, paint, composite */
.drawer {
left: -280px;
transition: left 200ms ease-out;
}
.drawer.is-open { left: 0; }
/* every frame: composite only */
.drawer {
transform: translate3d(-280px, 0, 0);
transition: transform 200ms ease-out;
}
.drawer.is-open { transform: translate3d(0, 0, 0); }
Changing left changes geometry, so the browser re-runs layout for the subtree and repaints it before compositing. transform and opacity are applied by the compositor to a layer that was already painted, so a frame costs a matrix multiply and nothing else. Writing translate3d rather than translateX is what gets the element its own layer in current browsers, and translateZ(0) on an otherwise static element is the same trick under a different name. The cost is real and it is memory: every promoted layer is a bitmap held on the GPU, so a page that promotes fifty of them trades jank for pressure. Promoted text also loses subpixel antialiasing and looks slightly thinner, which is the usual complaint after somebody applies the hack site-wide.