<button class="cart-btn">
<span class="btn-content">
<span class="btn-text">Add to Cart</span>
<svg class="icon-cart" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="9" cy="21" r="1"></circle>
<circle cx="20" cy="21" r="1"></circle>
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
</svg>
</span>
<div class="loader"></div>
<svg class="icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
</button>
* { box-sizing: border-box; }
body {
font-family: system-ui, "Segoe UI", sans-serif;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: #f1f5f9;
}
.cart-btn {
position: relative;
background: #1e293b;
color: white;
border: none;
border-radius: 12px;
padding: 14px 28px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
overflow: hidden;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
display: flex;
align-items: center;
justify-content: center;
min-width: 160px;
height: 54px;
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
}
.cart-btn:hover {
background: #334155;
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0,0,0,0.15);
}
.btn-content {
display: flex;
align-items: center;
gap: 10px;
transition: opacity 0.3s, transform 0.3s;
}
.icon-cart {
width: 20px;
height: 20px;
}
.loader {
display: none;
width: 24px;
height: 24px;
border: 3px solid rgba(255,255,255,0.3);
border-top-color: white;
border-radius: 50%;
animation: spin 0.8s linear infinite;
position: absolute;
}
.icon-check {
display: none;
width: 28px;
height: 28px;
color: white;
position: absolute;
stroke-dasharray: 24;
stroke-dashoffset: 24;
animation: drawCheck 0.4s forwards 0.1s;
}
/* STATE: Loading */
.cart-btn.is-loading {
width: 54px; /* Collapse to circle */
min-width: 54px;
border-radius: 50%;
background: #334155;
cursor: default;
transform: none;
padding: 0;
}
.cart-btn.is-loading .btn-content {
opacity: 0;
transform: scale(0.5);
}
.cart-btn.is-loading .loader {
display: block;
}
/* STATE: Added */
.cart-btn.is-added {
width: 54px;
min-width: 54px;
border-radius: 50%;
background: #22c55e; /* Green */
cursor: default;
padding: 0;
}
.cart-btn.is-added .btn-content,
.cart-btn.is-added .loader {
display: none;
}
.cart-btn.is-added .icon-check {
display: block;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes drawCheck {
to { stroke-dashoffset: 0; }
}
const btn = document.querySelector(".cart-btn");
btn.addEventListener("click", () => {
// Prevent multiple clicks
if (btn.classList.contains("is-loading") || btn.classList.contains("is-added")) return;
// 1. Start Loading
btn.classList.add("is-loading");
// Simulate API request (1.5s)
setTimeout(() => {
// 2. Success State
btn.classList.remove("is-loading");
btn.classList.add("is-added");
// 3. Reset after 2.5s so user can add again
setTimeout(() => {
btn.classList.remove("is-added");
}, 2500);
}, 1500);
});