We’re going to learn how to move things around using CSS translate: smooth, GPU-friendly-ish movement that doesn’t cause layout to reshuffle.
There are two main ways you’ll do this in CSS:
- The classic way:
transform: translate(...) - The modern, composable way:
translate: ...(the translate property)
We’ll cover both, plus translateX(), translateY(), translateZ(), and full 3D
with translate3d() and perspective.
What CSS translate does
Translate shifts an element visually along the X and/or Y axis (and Z for 3D):
- Translate moves the element without changing layout for surrounding elements.
- So you can animate it smoothly without pushing other content around.
Think of it like sliding a sticker on top of a page. The page layout stays the same; only the sticker’s visual position changes.
CSS translate property
The translate property is part of the “individual transform properties” family: translate,
rotate, scale. It lets you apply translation without writing the full
transform shorthand.
The typical syntax is:
translate: <tx>;translate: <tx> <ty>;translate: <tx> <ty> <tz>;(3D)
Let’s make it interactive and slide a card around.
.mover {
translate: 0px 0px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
}
.note {
font-family: ui-monospace, SFMono-Regular, Menlo;
font-size: 14px;
line-height: 1.4;
background: #fff;
border: 2px dashed #111;
border-radius: 12px;
padding: 12px;
}
.mover {
width: 220px;
height: 140px;
border-radius: 16px;
border: 3px solid #111;
background: #ffffff;
display: grid;
place-items: center;
font-family: ui-monospace, SFMono-Regular, Menlo;
font-size: 16px;
box-shadow: 0 10px 0 #111;
position: relative;
}
.mover::before {
content: "";
position: absolute;
inset: 10px;
border-radius: 12px;
border: 2px solid #111;
opacity: 0.15;
}
.crosshair {
height: 240px;
display: grid;
place-items: center;
border-radius: 14px;
background:
linear-gradient(#0000 49%, #1112 50%, #0000 51%),
linear-gradient(90deg, #0000 49%, #1112 50%, #0000 51%);
}
Drag the sliders. The element moves visually, but the surrounding layout stays put.translate: x y
Translate units and percentages
You can use px, rem, %, and more. Two important beginner notes:
- Percentages are relative to the element’s own size (not the parent). So
translate: 100% 0;typically moves the element one of its own widths to the right. translatedoesn’t change document flow. If you translate a thing “over” another thing, they can overlap.
Let’s see that percent behavior quickly.
.mover {
translate: 0 0;
}
.mover {
translate: 100% 0;
}
.mover {
translate: 100% 50%;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.row {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
}
.track {
height: 220px;
border: 3px solid #111;
border-radius: 14px;
background: #fff;
display: grid;
place-items: center;
position: relative;
overflow: hidden;
}
.track::before {
content: "Track (element can overlap)";
position: absolute;
top: 10px;
left: 12px;
font-family: ui-monospace, SFMono-Regular, Menlo;
font-size: 13px;
opacity: 0.6;
}
.mover {
width: 200px;
height: 120px;
border-radius: 16px;
border: 3px solid #111;
background: #ffffff;
display: grid;
place-items: center;
font-family: ui-monospace, SFMono-Regular, Menlo;
box-shadow: 0 10px 0 #111;
}
200px wide box
CSS translate vs transform translate
Now the big question: Should you use translate: ... or
transform: translate(...)?
They often produce the same visual result, but they differ in how you compose transforms.
transformis a single property that can include many functions:translate,rotate,scale, etc.translateis only translation, and it composes withrotateandscaleas separate properties.
This can be really helpful when multiple rules or states want to modify different parts of the transform without clobbering each other.
The classic: transform: translate()
.box {
transform: translate(0px, 0px);
}
.box {
transform: translate(120px, 0px);
}
.box {
transform: translate(120px, 60px);
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.track {
height: 220px;
border: 3px solid #111;
border-radius: 14px;
background: #fff;
display: grid;
place-items: center;
position: relative;
}
.box {
width: 180px;
height: 120px;
border-radius: 16px;
border: 3px solid #111;
background: #ffffff;
display: grid;
place-items: center;
font-family: ui-monospace, SFMono-Regular, Menlo;
box-shadow: 0 10px 0 #111;
transition: transform 300ms ease;
}
transform: translate()
The modern: translate: x y
Same idea, but as its own property:
.box {
translate: 0px 0px;
}
.box {
translate: 120px 0px;
}
.box {
translate: 120px 60px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.track {
height: 220px;
border: 3px solid #111;
border-radius: 14px;
background: #fff;
display: grid;
place-items: center;
position: relative;
}
.box {
width: 180px;
height: 120px;
border-radius: 16px;
border: 3px solid #111;
background: #ffffff;
display: grid;
place-items: center;
font-family: ui-monospace, SFMono-Regular, Menlo;
box-shadow: 0 10px 0 #111;
transition: translate 300ms ease;
}
translate: x y
So which one should you pick?
- If you already use
transformeverywhere and you’re comfortable with it, it’s totally fine. - If you want cleaner composition (especially when different states tweak rotate/scale/translate
independently),
translateis really nice.
CSS translateX
translateX() is a transform function. It’s used inside transform like
this:
transform: translateX(20px);
It moves the element horizontally. Here’s a quick comparison of a few values.
.puck {
transform: translateX(0px);
}
.puck {
transform: translateX(120px);
}
.puck {
transform: translateX(-120px);
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.lane {
height: 140px;
border: 3px solid #111;
border-radius: 999px;
background: #fff;
display: grid;
place-items: center;
position: relative;
overflow: hidden;
}
.puck {
width: 110px;
height: 110px;
border-radius: 999px;
border: 3px solid #111;
background: #ffffff;
display: grid;
place-items: center;
font-family: ui-monospace, SFMono-Regular, Menlo;
box-shadow: 0 10px 0 #111;
transition: transform 300ms ease;
}
X
CSS translateY
translateY() moves the element vertically (down for positive values, up for negative values):
transform: translateY(20px);
.puck {
transform: translateY(0px);
}
.puck {
transform: translateY(60px);
}
.puck {
transform: translateY(-60px);
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.lane {
height: 240px;
border: 3px solid #111;
border-radius: 14px;
background: #fff;
display: grid;
place-items: center;
position: relative;
overflow: hidden;
}
.puck {
width: 140px;
height: 90px;
border-radius: 16px;
border: 3px solid #111;
background: #ffffff;
display: grid;
place-items: center;
font-family: ui-monospace, SFMono-Regular, Menlo;
box-shadow: 0 10px 0 #111;
transition: transform 300ms ease;
}
Y
CSS translateZ
translateZ() moves the element toward or away from the viewer in 3D space:
transform: translateZ(80px);
Here’s the gotcha: you usually won’t see much change unless perspective is involved. Perspective gives the browser a “camera” view so Z-depth looks like scaling with depth.
We’ll add perspective on the parent, and preserve 3D so children stay in 3D space.
.scene {
perspective: 700px;
}
.card {
transform: translateZ(0px);
transform-style: preserve-3d;
transition: transform 250ms ease;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.scene {
height: 260px;
border: 3px solid #111;
border-radius: 14px;
background: #fff;
display: grid;
place-items: center;
position: relative;
overflow: hidden;
}
.scene::before {
content: "perspective: 700px";
position: absolute;
top: 10px;
left: 12px;
font-family: ui-monospace, SFMono-Regular, Menlo;
font-size: 13px;
opacity: 0.6;
}
.card {
width: 220px;
height: 140px;
border-radius: 16px;
border: 3px solid #111;
background: #ffffff;
display: grid;
place-items: center;
font-family: ui-monospace, SFMono-Regular, Menlo;
box-shadow: 0 12px 0 #111;
}
.card::after {
content: "Z";
transform: translateZ(1px);
opacity: 0.5;
}
translateZ()
CSS translate X and Y
Most real-world translate usage is “move it a bit this way and that way.” Here are some common patterns:
- Nudging icons:
translate: 2px 1px;(tiny pixel shifts for alignment) - Hover lift:
translate: 0 -6px; - Centering trick: move by
-50%after positioning at50%
Centering with translate(-50%, -50%)
This is a classic: position an element at left: 50% and top: 50%, then translate it back by
half of its own size.
.modal {
left: 50%;
top: 50%;
translate: -50% -50%;
}
.modal {
left: 50%;
top: 50%;
translate: -50% -50%;
rotate: -3deg;
}
.modal {
left: 50%;
top: 50%;
translate: -50% -50%;
rotate: 3deg;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.backdrop {
height: 280px;
border: 3px solid #111;
border-radius: 14px;
background: #fff;
position: relative;
overflow: hidden;
}
.backdrop::before {
content: "";
position: absolute;
inset: 0;
background:
radial-gradient(circle at 20% 20%, #1111 0 30%, #0000 31%),
radial-gradient(circle at 80% 70%, #1111 0 28%, #0000 29%);
}
.modal {
position: absolute;
width: 280px;
padding: 14px;
border-radius: 16px;
border: 3px solid #111;
background: #ffffff;
box-shadow: 0 12px 0 #111;
font-family: ui-monospace, SFMono-Regular, Menlo;
}
.modal p {
margin: 0;
font-size: 14px;
line-height: 1.4;
}
Perfectly centered using left/top plus translate.
CSS translate3d (and perspective)
translate3d(x, y, z) is the 3D version of translate. You’ll typically see it in transform:
transform: translate3d(20px, 10px, 80px);
Again: without perspective, Z movement can feel invisible. So we’ll add perspective on the parent and let you push X, Y, and Z.
.tile {
transform: translate3d(0px, 0px, 0px);
transform-style: preserve-3d;
transition: transform 250ms ease;
}
.world {
perspective: 700px;
}
.floor {
transform: rotateX(55deg);
transform-style: preserve-3d;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.world {
height: 280px;
border: 3px solid #111;
border-radius: 14px;
background: #fff;
display: grid;
place-items: center;
perspective: 700px;
position: relative;
overflow: hidden;
}
.world::before {
content: "perspective: 700px";
position: absolute;
top: 10px;
left: 12px;
font-family: ui-monospace, SFMono-Regular, Menlo;
font-size: 13px;
opacity: 0.6;
}
.floor {
width: 520px;
height: 220px;
border-radius: 16px;
border: 2px dashed #111;
display: grid;
place-items: center;
transform: rotateX(55deg);
transform-style: preserve-3d;
}
.tile {
width: 180px;
height: 120px;
border-radius: 16px;
border: 3px solid #111;
background: #ffffff;
display: grid;
place-items: center;
font-family: ui-monospace, SFMono-Regular, Menlo;
box-shadow: 0 12px 0 #111;
transform-style: preserve-3d;
transition: transform 250ms ease;
}
translate3d()
CSS translate and rotate
When you mix translation and rotation, order matters if you’re using the transform
shorthand.
transform: translate(...) rotate(...)translates first, then rotates the translated element.transform: rotate(...) translate(...)rotates first, then translates along the rotated axes.
This is one of the most common “why is it moving diagonally?!” moments.
.badge {
transform: translate(120px, 0px) rotate(25deg);
}
.badge {
transform: rotate(25deg) translate(120px, 0px);
}
.badge {
transform: translate(120px, 0px) rotate(-25deg);
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.frame {
height: 260px;
border: 3px solid #111;
border-radius: 14px;
background: #fff;
display: grid;
place-items: center;
position: relative;
overflow: hidden;
}
.origin {
width: 10px;
height: 10px;
background: #111;
border-radius: 999px;
position: absolute;
left: 50%;
top: 50%;
translate: -50% -50%;
opacity: 0.35;
}
.badge {
width: 170px;
height: 110px;
border-radius: 16px;
border: 3px solid #111;
background: #ffffff;
display: grid;
place-items: center;
font-family: ui-monospace, SFMono-Regular, Menlo;
box-shadow: 0 12px 0 #111;
transition: transform 250ms ease;
}
order matters
Composing with translate and rotate properties
If you use translate and rotate as separate properties, the browser composes them for you.
This can be nicer when you want “this state moves it” and “that state rotates it” without rewriting one giant
transform string.
.badge {
translate: 120px 0px;
rotate: 25deg;
}
.badge {
translate: 120px 0px;
rotate: -25deg;
}
.badge {
translate: -120px 0px;
rotate: 25deg;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
width: min(720px, 100%);
margin: 0 auto;
padding: 18px;
border: 3px solid #111;
background: #f6f6f6;
border-radius: 14px;
}
.frame {
height: 260px;
border: 3px solid #111;
border-radius: 14px;
background: #fff;
display: grid;
place-items: center;
position: relative;
overflow: hidden;
}
.origin {
width: 10px;
height: 10px;
background: #111;
border-radius: 999px;
position: absolute;
left: 50%;
top: 50%;
translate: -50% -50%;
opacity: 0.35;
}
.badge {
width: 170px;
height: 110px;
border-radius: 16px;
border: 3px solid #111;
background: #ffffff;
display: grid;
place-items: center;
font-family: ui-monospace, SFMono-Regular, Menlo;
box-shadow: 0 12px 0 #111;
transition: translate 250ms ease, rotate 250ms ease;
}
translate + rotate
Translate animation and performance notes
Beginner-friendly rule of thumb:
- Animating
translate(ortransform) is usually smoother than animating layout properties liketop,left,margin. - That’s because transforms typically avoid layout recalculation and can be handled more efficiently by the browser.
Also: translated elements can overlap and create stacking contexts depending on other properties. If something
disappears behind something else, check z-index and whether a new stacking context was created.
Learn more in the CSS Z-Index Interactive Tutorial.
CSS translate not working and debugging
If translate “does nothing,” it’s usually one of these:
- You’re translating the wrong element. Add a border temporarily and confirm which box is moving.
- You’re being overwritten. Another rule sets
transform(ortranslate) later in the cascade. - You used the wrong property.
translateX()is a transform function, not a standalone property. - You used percentages expecting the parent. Remember:
translate: 50% 0;is based on the element’s own size. - 3D without perspective. If
translateZ()seems invisible, addperspectiveon a parent. - Order confusion in transform.
rotate() translate()moves along rotated axes, which feels “wrong” until it clicks.
Quick debug checklist
- Inspect the element in DevTools and check the computed value of
transformand/ortranslate. - Temporarily set
outline: 3px solid #111;to confirm you’re targeting the right box. - Search your CSS for other
transform:declarations on the same element (common with hover states). - If using
translateZortranslate3d, addperspectiveto a parent and try again. - If something overlaps incorrectly, test
positionandz-index(and watch for stacking contexts).
Wrap up
You now have a full translate toolkit:
translate: x y;for clean, composable movementtransform: translate(),translateX(),translateY()for classic transform workflowstranslateZ()andtranslate3d()for 3D movement (with perspective)- And the biggest brain upgrade: transform order matters
CSS translate conclusion
CSS translate is a powerful tool for moving elements around without affecting layout. Whether you use
translate as its own property or translate() inside transform, you can create
smooth animations, hover effects, and dynamic layouts.
Learn more about CSS transforms in general in the CSS Transform Interactive Tutorial.
