AI CSS Animations

Chat Reply Arriving

A user message springs up into the thread, the thread scrolls to make room, and the assistant bubble arrives as typing dots that unfurl into the reply. The bubble opens with an animated clip-path.

Also known as: chat message animation css, message bubble animation, ai chat reply animation, chat bubble pop in.

Can you make this headline shorter?

Try “Ship fewer, better things.”

HTML

<div class="aia-message-arriving" role="status" aria-label="New reply">
  <div class="aia-message-arriving-thread">
    <p class="aia-message-arriving-user">Can you make this headline shorter?</p>
    <div class="aia-message-arriving-reply">
      <span class="aia-message-arriving-dots" aria-hidden="true"><i></i><i></i><i></i></span>
      <p class="aia-message-arriving-text">Try “Ship fewer, better things.”</p>
    </div>
  </div>
</div>

CSS

.aia-message-arriving {
  --c: var(--aia-color, #7c5cff);
  font-size: var(--aia-size, 16px);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  line-height: 1.4;
  width: 22em;
  max-width: 100%;
}
.aia-message-arriving-thread {
  display: flex;
  flex-direction: column;
  gap: 0.55em;
  animation: aia-message-arriving-scroll 8s infinite;
}
.aia-message-arriving-user {
  align-self: flex-end;
  margin: 0;
  padding: 0.6em 0.95em;
  border-radius: 1.15em 1.15em 0.4em 1.15em;
  background: var(--c);
  color: #fff;
  transform-origin: 100% 100%;
  animation: aia-message-arriving-user 8s infinite;
}
.aia-message-arriving-reply {
  position: relative;
  align-self: flex-start;
  padding: 0.6em 0.95em;
  border-radius: 1.15em 1.15em 1.15em 0.4em;
  background: color-mix(in srgb, currentColor 7%, transparent);
  transform-origin: 0 100%;
  animation: aia-message-arriving-reply 8s infinite;
}
.aia-message-arriving-text {
  margin: 0;
  white-space: nowrap;
  animation: aia-message-arriving-text 8s infinite;
}
.aia-message-arriving-dots {
  position: absolute;
  left: 0.95em;
  top: 50%;
  display: flex;
  gap: 0.28em;
  translate: 0 -50%;
  animation: aia-message-arriving-dots 8s infinite;
}
.aia-message-arriving-dots i {
  width: 0.45em;
  height: 0.45em;
  border-radius: 50%;
  background: currentColor;
  animation: aia-message-arriving-dot 1.2s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
.aia-message-arriving-dots i:nth-child(2) { animation-delay: 0.14s; }
.aia-message-arriving-dots i:nth-child(3) { animation-delay: 0.28s; }
@keyframes aia-message-arriving-scroll {
  0%, 15% { transform: translateY(1.5em); animation-timing-function: cubic-bezier(0.3, 1.3, 0.5, 1); }
  24%, 100% { transform: none; }
}
@keyframes aia-message-arriving-user {
  0%, 3% { opacity: 0; transform: translateY(0.8em) scale(0.92); animation-timing-function: cubic-bezier(0.3, 1.45, 0.5, 1); }
  11%, 90% { opacity: 1; transform: none; }
  97%, 100% { opacity: 0; transform: none; }
}
@keyframes aia-message-arriving-reply {
  0%, 15% {
    opacity: 0;
    transform: scale(0.85);
    clip-path: inset(0 calc(100% - 3.6em) 0 0 round 1.15em 1.15em 1.15em 0.4em);
    animation-timing-function: cubic-bezier(0.3, 1.45, 0.5, 1);
  }
  21%, 42% {
    opacity: 1;
    transform: none;
    clip-path: inset(0 calc(100% - 3.6em) 0 0 round 1.15em 1.15em 1.15em 0.4em);
    animation-timing-function: cubic-bezier(0.25, 1, 0.35, 1);
  }
  52%, 90% { opacity: 1; transform: none; clip-path: inset(0 0 0 0 round 1.15em 1.15em 1.15em 0.4em); }
  97%, 100% { opacity: 0; transform: none; clip-path: inset(0 0 0 0 round 1.15em 1.15em 1.15em 0.4em); }
}
@keyframes aia-message-arriving-dots {
  0%, 42%   { opacity: 1; }
  46%, 100% { opacity: 0; }
}
@keyframes aia-message-arriving-text {
  0%, 44%   { opacity: 0; filter: blur(0.2em); }
  54%, 100% { opacity: 1; filter: blur(0); }
}
@keyframes aia-message-arriving-dot {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.35; }
  30%           { transform: translateY(-0.25em); opacity: 0.85; }
}
@media (prefers-reduced-motion: reduce) {
  .aia-message-arriving-thread,
  .aia-message-arriving-user,
  .aia-message-arriving-reply,
  .aia-message-arriving-text,
  .aia-message-arriving-dots,
  .aia-message-arriving-dots i { animation: none; }
  .aia-message-arriving-dots { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .aia-message-arriving, .aia-message-arriving *, .aia-message-arriving::before, .aia-message-arriving::after, .aia-message-arriving *::before, .aia-message-arriving *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Customizing this snippet

  • Color: set --aia-color on the element or a parent. Currently previewing #3b82f6.
  • Base size: set --aia-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 16px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar ai animations