20 CSS Loaders 16 / 20

CSS Typing Dots Loader

Four conversational CSS loaders — a chat-bubble typing indicator, an inline AI-thinking status, a cursor blink, and a bouncing dot trio — matching the patterns used in messaging and AI product UIs.

Pure CSS MIT licensed
Live Demo Open in tab

This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.

Open in playground

The code

<div class="ld-16">
  <div class="ld-16__stage">
    <div>
      <div class="ld-16__label-row">Chat bubble</div>
      <div class="ld-16__bubble"><span></span><span></span><span></span></div>
    </div>
.ld-16,.ld-16 *,.ld-16 *::before,.ld-16 *::after{box-sizing:border-box;margin:0;padding:0}
.ld-16{
  --bg:#1c1c2e;--c1:#e2e8f0;--c2:#94a3b8;--c3:#38bdf8;--c4:#a78bfa;
  background:var(--bg);display:flex;align-items:center;justify-content:center;min-height:100vh;font-family:'Segoe UI',sans-serif;
}
.ld-16__stage{display:flex;gap:28px;flex-direction:column;align-items:flex-start;padding:40px;max-width:400px;width:100%}
.ld-16__label-row{color:rgba(255,255,255,.3);font-size:10px;letter-spacing:1.5px;text-transform:uppercase;margin-bottom:4px}

/* Chat bubble with typing dots */
.ld-16__bubble{background:#2d2d44;border-radius:18px 18px 18px 4px;padding:14px 18px;display:inline-flex;align-items:center;gap:6px;max-width:180px}
.ld-16__bubble span{width:8px;height:8px;border-radius:50%;background:var(--c2);animation:ld-16-typing 1.2s ease-in-out infinite}
.ld-16__bubble span:nth-child(1){animation-delay:0s}
.ld-16__bubble span:nth-child(2){animation-delay:.2s}
.ld-16__bubble span:nth-child(3){animation-delay:.4s}
@keyframes ld-16-typing{0%,60%,100%{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}

/* Inline text dots */
.ld-16__inline{display:flex;align-items:center;gap:8px;color:var(--c2);font-size:14px}
.ld-16__inline-dots{display:flex;gap:3px}
.ld-16__inline-dots span{width:5px;height:5px;border-radius:50%;background:var(--c3);animation:ld-16-typing 1s ease-in-out infinite}
.ld-16__inline-dots span:nth-child(1){animation-delay:0s}
.ld-16__inline-dots span:nth-child(2){animation-delay:.15s}
.ld-16__inline-dots span:nth-child(3){animation-delay:.3s}

/* Cursor blink */
.ld-16__cursor-wrap{color:var(--c1);font-size:16px;display:flex;align-items:center;gap:2px}
.ld-16__cursor{width:2px;height:20px;background:var(--c4);animation:ld-16-blink 1s step-end infinite}
@keyframes ld-16-blink{0%,100%{opacity:1}50%{opacity:0}}

/* Processing dots large */
.ld-16__processing{display:flex;gap:10px;align-items:center}
.ld-16__processing span{width:12px;height:12px;border-radius:50%;background:var(--c4);animation:ld-16-proc 1.4s ease-in-out infinite}
.ld-16__processing span:nth-child(1){animation-delay:0s}
.ld-16__processing span:nth-child(2){animation-delay:.2s;background:var(--c3)}
.ld-16__processing span:nth-child(3){animation-delay:.4s;background:var(--c4)}
@keyframes ld-16-proc{0%,80%,100%{transform:scale(.6) translateY(0);opacity:.4}40%{transform:scale(1) translateY(-10px);opacity:1}}

@media(prefers-reduced-motion:reduce){
  .ld-16__bubble span,.ld-16__inline-dots span,.ld-16__cursor,.ld-16__processing span{animation:none}
}

How this works

The chat bubble typing indicator wraps three dots inside a styled div with border-radius: 18px 18px 18px 4px to create the asymmetric chat-bubble tail. Each dot uses a single keyframe: translateY(0)translateY(-6px)translateY(0) with ease-in-out, cascaded with 0.2s delays per dot. The dots also fade between opacity:0.4 and opacity:1 in the same keyframe so they dim as they rise, reinforcing depth.

The cursor blink uses animation-timing-function: step-end so the cursor snaps between visible and invisible rather than fading — this matches the hard-cut behaviour of real text cursors. The processing dots use cubic-bezier easing on a compound transform of scale and translateY in the same keyframe, making each dot simultaneously grow and rise at its peak, then shrink as it falls back.

Customize

  • Match your chat app's bubble shape by adjusting the four border-radius values on .ld-16__bubble — use 18px 18px 4px 18px for an outgoing-message variant.
  • Slow the typing animation to 1.8s for a more contemplative AI-thinking feel, or drop to 0.8s for an eager autocomplete indicator.
  • Change dot size from 8px to 5px for a compact inline variant that fits within a single line of body text.
  • Add a fourth dot to the bounce sequence by adding a span:nth-child(4) rule with animation-delay: .6s for a slightly longer visual rhythm.
  • Swap translateY(-6px) for scale(1.4) to create a radial pulse variant instead of an upward bounce — useful for non-directional contexts.

Watch out for

  • The cursor blink must use step-end not steps(1, end) — while semantically equivalent, some linters flag the latter; use the keyword form for clarity.
  • The bubble border-radius asymmetry only creates a bottom-left tail pointing left — for a right-aligned outgoing bubble, swap the last two radius values and mirror with transform: scaleX(-1).
  • Staggered animation-delay on dots causes the first animation cycle to start at different offsets — add animation-fill-mode: both to each dot to prevent a flash on the initial render.

Browser support

ChromeSafariFirefoxEdge
49+ 9+ 44+ 49+

Typing indicator loaders use universal CSS; step-end timing function is supported in all modern browsers.

Search CodeFronts

Loading…