ScreenshotNeo

BlogGuides

CSS transform-origin: How to Control Transform Points

Use CSS transform-origin to choose where rotation, scaling, and skew pivot. Learn the syntax, responsive values, 3D offsets, SVG defaults, and debugging tips.

By the ScreenshotNeo team4 October 20267 min read

transform-origin sets the point around which a CSS transform acts. To rotate an element around its bottom-left corner, for example, set transform-origin: bottom left and then apply transform: rotate(...). For ordinary HTML elements the default pivot is the center, 50% 50% 0.

The property changes the transform’s pivot, not the transform function itself. Rotation, scaling, and skew use that pivot. MDN describes the effect as moving the chosen point to the origin, applying the transform, and moving it back; this is why an element may appear to shift when you change its origin even though its transform value stays the same. See the MDN reference and the CSS Transforms specification.

1. Basic syntax and values

The property accepts one, two, or three values:

transform-origin: x-position;
transform-origin: x-position y-position;
transform-origin: x-position y-position z-offset;
  • One value: a length or percentage sets the horizontal position. The vertical position defaults to center.
  • Two values: set the horizontal and vertical positions.
  • Three values: the first two set the horizontal and vertical positions; the third is a length along the Z axis. A percentage is invalid for the third value.
Value Meaning Example
left / top 0% along the corresponding axis left top
center 50% along the corresponding axis center center
right / bottom 100% along the corresponding axis right bottom
Percentage Position relative to the element’s bounding box 25% 75%
Length Explicit offset 20px 10px
Third length Z-axis offset for 3D transforms center center 30px

Keywords map to percentages: left and top are 0%, center is 50%, and right and bottom are 100%. Percentages refer to the element’s own bounding box, not the viewport. So 50% 50% means the box’s center.

For example, top right and right top describe the same corner. When mixing keywords and numbers, remember the axes: horizontal keywords (left, center, right) position X; vertical keywords (top, center, bottom) position Y.

2. Pick a pivot for common effects

Rotate around a corner

.card {
  transform-origin: bottom left;
  transform: rotate(12deg);
}

The card rotates around its bottom-left corner. Use top left, top right, or bottom right for the other corners. Positive and negative rotation angles select opposite directions according to CSS’s coordinate system.

Scale from an edge

.panel {
  transform-origin: 50% 100%;
  transform: scale(1.1);
}

This scales from the bottom edge, horizontally centered. Use left center or right center to scale from a side. Scaling from an edge can make the element grow into neighboring content; the origin does not reserve extra layout space.

Use a custom responsive point

.badge {
  transform-origin: 20% 80%;
  transform: rotate(-8deg);
}

Percentages adapt to the element’s box as its dimensions change. This is useful when the pivot should stay at a proportional location. Use lengths such as 12px 8px when the pivot must remain a fixed distance from the top-left edge.

Place the pivot outside the box

.swinging-door {
  transform-origin: -10% 50%;
  transform: rotateY(30deg);
}

Negative and over-100% values are valid. For instance, -20% 120% puts the pivot left of and below the box. An outside pivot can create a hinge or orbit-like effect, though the transformed element may overlap other content or be clipped by an ancestor’s overflow.

Set a 3D Z offset

.cube-face {
  transform-origin: center center 30px;
  transform: rotateY(35deg);
}

The third value is a length along Z. It can matter with 3D transforms and perspective, but it does not replace a parent’s perspective property. Do not write a percentage as the third value: transform-origin: center center 20% is invalid.

3. Runnable example

