A parcel drops into an empty shopping cart and the cart rocks on its wheels under the weight before settling. The parcel lives inside the cart group, so it rides the rock with it.
Also known as: shopping cart icon animation css, cart icon hover, add to basket animation.
<button class="hfx-cart-icon-add" aria-label="Add to cart"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><g class="hfx-cart-icon-add-cart"><rect class="hfx-cart-icon-add-item" x="10" y="3.5" width="7" height="6.5" rx="1.2" fill="currentColor" stroke="none"/><circle cx="8" cy="21" r="1"/><circle cx="19" cy="21" r="1"/><path d="M2 2h2l2.7 12.4a2 2 0 0 0 2 1.6h9.8a2 2 0 0 0 1.9-1.6L22 7.4H5.1"/></g></svg></button>
.hfx-cart-icon-add {
--c: var(--hfx-color, #3b82f6);
font-size: var(--hfx-size, 16px);
position: relative;
display: grid;
place-items: center;
width: 3.5em;
height: 3.5em;
padding: 0;
border: 0;
border-radius: 50%;
cursor: pointer;
background: color-mix(in srgb, var(--c) 12%, transparent);
color: var(--c);
transition: background-color 0.25s ease;}
.hfx-cart-icon-add svg {
width: 1.6em;
height: 1.6em;
overflow: visible;
}
.hfx-cart-icon-add-cart,
.hfx-cart-icon-add-item {
transform-box: view-box;
transform-origin: 56% 87.5%;
}
.hfx-cart-icon-add-item { opacity: 0; }
.hfx-cart-icon-add:is(:hover, :focus-visible) { background: color-mix(in srgb, var(--c) 20%, transparent); }
.hfx-cart-icon-add:is(:hover, :focus-visible) .hfx-cart-icon-add-item {
animation: hfx-cart-icon-add-drop 0.8s both;
}
.hfx-cart-icon-add:is(:hover, :focus-visible) .hfx-cart-icon-add-cart {
animation: hfx-cart-icon-add-rock 0.8s both;
}
@keyframes hfx-cart-icon-add-drop {
0% { opacity: 0; transform: translateY(-55%); animation-timing-function: cubic-bezier(0.55, 0, 1, 0.45); }
15% { opacity: 1; }
40% { opacity: 1; transform: translateY(0); animation-timing-function: ease-out; }
55% { opacity: 1; transform: translateY(-9%); animation-timing-function: ease-in; }
70%, 100% { opacity: 1; transform: translateY(0); }
}
@keyframes hfx-cart-icon-add-rock {
0%, 38% { transform: rotate(0deg); }
50% { transform: rotate(9deg); }
64% { transform: rotate(-5deg); }
78% { transform: rotate(2deg); }
100% { transform: rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
.hfx-cart-icon-add, .hfx-cart-icon-add *, .hfx-cart-icon-add::before, .hfx-cart-icon-add::after, .hfx-cart-icon-add *::before, .hfx-cart-icon-add *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--hfx-color on the element or a parent.
Currently previewing #3b82f6.--hfx-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 16px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.