16 CSS Pulse Animations06 / 16
Live Status Dot
A streaming platform / chat app live-status indicator — small neon-lime dot with a soft continuous radiating ring signaling that a stream, user, or server is actively "live" right now. Charcoal (#1a1a1a) streamer-lounge palette with neon lime (#84cc16) status color — Twitch / Discord / OBS-adjacent aesthetic. The dot appears in three real contexts within one demo: header ("12,400 watching"), stream card ("LIVE"), and channel list (with viewer count) — showing the pattern's versatility.
Published
The code
<div class="pl-06">
<header class="pl-06__topbar">
<div class="pl-06__brand">Streamdesk</div>
<div class="pl-06__viewers">
<span class="pl-06__dot" aria-label="Live now"></span>
<span class="pl-06__count">12,400</span>
<span class="pl-06__unit">watching</span>
</div>
</header>
<main class="pl-06__stage">
<div class="pl-06__featured">
<div class="pl-06__thumb">
<span class="pl-06__live-badge">
<span class="pl-06__dot pl-06__dot--md" aria-hidden="true"></span>
<span>LIVE</span>
</span>
<span class="pl-06__thumb-count">
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
8.2k
</span>
</div>
<p class="pl-06__stream-title">Building a game engine in Rust · Day 14</p>
<p class="pl-06__stream-meta">
<strong>silverwing</strong>
<span>·</span>
<span>Programming</span>
<span>·</span>
<span>3h 12m</span>
</p>
</div>
<aside class="pl-06__sidebar">
<p class="pl-06__sidebar-title">Online now</p>
<div class="pl-06__channels">
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true">s</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>silverwing</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">Rust · 8,220</div>
</div>
</div>
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true" style="background:linear-gradient(135deg,#ff6b6b,#f97316)">n</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>nightcrawler</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">FPS · 2,140</div>
</div>
</div>
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true" style="background:linear-gradient(135deg,#a78bfa,#7c3aed)">k</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>kodachi</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">Piano · 640</div>
</div>
</div>
</div>
</aside>
</main>
</div><div class="pl-06">
<header class="pl-06__topbar">
<div class="pl-06__brand">Streamdesk</div>
<div class="pl-06__viewers">
<span class="pl-06__dot" aria-label="Live now"></span>
<span class="pl-06__count">12,400</span>
<span class="pl-06__unit">watching</span>
</div>
</header>
<main class="pl-06__stage">
<div class="pl-06__featured">
<div class="pl-06__thumb">
<span class="pl-06__live-badge">
<span class="pl-06__dot pl-06__dot--md" aria-hidden="true"></span>
<span>LIVE</span>
</span>
<span class="pl-06__thumb-count">
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
8.2k
</span>
</div>
<p class="pl-06__stream-title">Building a game engine in Rust · Day 14</p>
<p class="pl-06__stream-meta">
<strong>silverwing</strong>
<span>·</span>
<span>Programming</span>
<span>·</span>
<span>3h 12m</span>
</p>
</div>
<aside class="pl-06__sidebar">
<p class="pl-06__sidebar-title">Online now</p>
<div class="pl-06__channels">
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true">s</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>silverwing</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">Rust · 8,220</div>
</div>
</div>
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true" style="background:linear-gradient(135deg,#ff6b6b,#f97316)">n</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>nightcrawler</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">FPS · 2,140</div>
</div>
</div>
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true" style="background:linear-gradient(135deg,#a78bfa,#7c3aed)">k</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>kodachi</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">Piano · 640</div>
</div>
</div>
</div>
</aside>
</main>
</div>.pl-06 {
--bg: #1a1a1a;
--surface: #242426;
--surface-hover: #2d2d30;
--ink: #f5f5f0;
--sub: #8a8a90;
--lime: #84cc16;
--lime-dim: rgba(132,204,22,.5);
--line: #333336;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: grid;
grid-template-rows: auto 1fr;
gap: 20px;
padding: 24px;
background: var(--bg);
color: var(--ink);
}
.pl-06 *,
.pl-06 *::before,
.pl-06 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pl-06 ::selection {
background: var(--lime);
color: var(--bg);
}
.pl-06__topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 18px;
border-radius: 10px;
background: var(--surface);
border: 1px solid var(--line);
}
.pl-06__brand {
font-weight: 700;
font-size: .95rem;
letter-spacing: -.02em;
}
.pl-06__viewers {
display: flex;
align-items: center;
gap: 8px;
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .82rem;
}
.pl-06__count {
color: var(--ink);
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.pl-06__unit {
color: var(--sub);
font-family: -apple-system,BlinkMacSystemFont,'Inter',sans-serif;
font-size: .78rem;
}
/* Live status dot — the core pattern */
.pl-06__dot {
position: relative;
flex-shrink: 0;
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--lime);
}
.pl-06__dot::before {
content: '';
position: absolute;
inset: -4px;
border-radius: 50%;
background: var(--lime-dim);
pointer-events: none;
animation: pl-06-ring 1.8s ease-out infinite;
}
.pl-06__dot--md {
width: 6px;
height: 6px;
}
.pl-06__dot--md::before {
inset: -3px;
}
@keyframes pl-06-ring {
0% {
transform: scale(1);
opacity: .55;
}
70% {
transform: scale(2.4);
opacity: 0;
}
100% {
transform: scale(2.6);
opacity: 0;
}
}
.pl-06__stage {
display: grid;
grid-template-columns: 1fr 260px;
gap: 16px;
min-height: 0;
}
@media (max-width: 640px) {
.pl-06__stage {
grid-template-columns: 1fr;
}
}
.pl-06__featured {
padding: 16px;
border-radius: 12px;
background: var(--surface);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
}
.pl-06__thumb {
position: relative;
aspect-ratio: 16/9;
background: linear-gradient(135deg,#3f3f46 0%,#18181b 55%,#0a0a0b 100%);
border-radius: 9px;
margin-bottom: 12px;
}
.pl-06__live-badge {
position: absolute;
top: 10px;
left: 10px;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
border-radius: 5px;
background: rgba(0,0,0,.7);
color: var(--ink);
font-size: .7rem;
font-weight: 800;
letter-spacing: .08em;
backdrop-filter: blur(4px);
}
.pl-06__thumb-count {
position: absolute;
bottom: 10px;
right: 10px;
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 8px;
border-radius: 5px;
background: rgba(0,0,0,.7);
color: var(--ink);
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
font-weight: 700;
backdrop-filter: blur(4px);
}
.pl-06__stream-title {
font-size: .95rem;
font-weight: 700;
letter-spacing: -.01em;
margin-bottom: 6px;
line-height: 1.3;
}
.pl-06__stream-meta {
display: flex;
align-items: center;
gap: 6px;
font-size: .75rem;
color: var(--sub);
}
.pl-06__stream-meta strong {
color: var(--ink);
font-weight: 600;
}
.pl-06__sidebar {
padding: 14px;
border-radius: 12px;
background: var(--surface);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
gap: 10px;
}
.pl-06__sidebar-title {
font-size: .7rem;
font-weight: 700;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--sub);
}
.pl-06__channels {
display: flex;
flex-direction: column;
gap: 6px;
}
.pl-06__channel {
display: flex;
align-items: center;
gap: 10px;
padding: 8px;
border-radius: 8px;
transition: background .15s ease;
cursor: pointer;
}
.pl-06__channel:hover {
background: var(--surface-hover);
}
.pl-06__avatar {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 50%;
flex-shrink: 0;
background: linear-gradient(135deg,#84cc16,#4d7c0f);
color: var(--bg);
font-size: .72rem;
font-weight: 700;
}
.pl-06__channel-info {
flex: 1;
min-width: 0;
}
.pl-06__channel-name {
display: flex;
align-items: center;
gap: 6px;
font-size: .82rem;
font-weight: 600;
}
.pl-06__channel-sub {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .7rem;
color: var(--sub);
margin-top: 1px;
}
@media (prefers-reduced-motion: reduce) {
.pl-06__dot::before {
animation: none!important;
}
}.pl-06 {
--bg: #1a1a1a;
--surface: #242426;
--surface-hover: #2d2d30;
--ink: #f5f5f0;
--sub: #8a8a90;
--lime: #84cc16;
--lime-dim: rgba(132,204,22,.5);
--line: #333336;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: grid;
grid-template-rows: auto 1fr;
gap: 20px;
padding: 24px;
background: var(--bg);
color: var(--ink);
}
.pl-06 *,
.pl-06 *::before,
.pl-06 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pl-06 ::selection {
background: var(--lime);
color: var(--bg);
}
.pl-06__topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 18px;
border-radius: 10px;
background: var(--surface);
border: 1px solid var(--line);
}
.pl-06__brand {
font-weight: 700;
font-size: .95rem;
letter-spacing: -.02em;
}
.pl-06__viewers {
display: flex;
align-items: center;
gap: 8px;
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .82rem;
}
.pl-06__count {
color: var(--ink);
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.pl-06__unit {
color: var(--sub);
font-family: -apple-system,BlinkMacSystemFont,'Inter',sans-serif;
font-size: .78rem;
}
/* Live status dot — the core pattern */
.pl-06__dot {
position: relative;
flex-shrink: 0;
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--lime);
}
.pl-06__dot::before {
content: '';
position: absolute;
inset: -4px;
border-radius: 50%;
background: var(--lime-dim);
pointer-events: none;
animation: pl-06-ring 1.8s ease-out infinite;
}
.pl-06__dot--md {
width: 6px;
height: 6px;
}
.pl-06__dot--md::before {
inset: -3px;
}
@keyframes pl-06-ring {
0% {
transform: scale(1);
opacity: .55;
}
70% {
transform: scale(2.4);
opacity: 0;
}
100% {
transform: scale(2.6);
opacity: 0;
}
}
.pl-06__stage {
display: grid;
grid-template-columns: 1fr 260px;
gap: 16px;
min-height: 0;
}
@media (max-width: 640px) {
.pl-06__stage {
grid-template-columns: 1fr;
}
}
.pl-06__featured {
padding: 16px;
border-radius: 12px;
background: var(--surface);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
}
.pl-06__thumb {
position: relative;
aspect-ratio: 16/9;
background: linear-gradient(135deg,#3f3f46 0%,#18181b 55%,#0a0a0b 100%);
border-radius: 9px;
margin-bottom: 12px;
}
.pl-06__live-badge {
position: absolute;
top: 10px;
left: 10px;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
border-radius: 5px;
background: rgba(0,0,0,.7);
color: var(--ink);
font-size: .7rem;
font-weight: 800;
letter-spacing: .08em;
backdrop-filter: blur(4px);
}
.pl-06__thumb-count {
position: absolute;
bottom: 10px;
right: 10px;
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 8px;
border-radius: 5px;
background: rgba(0,0,0,.7);
color: var(--ink);
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
font-weight: 700;
backdrop-filter: blur(4px);
}
.pl-06__stream-title {
font-size: .95rem;
font-weight: 700;
letter-spacing: -.01em;
margin-bottom: 6px;
line-height: 1.3;
}
.pl-06__stream-meta {
display: flex;
align-items: center;
gap: 6px;
font-size: .75rem;
color: var(--sub);
}
.pl-06__stream-meta strong {
color: var(--ink);
font-weight: 600;
}
.pl-06__sidebar {
padding: 14px;
border-radius: 12px;
background: var(--surface);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
gap: 10px;
}
.pl-06__sidebar-title {
font-size: .7rem;
font-weight: 700;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--sub);
}
.pl-06__channels {
display: flex;
flex-direction: column;
gap: 6px;
}
.pl-06__channel {
display: flex;
align-items: center;
gap: 10px;
padding: 8px;
border-radius: 8px;
transition: background .15s ease;
cursor: pointer;
}
.pl-06__channel:hover {
background: var(--surface-hover);
}
.pl-06__avatar {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 50%;
flex-shrink: 0;
background: linear-gradient(135deg,#84cc16,#4d7c0f);
color: var(--bg);
font-size: .72rem;
font-weight: 700;
}
.pl-06__channel-info {
flex: 1;
min-width: 0;
}
.pl-06__channel-name {
display: flex;
align-items: center;
gap: 6px;
font-size: .82rem;
font-weight: 600;
}
.pl-06__channel-sub {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .7rem;
color: var(--sub);
margin-top: 1px;
}
@media (prefers-reduced-motion: reduce) {
.pl-06__dot::before {
animation: none!important;
}
}Here's a working CSS Pulse Animation 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 Status Dot
Source: https://codefronts.com/motion/css-pulse-animation/live-status-dot/
A streaming platform / chat app live-status indicator — small neon-lime dot with a soft continuous radiating ring signaling that a stream, user, or server is actively "live" right now. Charcoal (#1a1a1a) streamer-lounge palette with neon lime (#84cc16) status color — Twitch / Discord / OBS-adjacent aesthetic. The dot appears in three real contexts within one demo: header ("12,400 watching"), stream card ("LIVE"), and channel list (with viewer count) — showing the pattern's versatility.
## HTML
```html
<div class="pl-06">
<header class="pl-06__topbar">
<div class="pl-06__brand">Streamdesk</div>
<div class="pl-06__viewers">
<span class="pl-06__dot" aria-label="Live now"></span>
<span class="pl-06__count">12,400</span>
<span class="pl-06__unit">watching</span>
</div>
</header>
<main class="pl-06__stage">
<div class="pl-06__featured">
<div class="pl-06__thumb">
<span class="pl-06__live-badge">
<span class="pl-06__dot pl-06__dot--md" aria-hidden="true"></span>
<span>LIVE</span>
</span>
<span class="pl-06__thumb-count">
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
8.2k
</span>
</div>
<p class="pl-06__stream-title">Building a game engine in Rust · Day 14</p>
<p class="pl-06__stream-meta">
<strong>silverwing</strong>
<span>·</span>
<span>Programming</span>
<span>·</span>
<span>3h 12m</span>
</p>
</div>
<aside class="pl-06__sidebar">
<p class="pl-06__sidebar-title">Online now</p>
<div class="pl-06__channels">
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true">s</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>silverwing</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">Rust · 8,220</div>
</div>
</div>
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true" style="background:linear-gradient(135deg,#ff6b6b,#f97316)">n</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>nightcrawler</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">FPS · 2,140</div>
</div>
</div>
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true" style="background:linear-gradient(135deg,#a78bfa,#7c3aed)">k</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>kodachi</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">Piano · 640</div>
</div>
</div>
</div>
</aside>
</main>
</div>
```
## CSS
```css
.pl-06 {
--bg: #1a1a1a;
--surface: #242426;
--surface-hover: #2d2d30;
--ink: #f5f5f0;
--sub: #8a8a90;
--lime: #84cc16;
--lime-dim: rgba(132,204,22,.5);
--line: #333336;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: grid;
grid-template-rows: auto 1fr;
gap: 20px;
padding: 24px;
background: var(--bg);
color: var(--ink);
}
.pl-06 *,
.pl-06 *::before,
.pl-06 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pl-06 ::selection {
background: var(--lime);
color: var(--bg);
}
.pl-06__topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 18px;
border-radius: 10px;
background: var(--surface);
border: 1px solid var(--line);
}
.pl-06__brand {
font-weight: 700;
font-size: .95rem;
letter-spacing: -.02em;
}
.pl-06__viewers {
display: flex;
align-items: center;
gap: 8px;
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .82rem;
}
.pl-06__count {
color: var(--ink);
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.pl-06__unit {
color: var(--sub);
font-family: -apple-system,BlinkMacSystemFont,'Inter',sans-serif;
font-size: .78rem;
}
/* Live status dot — the core pattern */
.pl-06__dot {
position: relative;
flex-shrink: 0;
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--lime);
}
.pl-06__dot::before {
content: '';
position: absolute;
inset: -4px;
border-radius: 50%;
background: var(--lime-dim);
pointer-events: none;
animation: pl-06-ring 1.8s ease-out infinite;
}
.pl-06__dot--md {
width: 6px;
height: 6px;
}
.pl-06__dot--md::before {
inset: -3px;
}
@keyframes pl-06-ring {
0% {
transform: scale(1);
opacity: .55;
}
70% {
transform: scale(2.4);
opacity: 0;
}
100% {
transform: scale(2.6);
opacity: 0;
}
}
.pl-06__stage {
display: grid;
grid-template-columns: 1fr 260px;
gap: 16px;
min-height: 0;
}
@media (max-width: 640px) {
.pl-06__stage {
grid-template-columns: 1fr;
}
}
.pl-06__featured {
padding: 16px;
border-radius: 12px;
background: var(--surface);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
}
.pl-06__thumb {
position: relative;
aspect-ratio: 16/9;
background: linear-gradient(135deg,#3f3f46 0%,#18181b 55%,#0a0a0b 100%);
border-radius: 9px;
margin-bottom: 12px;
}
.pl-06__live-badge {
position: absolute;
top: 10px;
left: 10px;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
border-radius: 5px;
background: rgba(0,0,0,.7);
color: var(--ink);
font-size: .7rem;
font-weight: 800;
letter-spacing: .08em;
backdrop-filter: blur(4px);
}
.pl-06__thumb-count {
position: absolute;
bottom: 10px;
right: 10px;
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 8px;
border-radius: 5px;
background: rgba(0,0,0,.7);
color: var(--ink);
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
font-weight: 700;
backdrop-filter: blur(4px);
}
.pl-06__stream-title {
font-size: .95rem;
font-weight: 700;
letter-spacing: -.01em;
margin-bottom: 6px;
line-height: 1.3;
}
.pl-06__stream-meta {
display: flex;
align-items: center;
gap: 6px;
font-size: .75rem;
color: var(--sub);
}
.pl-06__stream-meta strong {
color: var(--ink);
font-weight: 600;
}
.pl-06__sidebar {
padding: 14px;
border-radius: 12px;
background: var(--surface);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
gap: 10px;
}
.pl-06__sidebar-title {
font-size: .7rem;
font-weight: 700;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--sub);
}
.pl-06__channels {
display: flex;
flex-direction: column;
gap: 6px;
}
.pl-06__channel {
display: flex;
align-items: center;
gap: 10px;
padding: 8px;
border-radius: 8px;
transition: background .15s ease;
cursor: pointer;
}
.pl-06__channel:hover {
background: var(--surface-hover);
}
.pl-06__avatar {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 50%;
flex-shrink: 0;
background: linear-gradient(135deg,#84cc16,#4d7c0f);
color: var(--bg);
font-size: .72rem;
font-weight: 700;
}
.pl-06__channel-info {
flex: 1;
min-width: 0;
}
.pl-06__channel-name {
display: flex;
align-items: center;
gap: 6px;
font-size: .82rem;
font-weight: 600;
}
.pl-06__channel-sub {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .7rem;
color: var(--sub);
margin-top: 1px;
}
@media (prefers-reduced-motion: reduce) {
.pl-06__dot::before {
animation: none!important;
}
}
```Here's a working CSS Pulse Animation 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 Status Dot
Source: https://codefronts.com/motion/css-pulse-animation/live-status-dot/
A streaming platform / chat app live-status indicator — small neon-lime dot with a soft continuous radiating ring signaling that a stream, user, or server is actively "live" right now. Charcoal (#1a1a1a) streamer-lounge palette with neon lime (#84cc16) status color — Twitch / Discord / OBS-adjacent aesthetic. The dot appears in three real contexts within one demo: header ("12,400 watching"), stream card ("LIVE"), and channel list (with viewer count) — showing the pattern's versatility.
## HTML
```html
<div class="pl-06">
<header class="pl-06__topbar">
<div class="pl-06__brand">Streamdesk</div>
<div class="pl-06__viewers">
<span class="pl-06__dot" aria-label="Live now"></span>
<span class="pl-06__count">12,400</span>
<span class="pl-06__unit">watching</span>
</div>
</header>
<main class="pl-06__stage">
<div class="pl-06__featured">
<div class="pl-06__thumb">
<span class="pl-06__live-badge">
<span class="pl-06__dot pl-06__dot--md" aria-hidden="true"></span>
<span>LIVE</span>
</span>
<span class="pl-06__thumb-count">
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
8.2k
</span>
</div>
<p class="pl-06__stream-title">Building a game engine in Rust · Day 14</p>
<p class="pl-06__stream-meta">
<strong>silverwing</strong>
<span>·</span>
<span>Programming</span>
<span>·</span>
<span>3h 12m</span>
</p>
</div>
<aside class="pl-06__sidebar">
<p class="pl-06__sidebar-title">Online now</p>
<div class="pl-06__channels">
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true">s</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>silverwing</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">Rust · 8,220</div>
</div>
</div>
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true" style="background:linear-gradient(135deg,#ff6b6b,#f97316)">n</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>nightcrawler</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">FPS · 2,140</div>
</div>
</div>
<div class="pl-06__channel">
<span class="pl-06__avatar" aria-hidden="true" style="background:linear-gradient(135deg,#a78bfa,#7c3aed)">k</span>
<div class="pl-06__channel-info">
<div class="pl-06__channel-name">
<span>kodachi</span>
<span class="pl-06__dot" aria-label="Live"></span>
</div>
<div class="pl-06__channel-sub">Piano · 640</div>
</div>
</div>
</div>
</aside>
</main>
</div>
```
## CSS
```css
.pl-06 {
--bg: #1a1a1a;
--surface: #242426;
--surface-hover: #2d2d30;
--ink: #f5f5f0;
--sub: #8a8a90;
--lime: #84cc16;
--lime-dim: rgba(132,204,22,.5);
--line: #333336;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: grid;
grid-template-rows: auto 1fr;
gap: 20px;
padding: 24px;
background: var(--bg);
color: var(--ink);
}
.pl-06 *,
.pl-06 *::before,
.pl-06 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pl-06 ::selection {
background: var(--lime);
color: var(--bg);
}
.pl-06__topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 18px;
border-radius: 10px;
background: var(--surface);
border: 1px solid var(--line);
}
.pl-06__brand {
font-weight: 700;
font-size: .95rem;
letter-spacing: -.02em;
}
.pl-06__viewers {
display: flex;
align-items: center;
gap: 8px;
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .82rem;
}
.pl-06__count {
color: var(--ink);
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.pl-06__unit {
color: var(--sub);
font-family: -apple-system,BlinkMacSystemFont,'Inter',sans-serif;
font-size: .78rem;
}
/* Live status dot — the core pattern */
.pl-06__dot {
position: relative;
flex-shrink: 0;
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--lime);
}
.pl-06__dot::before {
content: '';
position: absolute;
inset: -4px;
border-radius: 50%;
background: var(--lime-dim);
pointer-events: none;
animation: pl-06-ring 1.8s ease-out infinite;
}
.pl-06__dot--md {
width: 6px;
height: 6px;
}
.pl-06__dot--md::before {
inset: -3px;
}
@keyframes pl-06-ring {
0% {
transform: scale(1);
opacity: .55;
}
70% {
transform: scale(2.4);
opacity: 0;
}
100% {
transform: scale(2.6);
opacity: 0;
}
}
.pl-06__stage {
display: grid;
grid-template-columns: 1fr 260px;
gap: 16px;
min-height: 0;
}
@media (max-width: 640px) {
.pl-06__stage {
grid-template-columns: 1fr;
}
}
.pl-06__featured {
padding: 16px;
border-radius: 12px;
background: var(--surface);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
}
.pl-06__thumb {
position: relative;
aspect-ratio: 16/9;
background: linear-gradient(135deg,#3f3f46 0%,#18181b 55%,#0a0a0b 100%);
border-radius: 9px;
margin-bottom: 12px;
}
.pl-06__live-badge {
position: absolute;
top: 10px;
left: 10px;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
border-radius: 5px;
background: rgba(0,0,0,.7);
color: var(--ink);
font-size: .7rem;
font-weight: 800;
letter-spacing: .08em;
backdrop-filter: blur(4px);
}
.pl-06__thumb-count {
position: absolute;
bottom: 10px;
right: 10px;
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 8px;
border-radius: 5px;
background: rgba(0,0,0,.7);
color: var(--ink);
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
font-weight: 700;
backdrop-filter: blur(4px);
}
.pl-06__stream-title {
font-size: .95rem;
font-weight: 700;
letter-spacing: -.01em;
margin-bottom: 6px;
line-height: 1.3;
}
.pl-06__stream-meta {
display: flex;
align-items: center;
gap: 6px;
font-size: .75rem;
color: var(--sub);
}
.pl-06__stream-meta strong {
color: var(--ink);
font-weight: 600;
}
.pl-06__sidebar {
padding: 14px;
border-radius: 12px;
background: var(--surface);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
gap: 10px;
}
.pl-06__sidebar-title {
font-size: .7rem;
font-weight: 700;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--sub);
}
.pl-06__channels {
display: flex;
flex-direction: column;
gap: 6px;
}
.pl-06__channel {
display: flex;
align-items: center;
gap: 10px;
padding: 8px;
border-radius: 8px;
transition: background .15s ease;
cursor: pointer;
}
.pl-06__channel:hover {
background: var(--surface-hover);
}
.pl-06__avatar {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 50%;
flex-shrink: 0;
background: linear-gradient(135deg,#84cc16,#4d7c0f);
color: var(--bg);
font-size: .72rem;
font-weight: 700;
}
.pl-06__channel-info {
flex: 1;
min-width: 0;
}
.pl-06__channel-name {
display: flex;
align-items: center;
gap: 6px;
font-size: .82rem;
font-weight: 600;
}
.pl-06__channel-sub {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .7rem;
color: var(--sub);
margin-top: 1px;
}
@media (prefers-reduced-motion: reduce) {
.pl-06__dot::before {
animation: none!important;
}
}
```How this works
The core dot pattern is a single <span> with a fixed 8px width/height, border-radius: 50%, and a lime background. A single ::before pseudo-element positioned inset: -4px (extending 4px beyond the dot in every direction) has the same border-radius and a subtle lime background, then animates via @keyframes pl-06-ring which cycles transform: scale(1) → scale(2.6) while fading opacity: .55 → 0 over 1.8 seconds. The continuous outward ring gives the dot presence without being obnoxious — critical for chat lists where 20+ live indicators may share a page.
Why the pulse is subtle (small scale range, muted opacity): live indicators are UBIQUITOUS in streaming apps — 20-50 of them may render in one Discord channel list. If each pulse were dramatic (scale 3.5+, opacity .8), the page would flash like a Vegas strip. The tight 2.6× scale + .55 alpha reads as "gently alive" without competing for attention.
Streamer / dark-mode palette: deep charcoal (#1a1a1a) background with lighter charcoal (#242426) card surfaces, neon lime (#84cc16) status color, JetBrains Mono for viewer count numerals (matches the Twitch/OBS aesthetic where technical counts get monospace treatment). The header bar shows total viewers with a live dot; the featured stream card shows LIVE badge with viewer count; the channel list shows 3 online users each with their own live dot.
Semantic HTML uses <span aria-label="Live now"> so screen readers announce "live now" instead of skipping the visual-only dot. Text next to the dot ("12,400 watching") reinforces the state for sighted users without redundancy for screen readers because the dot's aria-label is different ("Live now" vs. the visible count).
Make it yours
- Change status color per state — lime for live streaming, cyan for typing, amber for away, red for do-not-disturb. Map to your presence taxonomy.
- Adjust pulse speed by editing
animation-duration: 1.8s— slower (2.4s) for ambient always-on presence, faster (1.2s) for urgent live-notification contexts. Below 1s reads as "broken". - For a monochrome variant (news/ops platforms), use a single desaturated color — white dot on dark, dark dot on light — and rely purely on the pulse motion to signal "live" without color coding.
- Add a viewer/listener count next to the dot for streaming contexts ("⬤ 12,400 watching", "⬤ LIVE · 3 in call") — the count gives users a signal beyond the binary live/offline.
- For a triple-ring cascade (dramatic live-broadcast notification), add two more pseudo-element rings with staggered delays (
animation-delay: .6s,animation-delay: 1.2s).
Gotchas — read before shipping
- The pulse ring uses
transform: scalebecause the dot is a perfect circle — scale preserves the circle shape perfectly. For rectangular status badges ("LIVE" pill), switch tobox-shadowoutward-cast to preserve border-radius (see Demo #01). - Do NOT stack 30+ concurrent live-dot pulses on one page without
IntersectionObserverpause — each pulse gets its own compositor layer, and browsers cap active layers around 50-100. Pause off-screen pulses to stay under the cap. - Semantic role: use
aria-label="Live"on the dot ONLY if there's no adjacent text conveying live-ness. If text says "LIVE · 12,400 watching", the dot should bearia-hidden="true"— otherwise screen readers announce "live" twice. - For color-blind users (deuteranopia, protanopia), the lime status color reads as yellow-green — indistinguishable from a warning/amber state. Pair the color with a motion signal (the pulse) and text ("LIVE") so state is conveyed by 3 channels: color + motion + text. Never rely on color alone.
- For platforms with mixed dark/light modes, the lime color needs adjustment — pure
#84cc16is fine on dark charcoal, but on white it looks muddy. Use#65a30d(darker lime) in light mode for equivalent contrast. - The pulse animation is informational (signaling "live now"), not decorative — so in
prefers-reduced-motion, do NOT hide the dot entirely. Keep the dot visible in its saturated color; only disable the animated ring. The information (this stream is live) must still be conveyed.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 76+ | 9+ | 103+ | 76+ |
Floor set by backdrop-filter as this demo is written. The core technique itself goes back further — Chrome 45+.
transform + opacity + @keyframes. Universal, GPU-accelerated.