Back to the library

Pulse beacon

Variant: Soft

A status dot with a ring that expands and fades out of it. Reach for it when something is ongoing and unattended — a deploy running, a job in flight — and you want the page to say "still happening" without stealing focus. The ring travels far and slowly, which reads as breathing rather than blinking.

statusloopattentiondot

The source

A complete HTML document. Paste it into an empty .html file and it plays — no stylesheet, script, font, or image to fetch.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Pulse beacon — soft</title>
    <style>
      :root {
        --ivory: #faf9f5;
        --slate: #141413;
        --clay: #d97757;
        --gray-500: #87867f;
        --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
      }

      * {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
      }

      body {
        min-height: 100vh;
        display: grid;
        place-items: center;
        background: var(--ivory);
        font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
        color: var(--slate);
      }

      .status {
        display: flex;
        align-items: center;
        gap: 12px;
        font-size: 14px;
        letter-spacing: -0.01em;
      }

      .beacon {
        position: relative;
        width: 12px;
        height: 12px;
        flex: none;
      }

      .beacon span {
        position: absolute;
        inset: 0;
        border-radius: 50%;
      }

      .beacon .core {
        background: var(--clay);
      }

      /* Ambient status indicator, not a UI transition: the ring expands over
         1.2s on purpose so the dot reads as a slow breath rather than a blink.
         Only transform and opacity animate, so it stays on the GPU. */
      .beacon .ring {
        border: 1.5px solid var(--clay);
        opacity: 0;
        animation: beacon-soft 2.4s var(--ease-out) infinite;
      }

      @keyframes beacon-soft {
        0% {
          transform: scale(1);
          opacity: 0.5;
        }
        50%,
        100% {
          transform: scale(2.8);
          opacity: 0;
        }
      }

      @media (prefers-reduced-motion: reduce) {
        .beacon .ring {
          animation: beacon-soft-fade 2.4s ease infinite;
          transform: scale(2);
        }

        @keyframes beacon-soft-fade {
          0%,
          100% {
            opacity: 0;
          }
          35% {
            opacity: 0.4;
          }
        }
      }
    </style>
  </head>
  <body>
    <p class="status">
      <span class="beacon">
        <span class="ring"></span>
        <span class="core"></span>
      </span>
      Deploying to production
    </p>
  </body>
</html>

The variant family

Same idea, one axis moved. Compare them side by side before you commit.