GODRICH

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

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.

opacity+scale ring::before무대 paper
.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.

scale+opacity실제 클릭 제거transitionend
.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.

opacity+scale flashflag Phosphor duotone정적 3단+동적 1단
.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.

scaleY unfurltransform-origin: topclip-path polygon
.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.

scale 더블 스프링@keyframes double-ring무대 ink
.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.

opacity+translateY staggeranimation-delay다크 무대
.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.

filter grayscale→colorscale bounceshine sweep translateX
.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.

rotateY 3D flipbackface-visibilityperspective
.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.

translateY steps(10)@keyframes tickoverflow: hidden window
.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.

Enter the archive password

The password is inside this article. You will find it as you read.