{ CF }

30 CSS Badges

Typing Indicator

The three bouncing dots are the convention. Without the animation the badge could mean "typed recently" or "about to type." The motion defines the tense: present continuous, active right now.

Pure CSS MIT licensed

Typing Indicator the 2nd of 30 designs in the 30 CSS Badges collection. The design is implemented in pure CSS — no JavaScript required. Copy the HTML and CSS panels below into your project. Because the demo is pure CSS, it works in any framework or templating engine you happen to use. The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.

Live preview

Open in playground

The code

<div class="typing-stage">
  <div class="typing-wrap">
    <div class="typing-join">
      <div class="typing-join-dot"></div>
      Ana Sousa joined the conversation
    </div>

    <div class="typing-msg">Let me check the latest build and get back to you.</div>

    <div class="typing-msg typing-sent">Sure — does the badge spec look right to you?</div>

    <div class="typing-bubble">
      <div class="typing-inner">
        <svg class="typing-avatar" viewBox="0 0 20 20" aria-hidden="true">
          <circle cx="10" cy="10" r="10" fill="#3d7ebf"/>
          <text x="10" y="14.5" text-anchor="middle" font-size="10" fill="white" font-family="sans-serif" font-weight="bold">A</text>
        </svg>
        <span class="typing-name">Ana</span>
        <div class="typing-dots">
          <div class="typing-dot"></div>
          <div class="typing-dot"></div>
          <div class="typing-dot"></div>
        </div>
      </div>
    </div>
  </div>
</div>
.typing-stage {
  background: #ebe4d8;
  padding: 40px 36px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  justify-content: center;
  align-items: center;
  min-height: 360px;
}
.typing-wrap {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  max-width: 360px;
}
.typing-msg {
  align-self: flex-end;
  background: #fff;
  border-radius: 18px 18px 4px 18px;
  padding: 10px 16px;
  font-family: system-ui, "Bricolage Grotesque", sans-serif;
  font-size: 14px;
  color: #2a2018;
  box-shadow: 0 1px 4px rgba(0,0,0,0.08);
  max-width: 80%;
}
.typing-sent {
  background: #1a1612;
  color: #f0ece0;
  border-radius: 18px 18px 18px 4px;
  align-self: flex-start;
}
.typing-bubble { align-self: flex-start; }
.typing-inner {
  background: #fff;
  border-radius: 18px 18px 18px 4px;
  padding: 12px 16px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.08);
}
.typing-avatar {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  flex-shrink: 0;
}
.typing-name {
  font-family: system-ui, "Bricolage Grotesque", sans-serif;
  font-size: 11px;
  letter-spacing: 0.08em;
  color: #888;
  text-transform: uppercase;
}
.typing-dots {
  display: flex;
  gap: 3px;
  align-items: center;
}
.typing-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #aaa;
}
.typing-dot:nth-child(1) { animation: typing-bounce 1.4s ease-in-out infinite 0ms; }
.typing-dot:nth-child(2) { animation: typing-bounce 1.4s ease-in-out infinite 160ms; }
.typing-dot:nth-child(3) { animation: typing-bounce 1.4s ease-in-out infinite 320ms; }
@keyframes typing-bounce {
  0%, 60%, 100% { transform: translateY(0); }
  30% { transform: translateY(-5px); }
}
@media (prefers-reduced-motion: reduce) {
  .typing-dot { animation: none; }
}
.typing-join {
  align-self: center;
  background: rgba(255,255,255,0.7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 20px;
  padding: 6px 14px;
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: system-ui, "Bricolage Grotesque", sans-serif;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: #666;
  box-shadow: 0 1px 4px rgba(0,0,0,0.1);
}
.typing-join-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #0cce6b;
}

Search CodeFronts

Loading…