24 CSS Tags & Chips16 / 24

CSS onlyMIT licensed

Live Pulsing Status Badge CSS

LIVE, REC, ON AIR — broadcast badges with a pulse that costs nothing: the ping is a ::before ring animating only scale and opacity (compositor-side, 60fps with 200 badges on screen), and the demo explicitly contrasts it with the box-shadow-spread pulse people copy from old tutorials, which repaints every frame. Includes the double-ring sonar, the REC blink on steps(), a breathing ambient glow, and the viewer-count pill with tabular numerals so digits don't jitter.

Published Updated

Live Demo
Try it

The code

<section class="ctc-16" aria-label="Live pulsing status badge demo">
  <div class="ctc-16__stage">
    <div class="ctc-16__wrap">
      <header class="ctc-16__head">
        <p class="ctc-16__kicker">Pulse engineering · scale + opacity only</p>
        <h1>Alive at 60fps, even ×200</h1>
      </header>
      <div class="ctc-16__panel">
        <p class="ctc-16__lab">01 · The broadcast set</p>
        <div class="ctc-16__row">
          <span class="ctc-16__badge ctc-16__badge--live"><i class="ctc-16__dot"></i>LIVE</span>
          <span class="ctc-16__badge ctc-16__badge--rec"><i class="ctc-16__dot ctc-16__dot--blink"></i>REC 00:14:32</span>
          <span class="ctc-16__badge ctc-16__badge--air"><i class="ctc-16__dot ctc-16__dot--twin"></i>ON AIR</span>
          <span class="ctc-16__badge ctc-16__badge--view">👁 <b>1,204</b> watching</span>
        </div>
      </div>
      <div class="ctc-16__panel">
        <p class="ctc-16__lab">02 · Perf comparison — same look, different bill</p>
        <div class="ctc-16__row ctc-16__row--split">
          <div><span class="ctc-16__badge ctc-16__badge--live"><i class="ctc-16__dot"></i>transform ping</span><small>scale+opacity · compositor · ✓ ship it</small></div>
          <div><span class="ctc-16__badge ctc-16__badge--live"><i class="ctc-16__dot ctc-16__dot--shadow"></i>box-shadow pulse</span><small>spread anim · repaints every frame · ✗ lists</small></div>
        </div>
      </div>
      <div class="ctc-16__panel">
        <p class="ctc-16__lab">03 · Status board — pulse only what's actually happening</p>
        <ul class="ctc-16__board">
          <li><i class="ctc-16__dot ctc-16__dot--ok"></i><b>API</b><span>Operational</span><small>99.99%</small></li>
          <li><i class="ctc-16__dot ctc-16__dot--warn"></i><b>Webhooks</b><span>Degraded — retrying</span><small>97.2%</small></li>
          <li class="is-incident"><i class="ctc-16__dot"></i><b>EU database</b><span>Incident · failover running</span><small>—</small></li>
          <li><i class="ctc-16__dot ctc-16__dot--off"></i><b>Legacy v1</b><span>Deprecated</span><small>frozen</small></li>
        </ul>
      </div>
      <p class="ctc-16__note">The ring is <code>background:inherit</code> on <code>::before</code>, animating <code>scale</code> 1→2.6 and <code>opacity</code> .8→0 on an ease-out — energy dissipates, so the ring must decelerate.</p>
    </div>
  </div>
</section>
.ctc-16,
.ctc-16 *,
.ctc-16 *::before,
.ctc-16 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ctc-16 {
  --bg: oklch(0.19 0.015 270);
  --panel: oklch(0.235 0.018 270);
  --ink: oklch(0.95 0.008 270);
  --mut: oklch(0.64 0.02 270);
  --line: oklch(0.32 0.02 270);
  --red: oklch(0.62 0.23 25);
  --ok: oklch(0.74 0.18 150);
  --warn: oklch(0.8 0.15 85);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
  container-type: inline-size;
}

.ctc-16__stage {
  width: 100%;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  display: grid;
  place-items: center;
  padding: clamp(16px,4cqi,48px);
  background: radial-gradient(70cqi 55cqi at 85% -10%,oklch(0.3 0.08 25/.4),transparent 62%),var(--bg);
  container: ctc16/inline-size;
}

