Status Badge Design UI — 9 CSS Ideas to Copy
This status badge design ui roundup turns a small detail — one dot, one label — into instant feedback: online or away, urgent or not, verified or plain.
Auto-plays · click a tile to jump to its section · all nine in one zip
- 01 Status pill indicator (online/away/busy/offline)
- 02 Removable tag chip
- 03 Priority label badge (low/medium/high/urgent)
- 04 Corner ribbon fold-unfurl
- 05 Notification dot double-pulse
- 06 Tech-stack tag cloud
- 07 Achievement badge unlock animation
- 08 Premium / verified badge flip
- 09 Countdown expiry chip
These nine follow the order in which a real product shows them, not popularity. You log in and see a teammate's online status (01), pick and remove interest tags (02), and set a task's priority (03). Browsing turns up a sale ribbon (04) and a new notification (05); your profile collects a tech-stack cloud (06), a freshly unlocked achievement (07), and a verified badge (08); and a coupon's countdown (09) closes things out.
01Status pill indicator (online/away/busy/offline)
A ring hidden behind the online dot expands once via opacity and scale, while the other three states sit still, differing only in color.
.pill--online .dot::before {
content: "";
position: absolute; inset: -4px;
border-radius: 50%;
border: 2px solid $color;
opacity: 0;
transform: scale(.6);
}
.stage.is-demo .pill--online .dot::before {
animation: ping $duration $easing infinite;
}
@keyframes ping {
0% { opacity: .55; transform: scale(.6); }
70%, 100% { opacity: 0; transform: scale(2.1); }
}
02Removable tag chip
Clicking the × actually shrinks and removes a chip via scale and opacity, and the demo loops that same motion on its own.
.chip--gone {
transition: transform $dur-quick $easing, opacity $dur-quick $easing;
transform: scale(.6);
opacity: 0;
}
.stage.is-demo .chip--demo {
animation: chip-cycle $duration $easing infinite;
}
@keyframes chip-cycle {
0%, 38% { transform: scale(1); opacity: 1; }
55%, 78% { transform: scale(.55); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
03Priority label badge (low/medium/high/urgent)
Only the urgent label flashes via opacity and scale, so it's the first thing you notice among labels that differ only in color.
.label--urgent {
background: $error-soft;
color: $color;
}
.stage.is-demo .label--urgent {
animation: urgent-flash $duration $easing infinite;
}
@keyframes urgent-flash {
0%, 40% { opacity: 1; transform: scale(1); }
55% { opacity: .5; transform: scale(1.05); }
70%, 100% { opacity: 1; transform: scale(1); }
}
04Corner ribbon fold-unfurl
A folded ribbon drops open from the top edge via scaleY around a top transform-origin, then rolls back up — a different move from the light-sweep ribbon in another post.
.ribbon {
clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 82%, 0 100%);
transform: scaleY(0) rotate(0deg);
transform-origin: top center;
}
.stage.is-demo .ribbon {
animation: unfurl $duration $easing infinite;
}
@keyframes unfurl {
0%, 8% { transform: scaleY(0) rotate(0deg); }
30% { transform: scaleY(1.06) rotate(-2deg); }
42%, 78% { transform: scaleY(1) rotate(0deg); }
92%, 100% { transform: scaleY(0) rotate(0deg); }
}
05Notification dot double-pulse
The icon stays still while the corner dot springs twice via scale, reading like two messages landing back to back.
.dot {
background: $color;
box-shadow: 0 0 0 2px $stage-ink;
}
.stage.is-demo .dot {
animation: double-pulse $duration $easing infinite;
}
.stage.is-demo .dot__ring {
animation: double-ring $duration $easing infinite;
}
@keyframes double-pulse {
0%, 100% { transform: scale(1); }
8%, 24% { transform: scale(1.35); }
16%, 32% { transform: scale(1); }
}
@keyframes double-ring {
0%, 6% { opacity: 0; transform: scale(1); }
20% { opacity: .5; transform: scale(2.4); }
34% { opacity: 0; transform: scale(2.6); }
100% { opacity: 0; transform: scale(1); }
}
06Tech-stack tag cloud
Six skill tags rise and fade via opacity and translateY in sequence, driven entirely by per-tag delay values.
.cloud li {
transform: translateY(10px);
opacity: 0;
}
.stage.is-demo .cloud li {
animation: rise $duration $easing infinite;
animation-delay: calc(var(--i) * 90ms);
}
@keyframes rise {
0% { opacity: 0; transform: translateY(10px); }
22%, 70% { opacity: 1; transform: translateY(0); }
92%, 100% { opacity: 0; transform: translateY(-6px); }
}
07Achievement badge unlock animation
A dulled trophy springs back to full color via scale, and a diagonal shine sweeps across it once to mark the moment.
.badge {
filter: grayscale(1) brightness(.85);
transform: scale(.85);
}
.stage.is-demo .badge {
animation: unlock $duration $easing infinite;
}
@keyframes unlock {
0%, 30% { transform: scale(.85); filter: grayscale(1) brightness(.85); }
55% { transform: scale(1.12); filter: grayscale(0) brightness(1); }
70% { transform: scale(.96); }
82%, 100% { transform: scale(1); filter: grayscale(0) brightness(1); }
}
08Premium / verified badge flip
The whole card spins in 3D via rotateY, alternating between a gold Premium face and a blue Verified face.
.flip__inner {
transform-style: preserve-3d;
}
.flip__face {
backface-visibility: hidden;
}
.flip__face--back {
transform: rotateY(180deg);
}
.stage.is-demo .flip__inner {
animation: flip $duration $easing infinite;
}
@keyframes flip {
0%, 40% { transform: rotateY(0deg); }
55%, 85% { transform: rotateY(180deg); }
100% { transform: rotateY(360deg); }
}
09Countdown expiry chip
Ten digits stack vertically and step down one at a time via translateY, reading like a mechanical flip counter.
.chip__digit {
width: 18px; height: 26px;
overflow: hidden;
}
.chip__digit-track span {
height: 26px; line-height: 26px;
}
.stage.is-demo .chip__digit-track {
animation: tick $duration $easing infinite;
}
@keyframes tick {
0% { transform: translateY(0); }
100% { transform: translateY(-260px); }
}
Where it breaks — a digit stuffed into a line of text breaks its box
The countdown chip's ones-place digit first sat inline with the surrounding text, just scaled up to a bigger font-size. The digit spilled outside the chip and sat on the background — the chip is inline-flex, so its line height follows the surrounding text, and without overflow: hidden, a taller inline child simply overflows past the pill's edge. Pulling the digit into its own fixed-size box (18×26px) and adding overflow: hidden to the chip itself kept every digit inside. The zip password is cwww6ahb, and typing it exactly as shown opens all nine folders. The 3D flip badge has a similar trap — skip backface-visibility: hidden on either face, and the back text shows through mirrored mid-spin.
Accessibility
All nine drop their animation under prefers-reduced-motion: reduce and settle on a final state. The online ring keeps just the dot, the achievement badge stays at its unlocked color, and the countdown digit stops advancing. Every state reads from color and shape alone once motion is off. See MDN's prefers-reduced-motion docs for how to set it.
FAQ
Can I recolor these badges?
Yes — most demos take one $color variable at the top of the SCSS. The premium/verified flip (08) is the exception, since each face has its own gradient to edit. More badge ideas live in 9 CSS badge designs.
Does the removable tag chip need JavaScript?
CSS alone can't remove an element. The tag chip (02) ships with about ten lines of vanilla JS that actually deletes it on click; the React version moves the same logic into a couple of useState hooks.
Does the countdown chip track real time?
No — it's a looping demo built to show the motion, not wired to a clock. To count down for real, compare the current time to a target with setInterval and swap in the digit. See About for how these demos get built and checked.