webdevpuneet.com

Add to Cart Button — Fly Animation HTML CSS JS Snippet

Add to Cart Button · Buttons · Plain HTML, CSS & JS · Live preview

CategoryButtons

Copy & paste

Source Code

<div class="atc-stage">
  <div class="atc-cart" id="atcCart">
    <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="#1e293b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.7 13.4a2 2 0 0 0 2 1.6h9.7a2 2 0 0 0 2-1.6L23 6H6"/></svg>
    <span class="atc-badge" id="atcBadge">0</span>
  </div>

  <div class="atc-product">
    <div class="atc-img">🎧</div>
    <div class="atc-name">Studio Wireless Headphones</div>
    <div class="atc-price">$199.00</div>
    <button class="atc-btn" id="atcBtn" onclick="addToCart(this)">
      <span class="atc-btn-label">Add to cart</span>
      <svg class="atc-spinner" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.6"><circle cx="12" cy="12" r="9" stroke-opacity=".25"/><path d="M12 3a9 9 0 0 1 9 9"/></svg>
      <svg class="atc-check" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
    </button>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Three-state button machine
Overlaid label, spinner, and checkmark are toggled by .loading and .done classes — idle → loading → success → reset, all in CSS opacity transitions.
Fly-to-cart dot
getBoundingClientRect measures button and cart; a fixed dot animates with translate + scale on an arc and removes itself on transitionend.
Scroll-safe fixed positioning
The flying dot is position: fixed on body, so it lands accurately no matter where the product sits or how far the page is scrolled.
Arrival-synced badge
The count increments when the dot lands — not on click — so the cart number rises exactly as the product "drops in".
Springing + bumping badge
The badge reveals with a cubic-bezier spring from scale(0) and re-bumps on each add via a forced-reflow class restart.
Cart icon shake
A short atc-shake keyframe nudges the cart on arrival, reinforcing that an item just landed.
Re-entrancy guard
A busy flag and disabled button ignore clicks mid-animation, preventing overlapping cycles from rapid tapping.
Spring checkmark success
The confirm tick pops in with a cubic-bezier(.2,1.5,.4,1) scale and a green button, an unmistakable success signal.

About this UI Snippet

Add to Cart Button — Fly-to-Cart Dot, Loading→Success State Machine & Springing Badge

Screenshot of the Add to Cart Button snippet rendered live

The add-to-cart button is the most important button in commerce. The moment it is clicked decides whether a browsing session becomes revenue, and the feedback it gives in that moment shapes whether the shopper trusts that the item was actually added. A button that silently does nothing for 400ms feels broken; a button that shows progress, confirms success, and visibly sends the product into the cart feels reliable and delightful. This snippet implements that full experience in plain HTML, CSS, and vanilla JavaScript: a three-state button, a dot that flies from the button into the cart icon, and a badge counter that springs and bumps.

Loading → success state machine

The button holds three overlaid elements — a label, a spinner, and a checkmark — all absolutely centred. addToCart walks them through a state machine using classes. On click it adds .loading (label fades out, spinner fades in and rotates), then after a short delay swaps to .done (spinner out, green background, checkmark pops in with a spring, label becomes "Added"), and finally reverts to idle. A busy guard ignores clicks while a cycle is in progress, so rapid clicking cannot desync the states.

Fly-to-cart dot

This is the signature effect. flyToCart measures the button and the cart icon with getBoundingClientRect, creates a fixed-position dot at the button's centre, and on the next animation frame applies a transform: translate(dx, dy) scale(.4) with a cubic-bezier transition that has a slight negative control point — giving the dot a subtle arc rather than a straight line. The dot fades as it travels and removes itself on transitionend. Because it is position: fixed and appended to body, it flies correctly regardless of scroll position or where the product sits on the page.

Springing, bumping badge

When the dot lands, landInCart increments the count, reveals the badge with a spring scale (it starts at scale(0)), and re-triggers a bump keyframe via a forced reflow so every subsequent add visibly pulses the number. The cart icon itself does a quick shake, reinforcing that something just arrived. Synchronising the badge update to the dot's arrival — not the button click — is the detail that makes the whole thing feel physically connected: the count goes up exactly when the product "drops in".

Production-ready structure

The 650ms loading delay stands in for a network request; replace it with your real add-to-cart API call and trigger the .done state on the resolved promise. The fly animation and badge logic stay exactly the same. Pair this button with a mini cart dropdown, a product card grid, a variant selector for size/colour, or an order summary at checkout.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA product card appears with a floating cart icon (badge at 0) and a purple "Add to cart" button.
  2. 2
    Click Add to cartThe label fades to a spinning loader, and a purple dot launches from the button on a gentle arc toward the cart icon.
  3. 3
    Watch the dot landWhen the dot reaches the cart, the icon shakes, the badge springs into view, and the count ticks to 1.
  4. 4
    See the success stateThe button turns green with a popping checkmark and reads "Added", confirming the item was added.
  5. 5
    Add againAfter the button resets, click again — the badge bumps with a pulse to 2, 3, and so on, each synced to the dot's arrival.
  6. 6
    Try rapid clicksClick repeatedly — the busy guard ignores clicks mid-cycle so the states never overlap or desync.

Real-world uses

Common Use Cases

Product grids and listings
Add items straight from a catalogue without leaving the page. Drop it onto a product card and pair with a faceted filter sidebar.
Product detail pages
The primary CTA below the gallery; combine it with a variant selector so size and colour are chosen before the fly animation fires.
Quick-add in mini carts
Reuse the button inside a mini cart or sticky cart drawer for "buy it again" rows.
Wishlist-to-cart moves
Animate moving a saved item into the cart; combine with a favorite button for the save action.
Menu and food ordering
Add dishes to an order with satisfying feedback — the arrival-synced badge mirrors a growing food-delivery basket.
Digital goods and tickets
Even without shipping, the confirmation state and badge reassure buyers that a licence or ticket was added before checkout.
Related: Async Button with Real Progress Fill (Not a Fake Spinner)
See the Async Button with Real Progress Fill (Not a Fake Spinner) for a related buttons pattern worth pairing with this one.
Related: Dual-Hold Safety Button
See the Dual-Hold Safety Button for a related buttons pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Replace the 650ms setTimeout with your actual request: call your cart endpoint, and switch the button to the .done state in the promise's .then. Trigger flyToCart immediately on click for instant feedback (optimistic), or wait for success if you prefer; landInCart should set the badge to the count returned by the server rather than a local counter.

getBoundingClientRect returns viewport-relative coordinates, which match a position: fixed element's coordinate system exactly. That means the dot flies to the cart accurately regardless of page scroll, parent overflow, or transforms on ancestors — far more robust than computing offsets through nested offsetParent chains.

Wrap flyToCart in a check for window.matchMedia('(prefers-reduced-motion: reduce)').matches and skip the dot when true, calling landInCart directly. The button still shows the loading and success states, so the interaction stays clear without the travelling animation.

This snippet already guards with a busy flag and a disabled button during the cycle. For network-backed carts, also debounce the API call and rely on the server returning the authoritative quantity, so even if a click slips through, the displayed count stays correct.

In React, hold the button status ('idle' | 'loading' | 'done') and cartCount in useState, and run the fly animation in a click handler using a ref to the cart node. In Vue, use refs and bind :class from the status; trigger the dot in a method. In Angular, track status on the component and use [class.loading]/[class.done]. The fly and badge CSS port unchanged.