<div class="island-wrapper">
<!-- The Pill Component -->
<div class="island" id="dynamic-island">
<!-- State 1: Loading -->
<div class="island-layer state-loading">
<div class="spinner"></div>
<span class="label">Syncing...</span>
</div>
<!-- State 2: Success -->
<div class="island-layer state-success">
<div class="icon-success">
<svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" stroke-width="3" fill="none"><polyline points="20 6 9 17 4 12"></polyline></svg>
</div>
<div class="text-group">
<span class="title">Success</span>
<span class="subtitle">Data updated</span>
</div>
</div>
</div>
<!-- Trigger for Demo -->
<button class="trigger-btn" onclick="triggerAction()">Save Changes</button>
</div>* { box-sizing: border-box; }
body {
font-family: system-ui, "Segoe UI", sans-serif;
background: #f1f5f9;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 100px;
}
/* Wrapper to position the island (e.g., fixed at top) */
.island-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 60px;
}
.island {
background: #000;
color: white;
border-radius: 50px;
/* Initial State: Tiny Dot or Hidden */
width: 120px;
height: 36px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0,0,0,0.15);
/* The Spring Animation */
transition: width 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275),
height 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
/* LAYERS: Absolute positioning to overlay states */
.island-layer {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
opacity: 0;
transform: scale(0.9);
transition: opacity 0.3s ease, transform 0.3s ease;
pointer-events: none;
}
/* STATE STYLES */
/* 1. Loading State */
.island.mode-loading {
width: 140px;
height: 44px;
}
.island.mode-loading .state-loading {
opacity: 1;
transform: scale(1);
}
.spinner {
width: 18px;
height: 18px;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: white;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
/* 2. Success State */
.island.mode-success {
width: 220px;
height: 54px;
background: #000;
}
.island.mode-success .state-success {
opacity: 1;
transform: scale(1);
}
.icon-success {
color: #4ade80; /* Bright Green */
display: flex;
}
.text-group {
display: flex;
flex-direction: column;
line-height: 1.1;
text-align: left;
}
.title { font-weight: 600; font-size: 0.95rem; }
.subtitle { font-size: 0.75rem; color: #9ca3af; }
/* 3. Idle / Hidden State (Optional: could shrink to 0) */
.island:not(.mode-loading):not(.mode-success) {
width: 0;
height: 0;
opacity: 0;
}
.trigger-btn {
padding: 12px 24px;
background: #2563eb;
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.trigger-btn:hover { background: #1d4ed8; }
@keyframes spin {
to { transform: rotate(360deg); }
}const island = document.getElementById("dynamic-island");
function triggerAction() {
// Prevent double click
if (island.classList.contains("mode-loading") || island.classList.contains("mode-success")) return;
// 1. Start Loading
island.classList.add("mode-loading");
// Simulate Network Request (1.5 seconds)
setTimeout(() => {
// 2. Switch to Success
island.classList.remove("mode-loading");
island.classList.add("mode-success");
// 3. Close after delay (2.5 seconds)
setTimeout(() => {
island.classList.remove("mode-success");
}, 2500);
}, 1500);
}Nothing to show yetType HTML, CSS or JavaScript and your page appears here.
Click any element to find its code Esc
Check this code before you run it
It came from a shared link. It runs with fewer permissions, so it cannot open pop-ups, submit forms or start downloads.
0.00s / 0.00s
Nothing has animated yet. Hover, click or type in the preview and each CSS animation, transition or
element.animate() call shows up here as it plays.