---
title: 'Transforms'
excerpt: 'Prefer x, y, scale, and rotation over layout properties for smooth 60fps motion.'
icon: 'MousePointer2'
---

## Transform aliases

GSAP's `x`, `y`, `scale`, and `rotation` map to compositor-friendly **transforms** — not `left`, `top`, or `width`. Layout properties force reflow every frame; transforms usually stay on the GPU layer.

| GSAP | Prefer over |
| --- | --- |
| `x`, `y` | `left`, `top`, `margin` |
| `xPercent` | percentage `translate` for drawers |
| `scale` | animating `width`/`height` for simple zoom |
| `transformOrigin` | popovers scaling from trigger |

Reach for layout animation only when the document must reflow (accordion height, FLIP between grid and list). For slides, lifts, and pops, stay on transforms.

## Drawer (`xPercent`)

`xPercent` moves an element by a **percentage of its own width** — ideal for off-canvas drawers that should fully clear the viewport regardless of pixel size.

Toggle open/closed: `0` seats the panel; `100` parks it off to the right. Same duration and ease both ways keeps the sheet predictable.

<ComponentPreview>
  <GsapDrawerSlide />

  <DocsFileTabs title="Drawer xpercent">

```tsx title="drawer-xpercent.tsx"
gsap.to('.gsap-drawer', {
  xPercent: open ? 0 : 100,
  duration: 0.35,
  ease: 'power2.out',
});
```

```html title="drawer-xpercent-markup.html"
<aside class="gsap-drawer absolute right-0 top-0 h-full w-2/3 border-l bg-card p-4">
  <p>Settings</p>
  <p>Notifications, privacy, account.</p>
</aside>
```

  </DocsFileTabs>
</ComponentPreview>

## Origin-aware popover

Default `transformOrigin` is the element's center, so a menu scales from the middle of nowhere. Set origin to the **trigger corner** (`top left`) so the panel grows out of the button that opened it.

Watch: scale + fade from the filter control — it feels attached, not floating in.

<ComponentPreview>
  <GsapOriginPopover />

  <DocsFileTabs title="Popover origin">

```tsx title="popover-origin.tsx"
gsap.fromTo('.gsap-popover', { scale: 0.95, autoAlpha: 0 }, {
  scale: 1,
  autoAlpha: 1,
  duration: 0.2,
  ease: 'power2.out',
  transformOrigin: 'top left',
});
```

```html title="popover-origin-markup.html"
<div class="relative">
  <button>Filter</button>
  <!-- transformOrigin: 'top left' on .gsap-popover -->
  <div class="gsap-popover absolute left-0 top-full mt-2 w-44 rounded-lg border bg-card p-3">
    <p>Status</p>
    <p>Active · Draft · Archived</p>
  </div>
</div>
```

  </DocsFileTabs>
</ComponentPreview>

## Accordion chevron

`rotation` is a transform — cheap and smooth. Rotate the chevron to mirror open/closed; keep the panel height change separate (or use Flip / grid tricks) so you are not animating layout on the icon.

Click the row: the chevron turns `180°` with `power2.out` while the panel reveals.

<ComponentPreview>
  <GsapAccordionChevron />

  <DocsFileTabs title="Accordion rotation">

```tsx title="accordion-rotation.tsx"
gsap.to('.gsap-chevron', { rotation: open ? 180 : 0, duration: 0.25, ease: 'power2.out' });
```

```html title="accordion-rotation-markup.html"
<div class="rounded-lg border">
  <button>
    Billing details
    <span class="gsap-chevron inline-block">▼</span>
  </button>
  <div class="gsap-panel overflow-hidden px-4 pb-3">
    Update payment method and download invoices.
  </div>
</div>
```

  </DocsFileTabs>
</ComponentPreview>

## Good vs bad

Same visual travel, different cost. Top bar uses `x` (transform). Bottom bar uses `left` (layout). Side by side, both move — only one stays compositor-friendly under load.

Prefer transforms unless the layout itself must change. When a class should take over after a tween finishes, use `clearProps` so inline styles do not stick.

<ComponentPreview>
  <GsapTransformVsLayout />

  <DocsFileTabs title="Transform vs layout">

```tsx title="transform-vs-layout.tsx"
gsap.to('.gsap-good', { x: 80, duration: 1, repeat: -1, yoyo: true }); // GPU
gsap.to('.gsap-bad', { left: 80, duration: 1, repeat: -1, yoyo: true }); // layout
```

```html title="transform-vs-layout-markup.html"
<div>
  <p>Good: transform x (GPU)</p>
  <div class="relative h-10">
    <div class="gsap-good absolute left-0 top-1 size-8 rounded"></div>
  </div>
  <p>Avoid: left (layout)</p>
  <div class="relative h-10">
    <div class="gsap-bad absolute left-0 top-1 size-8 rounded"></div>
  </div>
</div>
```

  </DocsFileTabs>
</ComponentPreview>
