CSS offset-path for the WooCommerce fly-to-cart effect

Bold text CSS Fly to Cart beside an isometric shopping cart with a white product box sliding down a curved dotted rail.

Most WooCommerce stores animate the product image into the cart icon with GSAP or some jQuery plugin the theme shipped years ago. That’s a whole JavaScript dependency for one curve. The CSS offset-path property does the same job in about ten lines, as long as you know its two traps.

This one comes from the CSS-Tricks almanac entry on offset-path, which is still a good reference for the property, its values, and the offset-distance animation pattern. But it’s an old article, and one part of it should not go into new code: it shows the motion-* and offset-* syntaxes side by side. That’s trap number one.

What CSS offset-path actually does

offset-path defines a track. It doesn’t animate anything by itself. You animate offset-distance from 0% to 100% and the element travels along the track. offset-rotate decides which way the element faces, and the default, auto, keeps it pointed along the path like a car on a road.

The value most people use is path(), which takes the same string as an SVG path’s d attribute. So you can draw a curve in Figma or Inkscape, copy the d value, and paste it straight into your stylesheet. For HTML elements the unitless coordinates are pixels. Inside an SVG they follow that SVG’s viewBox instead.

The spec also defines ray() for a line at an angle, url() for referencing an SVG path by ID, and the basic shapes you already know from clip-path, like circle(), ellipse() and polygon(). When CSS-Tricks wrote that page, only path() and none worked, and even url() pointing at a path in the same SVG didn’t render any motion. That has moved on: ray() and the shapes are in the Motion Path spec and documented on MDN. path() itself now works in all current major browsers. If you want ray() on Safari, check the compatibility table on MDN first, because that’s the value I’d still verify per project.

Now the maintenance angle. This property began life as motion-path, with motion-offset and motion-rotation beside it, and the spec later renamed the whole family to offset-path, offset-distance and offset-rotate. Old demos and older theme CSS still use the motion-* names, and browsers treat those as unknown properties today. The animation just doesn’t run, and nothing logs an error, so a subtle effect can die in an update and nobody notices for months.

The fly-to-cart effect without a library

Here’s the pattern I’d use on a store. When add-to-cart succeeds, clone the product image, absolutely position the clone over the button, and let CSS carry it to the cart icon. The curve is drawn relative to the clone’s anchor point, so M 0 40 starts 40px below it and the two control points bend the flight toward wherever the cart icon sits.

.wc-fly-clone {
  offset-path: path("M 0 40 C 120 40, 240 -80, 420 20");
  offset-rotate: 0deg;
  animation: bbioon-fly 0.7s ease-in forwards;
}

@keyframes bbioon-fly {
  to { offset-distance: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .wc-fly-clone { animation: none; }
}

There’s a reason for that offset-rotate: 0deg line. With the default auto, the clone rotates to face its direction of travel, so your product image leans sideways and ends up upside down around the curve. On a product photo, that reads as broken. Keep auto for things that plausibly steer, like an arrow or a paper plane.

If you’d rather trigger the animation from JavaScript, after the add-to-cart AJAX response comes back for instance, you can drive offset-distance with element.animate() from the Web Animations API. The path definition stays in CSS, and you drop the keyframes block. Skipping an animation library for a declarative property is the same trade as replacing a scroll library with CSS, and it compounds: every dependency you don’t ship is one less thing to bundle, cache and debug.

The pixel trap on responsive stores

Trap number two: path() coordinates are absolute pixels. Your curve was drawn for a product page at desktop width, where the cart icon sits at a known position. On a 375px phone the icon is somewhere else entirely, the path has no idea, and the clone sails off toward a corner of the screen. There’s no error in the console, so the customer just watches the image leave.

You have three realistic options. Draw a shorter, flatter path per breakpoint and accept that small screens get a simpler arc. Wrap the animation in a fixed-size element and scale the whole thing down on narrow viewports, which keeps one path but can interact badly with ancestor transforms (I’m not sure this survives every theme’s existing transform rules, so test it on the real theme). Or build the path string in JavaScript at click time from getBoundingClientRect() on the button and the cart icon, which is the only option that’s correct on every layout, and it only takes a few lines.

If you genuinely need path motion that reacts to a live layout, that’s where GSAP’s MotionPathPlugin still earns its place. Most stores don’t need it. A plain diagonal translate() on mobile is usually what the design wanted anyway.

What I’d check before shipping

First, grep your codebase for the dead property names:

grep -rn "motion-path\|motion-offset\|motion-rotation" wp-content/themes wp-content/plugins

Any hit is either dead code you can delete or an animation that silently stopped when browsers dropped the motion-* names. I’d want to know which one it is on every site I maintain.

Second, respect prefers-reduced-motion, as in the snippet above. Fly-to-cart is decoration, and the cart count has to update correctly without it, because the animation must never be load-bearing for the actual purchase flow. The same rules that keep ambient animations from hurting your LCP apply here: position the clone from the button’s real coordinates, not from margins that break the next time the theme changes the button padding.

Do the grep today on your wp-content folder, then open a product page at 375px wide and watch where a fly-to-cart clone goes. If your theme bundles an animation library just for this one effect, that’s the kind of front-end cleanup I take on regularly. Send me the theme name and I’ll tell you what replacing it involves.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.

Leave a Comment