transform-origin

Sets the point that rotate/scale (and other transform functions) pivot around. The default is the element's own centre, 50% 50%.

Also known as: Rotation pivotAnchor point
···
html
<div class="wrap"><div class="square" id="sq"><i class="dot" id="dot"></i></div></div><div class="label" id="l">transform-origin: 0% 0%</div>
css
.wrap{position:relative;display:grid;place-items:center}
.square{position:relative;width:min(26vmin,64px);aspect-ratio:1;border-radius:12px;
  background:linear-gradient(135deg,#5b5bf7,#18c29c)}
.dot{position:absolute;width:12px;height:12px;margin:-6px 0 0 -6px;border-radius:50%;background:#fff;
  box-shadow:0 0 0 3px rgba(0,0,0,.3);transition:left .5s,top .5s}
.label{position:absolute;left:50%;bottom:6%;transform:translateX(-50%);padding:4px 10px;border-radius:999px;
  background:rgba(0,0,0,.6);color:#fff;font:600 12px/1.4 monospace}
js
const sq = document.getElementById('sq');
const dot = document.getElementById('dot');
const l = document.getElementById('l');
const ORIGINS = ['0% 0%','100% 0%','100% 100%','50% 50%','20% 80%'];
let oi = 0, angle = 0;
function setOrigin(){
  const css = ORIGINS[oi];
  sq.style.transformOrigin = css;
  const [x, y] = css.split(' ');
  dot.style.left = x; dot.style.top = y;
  l.textContent = 'transform-origin: ' + css;
  oi = (oi + 1) % ORIGINS.length;
}
setOrigin();
setInterval(setOrigin, 1800);
function spin(){ angle += 0.6; sq.style.transform = 'rotate(' + angle + 'deg)'; requestAnimationFrame(spin); }
spin();

transform-origin doesn't move the element — it only changes which point stays fixed while rotate/scale act on it. Values are %/px/keywords (top left, etc.) relative to the element's own box, and 3D transforms can add a third z value.

The dot in the demo marks whatever point is currently the pivot. Put it at a corner (0% 0%) and the element swings around that corner in a wide arc; put it at the centre (50% 50%) and it spins in place like a top.

The most common mix-up is treating it as a way to move the element — changing transform-origin alone leaves its on-screen position untouched; it only changes the path the following rotate/scale takes. A menu that unfurls from its trigger button's corner, or an accordion that expands from its top edge, are the textbook uses.

When to use

Use it to make a menu/tooltip appear to grow out of its trigger, or an accordion expand around its top edge.