.ctc-16__wrap {
  width: min(680px,100%);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.ctc-16__kicker {
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: oklch(0.72 0.18 25);
}

.ctc-16__head h1 {
  font-size: clamp(24px,3.8cqi,38px);
  font-weight: 800;
  letter-spacing: -.03em;
  margin: 8px 0 8px;
}

.ctc-16__panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 16px 18px;
}

.ctc-16__lab {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--mut);
  margin-bottom: 14px;
}

.ctc-16__row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

.ctc-16__badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 18px;
  border-radius: 99px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .08em;
  border: 1px solid var(--line);
  background: oklch(1 0 0/.05);
}

.ctc-16__badge--live {
  color: oklch(0.8 0.16 25);
  border-color: color-mix(in oklch,var(--red) 45%,transparent);
  background: color-mix(in oklch,var(--red) 14%,transparent);
}

.ctc-16__badge--rec {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.ctc-16__badge--air {
  color: oklch(0.85 0.1 85);
  border-color: color-mix(in oklch,var(--warn) 40%,transparent);
  background: color-mix(in oklch,var(--warn) 10%,transparent);
}

.ctc-16__badge--view {
  letter-spacing: 0;
  font-weight: 600;
  color: var(--mut);
}

.ctc-16__badge--view b {
  font-weight: 800;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.ctc-16__dot {
  position: relative;
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--red);
}

.ctc-16__dot::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: inherit;
  animation: ctc16-ping 1.6s cubic-bezier(0,0,.2,1) infinite;
}

@keyframes ctc16-ping {
  0% {
    scale: 1;
    opacity: .8;
  }

  100% {
    scale: 2.6;
    opacity: 0;
  }
}

.ctc-16__dot--blink {
  animation: ctc16-blink 1s steps(2,jump-none) infinite;
}

.ctc-16__dot--blink::before {
  animation: none;
}

@keyframes ctc16-blink {
  50% {
    opacity: .25;
  }
}

.ctc-16__dot--twin {
  background: var(--warn);
}

.ctc-16__dot--twin::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: inherit;
  animation: ctc16-ping 1.6s cubic-bezier(0,0,.2,1) .8s infinite;
}

.ctc-16__dot--shadow::before {
  animation: ctc16-shadow 1.6s ease-out infinite;
  scale: 1 !important;
  background: transparent;
}

@keyframes ctc16-shadow {
  0% {
    box-shadow: 0 0 0 0 oklch(0.62 0.23 25/.7);
  }

  100% {
    box-shadow: 0 0 0 12px oklch(0.62 0.23 25/0);
  }
}

.ctc-16__dot--ok {
  background: var(--ok);
}

.ctc-16__dot--ok::before {
  animation: none;
}

.ctc-16__dot--warn {
  background: var(--warn);
}

.ctc-16__dot--warn::before {
  animation: none;
}

.ctc-16__dot--off {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--mut);
}

.ctc-16__dot--off::before {
  animation: none;
}

.ctc-16__row--split {
  gap: 20px;
}

.ctc-16__row--split>div {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.ctc-16__row--split small {
  font-size: 11px;
  color: var(--mut);
}

.ctc-16__board {
  list-style: none;
  display: flex;
  flex-direction: column;
}

.ctc-16__board li {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  gap: 12px;
  padding: 11px 12px;
  border-radius: 10px;
}

.ctc-16__board li.is-incident {
  background: color-mix(in oklch,var(--red) 9%,transparent);
}

.ctc-16__board b {
  font-size: 13.5px;
  font-weight: 700;
}

.ctc-16__board span {
  font-size: 12px;
  color: var(--mut);
  justify-self: end;
}

.ctc-16__board li.is-incident span {
  color: oklch(0.8 0.16 25);
  font-weight: 700;
}

.ctc-16__board small {
  font-size: 11.5px;
  color: var(--mut);
  font-variant-numeric: tabular-nums;
  min-width: 52px;
  text-align: right;
}

.ctc-16__note {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--mut);
  border-left: 3px solid var(--red);
  padding-left: 12px;
}

