16 CSS Pulse Animations08 / 16
Server Status Ring
A DevOps operations-center dashboard with server-status rings — 4 tiles each showing a service, its uptime, and a concentric pulsing ring indicating health status. Terminal-green healthy state (#22c55e) with amber warning (#f59e0b) tile mixed in for realism. Datadog / PagerDuty / Grafana ops-center aesthetic with all-uppercase JetBrains Mono labels, deep-forest surface (#0a3a1f), and hostname / region metadata.
Published
The code
<div class="pl-08">
<header class="pl-08__head">
<div class="pl-08__brand">
<span class="pl-08__logo" aria-hidden="true">◆</span>
<span>opswatch</span>
</div>
<div class="pl-08__region-strip">
<span class="pl-08__region-item">us-east-1</span>
<span class="pl-08__region-item pl-08__region-item--sel">eu-west-2</span>
<span class="pl-08__region-item">ap-south-1</span>
</div>
<div class="pl-08__meta">
<span class="pl-08__meta-dot" aria-label="Live"></span>
<span>Live · 14:22 UTC</span>
</div>
</header>
<main class="pl-08__grid">
<article class="pl-08__tile" role="group" aria-label="api-gateway, uptime 99.98%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.98<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">api-gateway</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 142ms</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="auth-service, uptime 99.99%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.99<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">auth-service</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 38ms</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="billing-svc, uptime 98.42%, degraded">
<div class="pl-08__ring pl-08__ring--warn">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">98.42<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">billing-svc</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--warn">DEGRADED</span>
<span>p95 · 890ms ↑</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="search-index, uptime 99.94%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.94<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">search-index</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 76ms</span>
</div>
</article>
</main>
</div><div class="pl-08">
<header class="pl-08__head">
<div class="pl-08__brand">
<span class="pl-08__logo" aria-hidden="true">◆</span>
<span>opswatch</span>
</div>
<div class="pl-08__region-strip">
<span class="pl-08__region-item">us-east-1</span>
<span class="pl-08__region-item pl-08__region-item--sel">eu-west-2</span>
<span class="pl-08__region-item">ap-south-1</span>
</div>
<div class="pl-08__meta">
<span class="pl-08__meta-dot" aria-label="Live"></span>
<span>Live · 14:22 UTC</span>
</div>
</header>
<main class="pl-08__grid">
<article class="pl-08__tile" role="group" aria-label="api-gateway, uptime 99.98%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.98<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">api-gateway</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 142ms</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="auth-service, uptime 99.99%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.99<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">auth-service</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 38ms</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="billing-svc, uptime 98.42%, degraded">
<div class="pl-08__ring pl-08__ring--warn">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">98.42<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">billing-svc</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--warn">DEGRADED</span>
<span>p95 · 890ms ↑</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="search-index, uptime 99.94%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.94<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">search-index</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 76ms</span>
</div>
</article>
</main>
</div>.pl-08 {
--bg: #0a3a1f;
--card: #08301a;
--card-hover: #0c4025;
--ink: #d1fae5;
--sub: #6ee7b7;
--ok: #22c55e;
--ok-dim: rgba(34,197,94,.35);
--warn: #f59e0b;
--warn-dim: rgba(245,158,11,.4);
--line: rgba(34,197,94,.14);
font-family: 'JetBrains Mono',ui-monospace,'Menlo','Consolas',monospace;
width: 100%;
min-height: 100vh;
position: relative;
display: grid;
grid-template-rows: auto 1fr;
gap: 16px;
padding: 24px;
background: radial-gradient(60% 100% at 20% 10%, rgba(34,197,94,.06) 0%, transparent 60%), var(--bg);
color: var(--ink);
}
.pl-08 *,
.pl-08 *::before,
.pl-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pl-08 ::selection {
background: var(--ok);
color: var(--bg);
}
.pl-08__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 12px 18px;
border-radius: 9px;
background: var(--card);
border: 1px solid var(--line);
}
.pl-08__brand {
display: flex;
align-items: center;
gap: 8px;
font-size: .82rem;
font-weight: 700;
letter-spacing: .06em;
text-transform: uppercase;
}
.pl-08__logo {
color: var(--ok);
font-size: .9rem;
}
.pl-08__region-strip {
display: flex;
gap: 4px;
}
.pl-08__region-item {
padding: 5px 10px;
border-radius: 5px;
font-size: .72rem;
font-weight: 600;
letter-spacing: .04em;
color: var(--sub);
cursor: pointer;
transition: background .15s ease;
}
.pl-08__region-item:hover {
background: var(--card-hover);
}
.pl-08__region-item--sel {
background: var(--card-hover);
color: var(--ink);
}
.pl-08__meta {
display: flex;
align-items: center;
gap: 8px;
font-size: .74rem;
color: var(--sub);
font-variant-numeric: tabular-nums;
}
.pl-08__meta-dot {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--ok);
animation: pl-08-blink 1.4s ease-in-out infinite;
}
@keyframes pl-08-blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: .4;
}
}
.pl-08__grid {
display: grid;
grid-template-columns: repeat(4,1fr);
gap: 12px;
}
@media (max-width: 820px) {
.pl-08__grid {
grid-template-columns: repeat(2,1fr);
}
}
.pl-08__tile {
padding: 18px 14px;
border-radius: 10px;
background: var(--card);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
align-items: center;
transition: background .18s ease,border-color .18s ease;
}
.pl-08__tile:hover {
background: var(--card-hover);
}
/* Status ring with pulsing pseudo-elements */
.pl-08__ring {
position: relative;
width: 80px;
height: 80px;
border-radius: 50%;
border: 2px solid var(--ok);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
}
.pl-08__ring::before,
.pl-08__ring::after {
content: '';
position: absolute;
inset: -6px;
border-radius: 50%;
border: 2px solid currentColor;
color: var(--ok-dim);
pointer-events: none;
animation: pl-08-ring 2s ease-out infinite;
}
.pl-08__ring::after {
animation-delay: 1s;
}
.pl-08__ring--warn {
border-color: var(--warn);
}
.pl-08__ring--warn::before,
.pl-08__ring--warn::after {
color: var(--warn-dim);
animation-duration: 1.2s;
}
.pl-08__ring--warn::after {
animation-delay: .6s;
}
@keyframes pl-08-ring {
0% {
transform: scale(1);
opacity: .7;
}
70% {
transform: scale(1.35);
opacity: 0;
}
100% {
transform: scale(1.4);
opacity: 0;
}
}
.pl-08__ring-inner {
display: flex;
flex-direction: column;
align-items: center;
line-height: 1;
}
.pl-08__uptime {
font-size: 1.1rem;
font-weight: 800;
letter-spacing: -.01em;
font-variant-numeric: tabular-nums;
color: var(--ink);
}
.pl-08__uptime em {
font-size: .65rem;
font-style: normal;
color: var(--sub);
margin-left: 1px;
}
.pl-08__ring-label {
font-size: .55rem;
font-weight: 700;
letter-spacing: .14em;
color: var(--sub);
margin-top: 3px;
}
.pl-08__tile-name {
font-size: .85rem;
font-weight: 700;
letter-spacing: -.005em;
color: var(--ink);
margin-bottom: 8px;
}
.pl-08__tile-meta {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
font-size: .68rem;
color: var(--sub);
font-variant-numeric: tabular-nums;
}
.pl-08__tag {
padding: 2px 7px;
border-radius: 3px;
font-size: .6rem;
font-weight: 800;
letter-spacing: .1em;
}
.pl-08__tag--ok {
background: rgba(34,197,94,.15);
color: var(--ok);
}
.pl-08__tag--warn {
background: rgba(245,158,11,.15);
color: var(--warn);
}
@media (prefers-reduced-motion: reduce) {
.pl-08__ring::before,
.pl-08__ring::after,
.pl-08__meta-dot {
animation: none!important;
}
.pl-08__ring::before,
.pl-08__ring::after {
opacity: 0!important;
}
}.pl-08 {
--bg: #0a3a1f;
--card: #08301a;
--card-hover: #0c4025;
--ink: #d1fae5;
--sub: #6ee7b7;
--ok: #22c55e;
--ok-dim: rgba(34,197,94,.35);
--warn: #f59e0b;
--warn-dim: rgba(245,158,11,.4);
--line: rgba(34,197,94,.14);
font-family: 'JetBrains Mono',ui-monospace,'Menlo','Consolas',monospace;
width: 100%;
min-height: 100vh;
position: relative;
display: grid;
grid-template-rows: auto 1fr;
gap: 16px;
padding: 24px;
background: radial-gradient(60% 100% at 20% 10%, rgba(34,197,94,.06) 0%, transparent 60%), var(--bg);
color: var(--ink);
}
.pl-08 *,
.pl-08 *::before,
.pl-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pl-08 ::selection {
background: var(--ok);
color: var(--bg);
}
.pl-08__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 12px 18px;
border-radius: 9px;
background: var(--card);
border: 1px solid var(--line);
}
.pl-08__brand {
display: flex;
align-items: center;
gap: 8px;
font-size: .82rem;
font-weight: 700;
letter-spacing: .06em;
text-transform: uppercase;
}
.pl-08__logo {
color: var(--ok);
font-size: .9rem;
}
.pl-08__region-strip {
display: flex;
gap: 4px;
}
.pl-08__region-item {
padding: 5px 10px;
border-radius: 5px;
font-size: .72rem;
font-weight: 600;
letter-spacing: .04em;
color: var(--sub);
cursor: pointer;
transition: background .15s ease;
}
.pl-08__region-item:hover {
background: var(--card-hover);
}
.pl-08__region-item--sel {
background: var(--card-hover);
color: var(--ink);
}
.pl-08__meta {
display: flex;
align-items: center;
gap: 8px;
font-size: .74rem;
color: var(--sub);
font-variant-numeric: tabular-nums;
}
.pl-08__meta-dot {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--ok);
animation: pl-08-blink 1.4s ease-in-out infinite;
}
@keyframes pl-08-blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: .4;
}
}
.pl-08__grid {
display: grid;
grid-template-columns: repeat(4,1fr);
gap: 12px;
}
@media (max-width: 820px) {
.pl-08__grid {
grid-template-columns: repeat(2,1fr);
}
}
.pl-08__tile {
padding: 18px 14px;
border-radius: 10px;
background: var(--card);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
align-items: center;
transition: background .18s ease,border-color .18s ease;
}
.pl-08__tile:hover {
background: var(--card-hover);
}
/* Status ring with pulsing pseudo-elements */
.pl-08__ring {
position: relative;
width: 80px;
height: 80px;
border-radius: 50%;
border: 2px solid var(--ok);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
}
.pl-08__ring::before,
.pl-08__ring::after {
content: '';
position: absolute;
inset: -6px;
border-radius: 50%;
border: 2px solid currentColor;
color: var(--ok-dim);
pointer-events: none;
animation: pl-08-ring 2s ease-out infinite;
}
.pl-08__ring::after {
animation-delay: 1s;
}
.pl-08__ring--warn {
border-color: var(--warn);
}
.pl-08__ring--warn::before,
.pl-08__ring--warn::after {
color: var(--warn-dim);
animation-duration: 1.2s;
}
.pl-08__ring--warn::after {
animation-delay: .6s;
}
@keyframes pl-08-ring {
0% {
transform: scale(1);
opacity: .7;
}
70% {
transform: scale(1.35);
opacity: 0;
}
100% {
transform: scale(1.4);
opacity: 0;
}
}
.pl-08__ring-inner {
display: flex;
flex-direction: column;
align-items: center;
line-height: 1;
}
.pl-08__uptime {
font-size: 1.1rem;
font-weight: 800;
letter-spacing: -.01em;
font-variant-numeric: tabular-nums;
color: var(--ink);
}
.pl-08__uptime em {
font-size: .65rem;
font-style: normal;
color: var(--sub);
margin-left: 1px;
}
.pl-08__ring-label {
font-size: .55rem;
font-weight: 700;
letter-spacing: .14em;
color: var(--sub);
margin-top: 3px;
}
.pl-08__tile-name {
font-size: .85rem;
font-weight: 700;
letter-spacing: -.005em;
color: var(--ink);
margin-bottom: 8px;
}
.pl-08__tile-meta {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
font-size: .68rem;
color: var(--sub);
font-variant-numeric: tabular-nums;
}
.pl-08__tag {
padding: 2px 7px;
border-radius: 3px;
font-size: .6rem;
font-weight: 800;
letter-spacing: .1em;
}
.pl-08__tag--ok {
background: rgba(34,197,94,.15);
color: var(--ok);
}
.pl-08__tag--warn {
background: rgba(245,158,11,.15);
color: var(--warn);
}
@media (prefers-reduced-motion: reduce) {
.pl-08__ring::before,
.pl-08__ring::after,
.pl-08__meta-dot {
animation: none!important;
}
.pl-08__ring::before,
.pl-08__ring::after {
opacity: 0!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: Server Status Ring
Source: https://codefronts.com/motion/css-pulse-animation/server-status-ring/
A DevOps operations-center dashboard with server-status rings — 4 tiles each showing a service, its uptime, and a concentric pulsing ring indicating health status. Terminal-green healthy state (#22c55e) with amber warning (#f59e0b) tile mixed in for realism. Datadog / PagerDuty / Grafana ops-center aesthetic with all-uppercase JetBrains Mono labels, deep-forest surface (#0a3a1f), and hostname / region metadata.
## HTML
```html
<div class="pl-08">
<header class="pl-08__head">
<div class="pl-08__brand">
<span class="pl-08__logo" aria-hidden="true">◆</span>
<span>opswatch</span>
</div>
<div class="pl-08__region-strip">
<span class="pl-08__region-item">us-east-1</span>
<span class="pl-08__region-item pl-08__region-item--sel">eu-west-2</span>
<span class="pl-08__region-item">ap-south-1</span>
</div>
<div class="pl-08__meta">
<span class="pl-08__meta-dot" aria-label="Live"></span>
<span>Live · 14:22 UTC</span>
</div>
</header>
<main class="pl-08__grid">
<article class="pl-08__tile" role="group" aria-label="api-gateway, uptime 99.98%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.98<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">api-gateway</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 142ms</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="auth-service, uptime 99.99%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.99<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">auth-service</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 38ms</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="billing-svc, uptime 98.42%, degraded">
<div class="pl-08__ring pl-08__ring--warn">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">98.42<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">billing-svc</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--warn">DEGRADED</span>
<span>p95 · 890ms ↑</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="search-index, uptime 99.94%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.94<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">search-index</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 76ms</span>
</div>
</article>
</main>
</div>
```
## CSS
```css
.pl-08 {
--bg: #0a3a1f;
--card: #08301a;
--card-hover: #0c4025;
--ink: #d1fae5;
--sub: #6ee7b7;
--ok: #22c55e;
--ok-dim: rgba(34,197,94,.35);
--warn: #f59e0b;
--warn-dim: rgba(245,158,11,.4);
--line: rgba(34,197,94,.14);
font-family: 'JetBrains Mono',ui-monospace,'Menlo','Consolas',monospace;
width: 100%;
min-height: 100vh;
position: relative;
display: grid;
grid-template-rows: auto 1fr;
gap: 16px;
padding: 24px;
background: radial-gradient(60% 100% at 20% 10%, rgba(34,197,94,.06) 0%, transparent 60%), var(--bg);
color: var(--ink);
}
.pl-08 *,
.pl-08 *::before,
.pl-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pl-08 ::selection {
background: var(--ok);
color: var(--bg);
}
.pl-08__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 12px 18px;
border-radius: 9px;
background: var(--card);
border: 1px solid var(--line);
}
.pl-08__brand {
display: flex;
align-items: center;
gap: 8px;
font-size: .82rem;
font-weight: 700;
letter-spacing: .06em;
text-transform: uppercase;
}
.pl-08__logo {
color: var(--ok);
font-size: .9rem;
}
.pl-08__region-strip {
display: flex;
gap: 4px;
}
.pl-08__region-item {
padding: 5px 10px;
border-radius: 5px;
font-size: .72rem;
font-weight: 600;
letter-spacing: .04em;
color: var(--sub);
cursor: pointer;
transition: background .15s ease;
}
.pl-08__region-item:hover {
background: var(--card-hover);
}
.pl-08__region-item--sel {
background: var(--card-hover);
color: var(--ink);
}
.pl-08__meta {
display: flex;
align-items: center;
gap: 8px;
font-size: .74rem;
color: var(--sub);
font-variant-numeric: tabular-nums;
}
.pl-08__meta-dot {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--ok);
animation: pl-08-blink 1.4s ease-in-out infinite;
}
@keyframes pl-08-blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: .4;
}
}
.pl-08__grid {
display: grid;
grid-template-columns: repeat(4,1fr);
gap: 12px;
}
@media (max-width: 820px) {
.pl-08__grid {
grid-template-columns: repeat(2,1fr);
}
}
.pl-08__tile {
padding: 18px 14px;
border-radius: 10px;
background: var(--card);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
align-items: center;
transition: background .18s ease,border-color .18s ease;
}
.pl-08__tile:hover {
background: var(--card-hover);
}
/* Status ring with pulsing pseudo-elements */
.pl-08__ring {
position: relative;
width: 80px;
height: 80px;
border-radius: 50%;
border: 2px solid var(--ok);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
}
.pl-08__ring::before,
.pl-08__ring::after {
content: '';
position: absolute;
inset: -6px;
border-radius: 50%;
border: 2px solid currentColor;
color: var(--ok-dim);
pointer-events: none;
animation: pl-08-ring 2s ease-out infinite;
}
.pl-08__ring::after {
animation-delay: 1s;
}
.pl-08__ring--warn {
border-color: var(--warn);
}
.pl-08__ring--warn::before,
.pl-08__ring--warn::after {
color: var(--warn-dim);
animation-duration: 1.2s;
}
.pl-08__ring--warn::after {
animation-delay: .6s;
}
@keyframes pl-08-ring {
0% {
transform: scale(1);
opacity: .7;
}
70% {
transform: scale(1.35);
opacity: 0;
}
100% {
transform: scale(1.4);
opacity: 0;
}
}
.pl-08__ring-inner {
display: flex;
flex-direction: column;
align-items: center;
line-height: 1;
}
.pl-08__uptime {
font-size: 1.1rem;
font-weight: 800;
letter-spacing: -.01em;
font-variant-numeric: tabular-nums;
color: var(--ink);
}
.pl-08__uptime em {
font-size: .65rem;
font-style: normal;
color: var(--sub);
margin-left: 1px;
}
.pl-08__ring-label {
font-size: .55rem;
font-weight: 700;
letter-spacing: .14em;
color: var(--sub);
margin-top: 3px;
}
.pl-08__tile-name {
font-size: .85rem;
font-weight: 700;
letter-spacing: -.005em;
color: var(--ink);
margin-bottom: 8px;
}
.pl-08__tile-meta {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
font-size: .68rem;
color: var(--sub);
font-variant-numeric: tabular-nums;
}
.pl-08__tag {
padding: 2px 7px;
border-radius: 3px;
font-size: .6rem;
font-weight: 800;
letter-spacing: .1em;
}
.pl-08__tag--ok {
background: rgba(34,197,94,.15);
color: var(--ok);
}
.pl-08__tag--warn {
background: rgba(245,158,11,.15);
color: var(--warn);
}
@media (prefers-reduced-motion: reduce) {
.pl-08__ring::before,
.pl-08__ring::after,
.pl-08__meta-dot {
animation: none!important;
}
.pl-08__ring::before,
.pl-08__ring::after {
opacity: 0!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: Server Status Ring
Source: https://codefronts.com/motion/css-pulse-animation/server-status-ring/
A DevOps operations-center dashboard with server-status rings — 4 tiles each showing a service, its uptime, and a concentric pulsing ring indicating health status. Terminal-green healthy state (#22c55e) with amber warning (#f59e0b) tile mixed in for realism. Datadog / PagerDuty / Grafana ops-center aesthetic with all-uppercase JetBrains Mono labels, deep-forest surface (#0a3a1f), and hostname / region metadata.
## HTML
```html
<div class="pl-08">
<header class="pl-08__head">
<div class="pl-08__brand">
<span class="pl-08__logo" aria-hidden="true">◆</span>
<span>opswatch</span>
</div>
<div class="pl-08__region-strip">
<span class="pl-08__region-item">us-east-1</span>
<span class="pl-08__region-item pl-08__region-item--sel">eu-west-2</span>
<span class="pl-08__region-item">ap-south-1</span>
</div>
<div class="pl-08__meta">
<span class="pl-08__meta-dot" aria-label="Live"></span>
<span>Live · 14:22 UTC</span>
</div>
</header>
<main class="pl-08__grid">
<article class="pl-08__tile" role="group" aria-label="api-gateway, uptime 99.98%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.98<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">api-gateway</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 142ms</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="auth-service, uptime 99.99%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.99<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">auth-service</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 38ms</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="billing-svc, uptime 98.42%, degraded">
<div class="pl-08__ring pl-08__ring--warn">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">98.42<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">billing-svc</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--warn">DEGRADED</span>
<span>p95 · 890ms ↑</span>
</div>
</article>
<article class="pl-08__tile" role="group" aria-label="search-index, uptime 99.94%, healthy">
<div class="pl-08__ring pl-08__ring--ok">
<div class="pl-08__ring-inner">
<span class="pl-08__uptime">99.94<em>%</em></span>
<span class="pl-08__ring-label">UPTIME</span>
</div>
</div>
<div class="pl-08__tile-name">search-index</div>
<div class="pl-08__tile-meta">
<span class="pl-08__tag pl-08__tag--ok">HEALTHY</span>
<span>p95 · 76ms</span>
</div>
</article>
</main>
</div>
```
## CSS
```css
.pl-08 {
--bg: #0a3a1f;
--card: #08301a;
--card-hover: #0c4025;
--ink: #d1fae5;
--sub: #6ee7b7;
--ok: #22c55e;
--ok-dim: rgba(34,197,94,.35);
--warn: #f59e0b;
--warn-dim: rgba(245,158,11,.4);
--line: rgba(34,197,94,.14);
font-family: 'JetBrains Mono',ui-monospace,'Menlo','Consolas',monospace;
width: 100%;
min-height: 100vh;
position: relative;
display: grid;
grid-template-rows: auto 1fr;
gap: 16px;
padding: 24px;
background: radial-gradient(60% 100% at 20% 10%, rgba(34,197,94,.06) 0%, transparent 60%), var(--bg);
color: var(--ink);
}
.pl-08 *,
.pl-08 *::before,
.pl-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pl-08 ::selection {
background: var(--ok);
color: var(--bg);
}
.pl-08__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 12px 18px;
border-radius: 9px;
background: var(--card);
border: 1px solid var(--line);
}
.pl-08__brand {
display: flex;
align-items: center;
gap: 8px;
font-size: .82rem;
font-weight: 700;
letter-spacing: .06em;
text-transform: uppercase;
}
.pl-08__logo {
color: var(--ok);
font-size: .9rem;
}
.pl-08__region-strip {
display: flex;
gap: 4px;
}
.pl-08__region-item {
padding: 5px 10px;
border-radius: 5px;
font-size: .72rem;
font-weight: 600;
letter-spacing: .04em;
color: var(--sub);
cursor: pointer;
transition: background .15s ease;
}
.pl-08__region-item:hover {
background: var(--card-hover);
}
.pl-08__region-item--sel {
background: var(--card-hover);
color: var(--ink);
}
.pl-08__meta {
display: flex;
align-items: center;
gap: 8px;
font-size: .74rem;
color: var(--sub);
font-variant-numeric: tabular-nums;
}
.pl-08__meta-dot {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--ok);
animation: pl-08-blink 1.4s ease-in-out infinite;
}
@keyframes pl-08-blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: .4;
}
}
.pl-08__grid {
display: grid;
grid-template-columns: repeat(4,1fr);
gap: 12px;
}
@media (max-width: 820px) {
.pl-08__grid {
grid-template-columns: repeat(2,1fr);
}
}
.pl-08__tile {
padding: 18px 14px;
border-radius: 10px;
background: var(--card);
border: 1px solid var(--line);
display: flex;
flex-direction: column;
align-items: center;
transition: background .18s ease,border-color .18s ease;
}
.pl-08__tile:hover {
background: var(--card-hover);
}
/* Status ring with pulsing pseudo-elements */
.pl-08__ring {
position: relative;
width: 80px;
height: 80px;
border-radius: 50%;
border: 2px solid var(--ok);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
}
.pl-08__ring::before,
.pl-08__ring::after {
content: '';
position: absolute;
inset: -6px;
border-radius: 50%;
border: 2px solid currentColor;
color: var(--ok-dim);
pointer-events: none;
animation: pl-08-ring 2s ease-out infinite;
}
.pl-08__ring::after {
animation-delay: 1s;
}
.pl-08__ring--warn {
border-color: var(--warn);
}
.pl-08__ring--warn::before,
.pl-08__ring--warn::after {
color: var(--warn-dim);
animation-duration: 1.2s;
}
.pl-08__ring--warn::after {
animation-delay: .6s;
}
@keyframes pl-08-ring {
0% {
transform: scale(1);
opacity: .7;
}
70% {
transform: scale(1.35);
opacity: 0;
}
100% {
transform: scale(1.4);
opacity: 0;
}
}
.pl-08__ring-inner {
display: flex;
flex-direction: column;
align-items: center;
line-height: 1;
}
.pl-08__uptime {
font-size: 1.1rem;
font-weight: 800;
letter-spacing: -.01em;
font-variant-numeric: tabular-nums;
color: var(--ink);
}
.pl-08__uptime em {
font-size: .65rem;
font-style: normal;
color: var(--sub);
margin-left: 1px;
}
.pl-08__ring-label {
font-size: .55rem;
font-weight: 700;
letter-spacing: .14em;
color: var(--sub);
margin-top: 3px;
}
.pl-08__tile-name {
font-size: .85rem;
font-weight: 700;
letter-spacing: -.005em;
color: var(--ink);
margin-bottom: 8px;
}
.pl-08__tile-meta {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
font-size: .68rem;
color: var(--sub);
font-variant-numeric: tabular-nums;
}
.pl-08__tag {
padding: 2px 7px;
border-radius: 3px;
font-size: .6rem;
font-weight: 800;
letter-spacing: .1em;
}
.pl-08__tag--ok {
background: rgba(34,197,94,.15);
color: var(--ok);
}
.pl-08__tag--warn {
background: rgba(245,158,11,.15);
color: var(--warn);
}
@media (prefers-reduced-motion: reduce) {
.pl-08__ring::before,
.pl-08__ring::after,
.pl-08__meta-dot {
animation: none!important;
}
.pl-08__ring::before,
.pl-08__ring::after {
opacity: 0!important;
}
}
```How this works
Each tile is a rounded rectangle with a large centered circular "status ring" — the outer ring is the tile's border (border-radius: 50%; border: 2px solid the status color). Inside, a smaller inner ring shows the uptime percentage. Two ::before and ::after pseudo-elements on the outer ring are positioned inset: -6px (extending outside the ring). Each pseudo-element animates transform: scale(1) → scale(1.4) with opacity: .6 → 0 over 2 seconds, staggered by 1 second, creating a continuous outward pulse that reads as "actively monitoring, service is up".
Healthy tiles pulse with green rings (#22c55e) at a slower rate (2s cycle) — the calm rhythm of a healthy service. The warning tile pulses at 1.2s with amber (#f59e0b) — faster to draw attention without full alarm. If a tile were in a critical state, it would use red (#dc2626) at a 0.6s frantic pulse — but no red tile in this demo (production dashboards should never NORMALLY have red; if they do, that's an alert-fatigue smell).
Terminal / ops-center palette: deep forest-green surface (#0a3a1f) with darker card background (#08301a), all-uppercase JetBrains Mono labels with wide letter-spacing for that Terminal Terminal aesthetic. Uptime numbers use tabular-nums so they don't jiggle when values update. Region tag (us-east-1, eu-west-2) mimics AWS/GCP region codes clinicians recognize.
Semantic HTML uses <div role="group" aria-label="api-gateway server, uptime 99.98 percent, status healthy"> so screen readers announce the full state without depending on visual color. Live status updates use aria-live="polite" so screen readers announce status transitions when they happen.
Make it yours
- Change the health-status color mapping — green healthy, amber warning, red critical, gray unknown/offline. Map to your SLO / SLA taxonomy. Never use only color: pair with pulse rate + text label.
- Adjust the concentric ring count from 2 to 3 pseudo-elements for a more dramatic status indication — useful for critical services that need extra visual weight.
- Add a status label inside the ring ("OK", "WARN", "DEGRADED") — reinforces color coding for color-blind users. Position with
display: flex; place-items: centerinside the ring wrapper. - For latency-sensitive services, replace uptime % with p95 latency (
142ms) as the ring's inner number — matches the metric ops engineers actually watch. - Add tile background tinting based on status (subtle green wash for healthy, subtle amber for warning) — reinforces the color coding at a scan-glance.
Gotchas — read before shipping
- Avoid alert fatigue: production ops dashboards should show mostly-green with occasional amber. If your dashboard is normally showing red, you have too-sensitive alerts and engineers will start ignoring the red — a real patient-safety-like issue in ops.
- Do NOT rely on color alone for status — pair every status color with a text label ("HEALTHY", "WARNING") for color-blind users (deuteranopia affects 8% of male users, protanopia 1%).
- The pulse rate MUST scale with severity — healthy should feel calm, warning should feel attentive, critical should feel urgent. A red critical tile at the same 2s calm rate reads as "not important".
- For high-density dashboards (50+ services), IntersectionObserver pause pulses for off-screen tiles — otherwise 50 concurrent GPU compositor layers stress the browser and cause frame drops on real hardware.
- For accessibility, the status ring should have
role="progressbar"+aria-valuenow+aria-valuemin+aria-valuemaxso screen readers can announce "99.98% uptime, target 99.9%" — the number matters more than the visual ring. - The uptime number should use
font-variant-numeric: tabular-nums— proportional numerals cause the ring text to jiggle when values update, which reads as "broken/glitching" instead of "live monitoring". - For
prefers-reduced-motion: keep the STATIC ring color (conveying health status), disable the animated concentric pulse. The color mapping still communicates "healthy/warning" without motion.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 45+ | 10+ | 40+ | 45+ |
transform + opacity + @keyframes. Universal, GPU-accelerated.