Save this as an HTML file and open it in a browser. Change the selector’s origin or angle to see the pivot move. The outline marks the untransformed box; the dot marks the selected pivot.

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Transform origin demo</title>
<style>
  body { font: 16px system-ui, sans-serif; padding: 3rem; }
  .stage { padding: 3rem; border: 1px dashed #888; width: max-content; }
  .card {
    position: relative;
    width: 12rem;
    height: 7rem;
    display: grid;
    place-items: center;
    background: #d9eaff;
    border: 2px solid #245b9e;
    transform-origin: bottom left;
    transform: rotate(-12deg);
  }
  .card::before {
    content: "";
    position: absolute;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #d22;
    left: 0;
    bottom: 0;
    transform: translate(-50%, 50%);
  }
</style>
<div class="stage"><div class="card">Rotate around bottom left</div></div>
</html>

4. Choose between keywords, percentages, lengths, and 3D values

Choose When it fits Example
Edge or corner keywords The pivot is a semantic edge or corner and readability matters bottom left
Percentages The pivot should scale with the element’s dimensions 30% 70%
Lengths The pivot should stay a precise distance from an edge 16px 12px
Third Z length A 3D transform needs its origin offset along Z center center 24px

The initial value for ordinary CSS elements is 50% 50% 0. The property is not inherited, so a child does not automatically take its parent’s pivot. For a transition between pivot locations, set the origin before the transform or transition it deliberately; changing both at once can make the motion harder to reason about.

5. SVG and browser compatibility

SVG has a notable default difference. For SVG elements, the initial value is generally 0 0, except the root <svg> and an <svg> directly inside <foreignObject>, which use 50% 50% like ordinary CSS elements. If an SVG shape rotates around an unexpected point, set transform-origin explicitly and check the SVG element’s box and transform reference box.

MDN marks transform-origin as widely available across browsers since September 2015. For legacy browsers, SVG behavior, or unusual syntax, check the current compatibility table.

6. Debugging checklist

  1. Confirm the element has a transform. transform-origin alone does not rotate or scale anything.
  2. Check the element’s actual box. Percentages are relative to that box, which can differ from the visible artwork inside an SVG.
  3. Verify value order: X first, Y second. Use bottom left for the bottom-left corner.
  4. Check computed styles in browser developer tools. A later, more specific rule may override the declaration.
  5. Look for clipping from ancestors with overflow: hidden or overflow: clip.
  6. For 3D, check the parent’s perspective and preserve-3d setup as well as the child’s Z origin.
Symptom Likely cause Fix
It still rotates around the center Rule is overridden, selector does not match, or there is no effective transform Inspect computed transform-origin and transform; increase selector specificity only if needed.
It shifts when the origin changes Expected pivot compensation: the selected point is translated to and from the origin during transformation Adjust surrounding layout or transform values if the final visual position must stay fixed.
Percentage pivot seems wrong Percentage is based on the element box, not the viewport or visible child artwork Inspect dimensions and use an explicit length if the desired point is a fixed distance.
SVG rotates around an unexpected point SVG initial value or reference box differs from the HTML assumption Set the origin explicitly and verify SVG transform-box behavior.
Third value declaration is ignored Z position was written as a percentage or the declaration has invalid syntax Use a length, such as 30px, for the third value.
Part of the transformed element disappears An ancestor clips overflow Review ancestor overflow or place the transformed element in a container that permits the needed space.

7. Performance and reliability notes

transform-origin is a geometric input to a transform; it does not itself change document flow or add a screenshot-specific delay. If an animation looks janky, first check how much content is being transformed, how many elements animate at once, and whether layout or paint work is also happening. Keep the pivot rule explicit where SVG, dynamic sizing, or inherited assumptions could make the result unclear.

For visual regression or documentation captures, use the same viewport, device scale, page state, and animation state each time. A different viewport changes percentage pivots when the element’s own box changes. If the goal is simply to capture a page after its CSS has rendered, a screenshot API can avoid maintaining browser launch and capture code.

8. Capture the result with ScreenshotNeo

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. Its screenshot endpoint can capture a rendered page as PNG, JPEG, WebP, or PDF. Use the browser demo above to tune the origin, then capture the page containing the final CSS. Full API options are in the ScreenshotNeo documentation.

Or skip the browser setup

One GET request returns the screenshot. Replace the target URL with your page; the example saves the response as WebP.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo accepts cookie consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card required.

9. FAQ

What does transform-origin: 50% 50% mean?

It sets the pivot to the center of the element’s bounding box in both axes.

Can transform-origin be animated?

Yes. It can be changed over time, but a moving pivot changes the transform’s apparent path. Test the full animation rather than only its start and end values.

Does transform-origin move the element in normal layout?

No. Transforms affect the rendered result without repositioning the element’s normal-flow layout box. The transformed pixels may extend beyond that box.

Is transform-origin: right top different from top right?

No. Both identify the top-right point.