.ctc-16__note code {
  font-family: ui-monospace,Menlo,monospace;
  font-size: .9em;
  background: oklch(1 0 0/.09);
  padding: 1px 5px;
  border-radius: 4px;
  color: var(--ink);
}

@container ctc16 (width < 520px) {
  .ctc-16__board li {
    grid-template-columns: auto 1fr auto;
  }

  .ctc-16__board small {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ctc-16__dot::before,
    .ctc-16__dot::after,
    .ctc-16__dot--blink {
    animation: none !important;
    opacity: 1;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Tags & Chip from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Live Pulsing Status Badge CSS
Source: https://codefronts.com/snippets/css-tags-chips/status-pulse/

LIVE, REC, ON AIR — broadcast badges with a pulse that costs nothing: the ping is a ::before ring animating only scale and opacity (compositor-side, 60fps with 200 badges on screen), and the demo explicitly contrasts it with the box-shadow-spread pulse people copy from old tutorials, which repaints every frame. Includes the double-ring sonar, the REC blink on steps(), a breathing ambient glow, and the viewer-count pill with tabular numerals so digits don't jitter.
## HTML
```html
<section class="ctc-16" aria-label="Live pulsing status badge demo">
  <div class="ctc-16__stage">
    <div class="ctc-16__wrap">
      <header class="ctc-16__head">
        <p class="ctc-16__kicker">Pulse engineering · scale + opacity only</p>
        <h1>Alive at 60fps, even ×200</h1>
      </header>
      <div class="ctc-16__panel">
        <p class="ctc-16__lab">01 · The broadcast set</p>
        <div class="ctc-16__row">
          <span class="ctc-16__badge ctc-16__badge--live"><i class="ctc-16__dot"></i>LIVE</span>
          <span class="ctc-16__badge ctc-16__badge--rec"><i class="ctc-16__dot ctc-16__dot--blink"></i>REC 00:14:32</span>
          <span class="ctc-16__badge ctc-16__badge--air"><i class="ctc-16__dot ctc-16__dot--twin"></i>ON AIR</span>
          <span class="ctc-16__badge ctc-16__badge--view">👁 <b>1,204</b> watching</span>
        </div>
      </div>
      <div class="ctc-16__panel">
        <p class="ctc-16__lab">02 · Perf comparison — same look, different bill</p>
        <div class="ctc-16__row ctc-16__row--split">
          <div><span class="ctc-16__badge ctc-16__badge--live"><i class="ctc-16__dot"></i>transform ping</span><small>scale+opacity · compositor · ✓ ship it</small></div>
          <div><span class="ctc-16__badge ctc-16__badge--live"><i class="ctc-16__dot ctc-16__dot--shadow"></i>box-shadow pulse</span><small>spread anim · repaints every frame · ✗ lists</small></div>
        </div>
      </div>
      <div class="ctc-16__panel">
        <p class="ctc-16__lab">03 · Status board — pulse only what's actually happening</p>
        <ul class="ctc-16__board">
          <li><i class="ctc-16__dot ctc-16__dot--ok"></i><b>API</b><span>Operational</span><small>99.99%</small></li>
          <li><i class="ctc-16__dot ctc-16__dot--warn"></i><b>Webhooks</b><span>Degraded — retrying</span><small>97.2%</small></li>
          <li class="is-incident"><i class="ctc-16__dot"></i><b>EU database</b><span>Incident · failover running</span><small>—</small></li>
          <li><i class="ctc-16__dot ctc-16__dot--off"></i><b>Legacy v1</b><span>Deprecated</span><small>frozen</small></li>
        </ul>
      </div>
      <p class="ctc-16__note">The ring is <code>background:inherit</code> on <code>::before</code>, animating <code>scale</code> 1→2.6 and <code>opacity</code> .8→0 on an ease-out — energy dissipates, so the ring must decelerate.</p>
    </div>
  </div>
</section>
```
## CSS
```css
.ctc-16,
.ctc-16 *,
.ctc-16 *::before,
.ctc-16 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ctc-16 {
  --bg: oklch(0.19 0.015 270);
  --panel: oklch(0.235 0.018 270);
  --ink: oklch(0.95 0.008 270);
  --mut: oklch(0.64 0.02 270);
  --line: oklch(0.32 0.02 270);
  --red: oklch(0.62 0.23 25);
  --ok: oklch(0.74 0.18 150);
  --warn: oklch(0.8 0.15 85);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
  container-type: inline-size;
}

.ctc-16__stage {
  width: 100%;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  display: grid;
  place-items: center;
  padding: clamp(16px,4cqi,48px);
  background: radial-gradient(70cqi 55cqi at 85% -10%,oklch(0.3 0.08 25/.4),transparent 62%),var(--bg);
  container: ctc16/inline-size;
}

.ctc-16__wrap {
  width: min(680px,100%);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.ctc-16__kicker {
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: oklch(0.72 0.18 25);
}

.ctc-16__head h1 {
  font-size: clamp(24px,3.8cqi,38px);
  font-weight: 800;
  letter-spacing: -.03em;
  margin: 8px 0 8px;
}

.ctc-16__panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 16px 18px;
}

.ctc-16__lab {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--mut);
  margin-bottom: 14px;
}

.ctc-16__row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

.ctc-16__badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 18px;
  border-radius: 99px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .08em;
  border: 1px solid var(--line);
  background: oklch(1 0 0/.05);
}

.ctc-16__badge--live {
  color: oklch(0.8 0.16 25);
  border-color: color-mix(in oklch,var(--red) 45%,transparent);
  background: color-mix(in oklch,var(--red) 14%,transparent);
}

.ctc-16__badge--rec {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.ctc-16__badge--air {
  color: oklch(0.85 0.1 85);
  border-color: color-mix(in oklch,var(--warn) 40%,transparent);
  background: color-mix(in oklch,var(--warn) 10%,transparent);
}

.ctc-16__badge--view {
  letter-spacing: 0;
  font-weight: 600;
  color: var(--mut);
}

.ctc-16__badge--view b {
  font-weight: 800;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.ctc-16__dot {
  position: relative;
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--red);
}

.ctc-16__dot::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: inherit;
  animation: ctc16-ping 1.6s cubic-bezier(0,0,.2,1) infinite;
}

@keyframes ctc16-ping {
  0% {
    scale: 1;
    opacity: .8;
  }

  100% {
    scale: 2.6;
    opacity: 0;
  }
}

.ctc-16__dot--blink {
  animation: ctc16-blink 1s steps(2,jump-none) infinite;
}

.ctc-16__dot--blink::before {
  animation: none;
}

@keyframes ctc16-blink {
  50% {
    opacity: .25;
  }
}

.ctc-16__dot--twin {
  background: var(--warn);
}

.ctc-16__dot--twin::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: inherit;
  animation: ctc16-ping 1.6s cubic-bezier(0,0,.2,1) .8s infinite;
}

.ctc-16__dot--shadow::before {
  animation: ctc16-shadow 1.6s ease-out infinite;
  scale: 1 !important;
  background: transparent;
}

@keyframes ctc16-shadow {
  0% {
    box-shadow: 0 0 0 0 oklch(0.62 0.23 25/.7);
  }

  100% {
    box-shadow: 0 0 0 12px oklch(0.62 0.23 25/0);
  }
}

.ctc-16__dot--ok {
  background: var(--ok);
}

.ctc-16__dot--ok::before {
  animation: none;
}

.ctc-16__dot--warn {
  background: var(--warn);
}

.ctc-16__dot--warn::before {
  animation: none;
}

.ctc-16__dot--off {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--mut);
}

.ctc-16__dot--off::before {
  animation: none;
}

.ctc-16__row--split {
  gap: 20px;
}

.ctc-16__row--split>div {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.ctc-16__row--split small {
  font-size: 11px;
  color: var(--mut);
}

.ctc-16__board {
  list-style: none;
  display: flex;
  flex-direction: column;
}

.ctc-16__board li {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  gap: 12px;
  padding: 11px 12px;
  border-radius: 10px;
}

.ctc-16__board li.is-incident {
  background: color-mix(in oklch,var(--red) 9%,transparent);
}

.ctc-16__board b {
  font-size: 13.5px;
  font-weight: 700;
}

.ctc-16__board span {
  font-size: 12px;
  color: var(--mut);
  justify-self: end;
}

.ctc-16__board li.is-incident span {
  color: oklch(0.8 0.16 25);
  font-weight: 700;
}

.ctc-16__board small {
  font-size: 11.5px;
  color: var(--mut);
  font-variant-numeric: tabular-nums;
  min-width: 52px;
  text-align: right;
}

.ctc-16__note {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--mut);
  border-left: 3px solid var(--red);
  padding-left: 12px;
}

.ctc-16__note code {
  font-family: ui-monospace,Menlo,monospace;
  font-size: .9em;
  background: oklch(1 0 0/.09);
  padding: 1px 5px;
  border-radius: 4px;
  color: var(--ink);
}

@container ctc16 (width < 520px) {
  .ctc-16__board li {
    grid-template-columns: auto 1fr auto;
  }

  .ctc-16__board small {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ctc-16__dot::before,
    .ctc-16__dot::after,
    .ctc-16__dot--blink {
    animation: none !important;
    opacity: 1;
  }
}
```

How this works

The canonical ping is one pseudo-element and two compositor properties:

.dot::before{ content:""; position:absolute; inset:0;
  border-radius:50%; background:inherit;
  animation:ctc16-ping 1.6s cubic-bezier(0,0,.2,1) infinite; }
@keyframes ctc16-ping{
  0%{ scale:1; opacity:.8 } 100%{ scale:2.6; opacity:0 } }

scale and opacity never touch layout or paint — the browser transforms a cached layer, which is why this survives a grid of hundreds. The tempting alternative, animating box-shadow: 0 0 0 12px spread, produces the identical visual but repaints the badge's whole damage rectangle every frame; the demo shows both so the difference is a DevTools paint-flash away. The ease-out curve matters too: sonar energy dissipates, so the ring must decelerate — a linear ping looks mechanical.

Around that core: the REC badge blinks its dot with steps(2, jump-none) (broadcast equipment blinks, it does not fade); the double-ring variant runs the same ping on ::before and ::after with a .8s stagger for urgency; the breathing glow animates opacity on a pre-blurred halo (never the blur radius). Every badge pairs its color with a text label — 'LIVE', '1,204 watching' — because a red dot alone is invisible to roughly 1 in 12 of your male users. Under reduced motion, all pulses stop but every label and color stays.

Make it yours

  • Tempo: 1.6s is calm authority; 1s reads as alarm. Match the double-ring stagger to half the duration or the rings clump.
  • Reach: the end scale (2.6). Keep the ring inside the badge's padding or give the dot margin — a clipped ping looks broken.
  • Palette: broadcast red oklch(0.62 0.23 25) for LIVE/REC, green 150 for 'operational', amber 85 for degraded — the demo's status board shows all three coexisting.
  • Density: for a status table, drop the ping and keep only the steady dot + label; reserve pulse for the one row that is genuinely live.

Gotchas — read before shipping

  • background:inherit on the ::before ring keeps ring and dot in perfect sync through theme changes — hardcode the color twice and they drift apart on the first redesign.
  • Never animate box-shadow spread for pulses in lists — identical look, full repaint per frame. Toggle DevTools paint flashing on this demo's comparison pair and watch.
  • An infinite pulse on 30 rows makes the page feel broken, not alive — the demo's status board pulses only the incident row.
  • Keep content out of the ::before ring — screen readers ignore it, which is correct: the pulse is decoration, the label is the information.

Browser support

ChromeSafariFirefoxEdge
111+16.4+113+111+

scale/opacity keyframes are universal; steps() easing likewise. The floor is oklch()/color-mix() tokens. tabular-nums needs any font with the feature — system stacks all qualify.

Search CodeFronts

Loading…