پیشنمایش زنده
کد HTML
<div class="stat-widget" aria-hidden="false">
<button class="stat-pill variant-green" aria-label="67 percent sales by day">
<div class="ring-wrap" aria-hidden="true">
<div class="ring" role="presentation">
<div class="core"></div>
</div>
</div>
<div class="meta">
<div class="value">67%</div>
<div class="label">Sales by Day</div>
</div>
<span class="ground-shadow" aria-hidden="true"></span>
</button>
<button class="stat-pill variant-pink" aria-label="$234 sales by day">
<div class="ring-wrap" aria-hidden="true">
<div class="ring" role="presentation">
<div class="core"></div>
</div>
</div>
<div class="meta">
<div class="value">$ 234</div>
<div class="label">Sales by Day</div>
</div>
<span class="ground-shadow" aria-hidden="true"></span>
</button>
</div>
کد CSS
/* Entire component scoped under .stat-widget to avoid global selectors */
.stat-widget {
display: flex;
flex-direction: column;
gap: 22px;
padding: 28px;
min-height: 140px;
align-items: center;
justify-content: center;
font-family:
Inter,
system-ui,
-apple-system,
"Segoe UI",
Roboto,
"Helvetica Neue",
Arial;
perspective: 900px; /* gives 3D depth for children */
}
.stat-widget .stat-pill {
width: 320px;
height: 96px;
border-radius: 48px;
background: linear-gradient(
180deg,
rgba(255, 255, 255, 0.03),
rgba(255, 255, 255, 0.01)
);
box-shadow:
0 8px 18px rgba(0, 0, 0, 0.6),
inset 0 1px 0 rgba(255, 255, 255, 0.02);
display: flex;
align-items: center;
gap: 16px;
padding: 14px 20px;
position: relative;
transform-style: preserve-3d;
transition:
transform 300ms cubic-bezier(0.2, 0.9, 0.3, 1),
box-shadow 300ms;
cursor: pointer;
user-select: none;
border: none;
outline: none;
background-clip: padding-box;
}
.stat-widget .stat-pill:before {
content: "";
position: absolute;
left: 0;
right: 0;
top: 6px;
height: 36px;
border-radius: 48px 48px 0 0;
background: linear-gradient(
180deg,
rgba(255, 255, 255, 0.03),
rgba(255, 255, 255, 0)
);
pointer-events: none;
mix-blend-mode: overlay;
}
.stat-widget .ring-wrap {
width: 64px;
height: 64px;
position: relative;
flex: 0 0 64px;
transform-style: preserve-3d;
}
/* Ring has a rotating transform to simulate fill animation (CSS-only) */
.stat-widget .ring {
width: 100%;
height: 100%;
border-radius: 50%;
display: grid;
place-items: center;
box-shadow:
0 6px 12px rgba(0, 0, 0, 0.4),
inset 0 -6px 18px rgba(0, 0, 0, 0.35);
transform-origin: 50% 50%;
transition: transform 350ms cubic-bezier(0.2, 0.9, 0.3, 1);
will-change: transform;
/* animation: rotateIn to give impression of progress filling */
animation: ringRotate 900ms cubic-bezier(0.2, 0.9, 0.3, 1) both;
}
.stat-widget .ring .core {
width: 46px;
height: 46px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.55);
display: grid;
place-items: center;
color: #fff;
font-weight: 600;
font-size: 12px;
letter-spacing: -0.02em;
box-shadow:
0 2px 6px rgba(0, 0, 0, 0.6),
inset 0 1px 0 rgba(255, 255, 255, 0.02);
}
.stat-widget .meta {
color: #e9eef9;
display: flex;
flex-direction: column;
justify-content: center;
gap: 6px;
transform: translateZ(6px);
}
.stat-widget .meta .value {
font-size: 20px;
font-weight: 700;
line-height: 1;
}
.stat-widget .meta .label {
font-size: 12px;
color: rgba(255, 255, 255, 0.65);
font-weight: 500;
}
/* ground shadow for 3D depth */
.stat-widget .stat-pill .ground-shadow {
position: absolute;
left: 10px;
right: 10px;
bottom: -12px;
height: 14px;
border-radius: 50%;
filter: blur(18px);
opacity: 0.45;
pointer-events: none;
background: linear-gradient(90deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.35));
transform: translateZ(-40px);
transition: opacity 220ms;
}
.stat-widget .stat-pill:hover .ground-shadow {
opacity: 0.9;
}
/* hover/active tilt purely with CSS to create 3D interaction */
.stat-widget .stat-pill:hover {
transform: translateY(-10px) rotateX(6deg) rotateY(8deg) scale(1.01);
box-shadow: 0 18px 40px rgba(2, 6, 23, 0.6);
}
.stat-widget .stat-pill:active {
transform: translateY(-4px) rotateX(2deg) rotateY(3deg) scale(0.995);
transition-duration: 120ms;
}
.stat-widget .stat-pill:focus {
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
}
/* subtle breathing animation for the pill itself */
@keyframes floaty {
0% {
transform: translateY(0) translateZ(0);
}
50% {
transform: translateY(-4px) translateZ(4px);
}
100% {
transform: translateY(0) translateZ(0);
}
}
.stat-widget .stat-pill {
animation: floaty 4200ms ease-in-out infinite;
}
/* ring rotate animation to simulate fill */
@keyframes ringRotate {
0% {
transform: rotate(-120deg);
}
70% {
transform: rotate(8deg);
}
100% {
transform: rotate(0deg);
}
}
/* color variants: the conic-gradient is hard-coded to show a desired percentage
67% => angle 241.2deg (67*3.6)
72% => angle 259.2deg (72*3.6)
*/
.stat-widget .stat-pill.variant-green .ring {
background-image: conic-gradient(
#2ee6a6 0deg,
#2ee6a6 241.2deg,
rgba(255, 255, 255, 0.06) 241.2deg 360deg
);
}
.stat-widget .stat-pill.variant-pink .ring {
background-image: conic-gradient(
#ff6ad5 0deg,
#ff6ad5 259.2deg,
rgba(255, 255, 255, 0.06) 259.2deg 360deg
);
}
/* place core text centrally and ensure percentage formatting */
.stat-widget .stat-pill .core-percent {
font-size: 12px;
font-weight: 700;
color: #fff;
}
/* responsive tweak for narrow screens */
@media (max-width: 360px) {
.stat-widget {
padding: 18px;
}
.stat-widget .stat-pill {
width: 280px;
height: 92px;
}
.stat-widget .ring-wrap {
width: 58px;
height: 58px;
}
.stat-widget .ring .core {
width: 42px;
height: 42px;
}
}