AI CSS Animations

Tool Call Retry and Recover

A WebFetch tool call times out: its spinner turns into a red cross and the card edge flushes red, attempt 2 of 3 slides in and runs, then lands 200 OK with a green check as the attempt pips fill.

Also known as: tool call retry animation, auto retry attempt 2 of 3, failed request recovered.

WebFetchdocs.stripe.com/api/refunds
  1. Attempt 1 ConnectingETIMEDOUT after 30s
  2. Attempt 2 of 3 Retrying200 OK, 14.2 kB

HTML

<div class="aia-tool-retry-recover" role="status" aria-label="WebFetch timed out, retried and succeeded">
  <div class="aia-tool-retry-recover-head">
    <span class="aia-tool-retry-recover-icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="8.5"/><path d="M3.5 12h17M12 3.5c2.4 2.4 3.4 5.3 3.4 8.5s-1 6.1-3.4 8.5c-2.4-2.4-3.4-5.3-3.4-8.5s1-6.1 3.4-8.5z"/></svg></span>
    <span class="aia-tool-retry-recover-call"><b>WebFetch</b><code>docs.stripe.com/api/refunds</code></span>
    <span class="aia-tool-retry-recover-pips" aria-hidden="true"><i></i><i></i><i></i></span>
  </div>
  <ol class="aia-tool-retry-recover-log">
    <li class="aia-tool-retry-recover-a1">
      <span class="aia-tool-retry-recover-st" aria-hidden="true"><i></i><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 7l10 10M17 7 7 17"/></svg></span>
      <span>Attempt 1</span>
      <span class="aia-tool-retry-recover-msg"><em>Connecting</em><em>ETIMEDOUT after 30s</em></span>
    </li>
    <li class="aia-tool-retry-recover-a2">
      <span class="aia-tool-retry-recover-st" aria-hidden="true"><i></i><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5.5 12.5 4 4 9-9.5"/></svg></span>
      <span>Attempt 2 of 3</span>
      <span class="aia-tool-retry-recover-msg"><em>Retrying</em><em>200 OK, 14.2 kB</em></span>
    </li>
  </ol>
</div>

CSS

@property --aia-tool-retry-recover-t {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}
.aia-tool-retry-recover {
  --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;
  position: relative;
  box-sizing: border-box;
  padding: 0.9em 1em;
  border-radius: 0.9em;
  background: color-mix(in srgb, currentColor 3%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 11%, transparent), 0 0.1em 0.2em color-mix(in srgb, currentColor 4%, transparent), 0 0.9em 2em -1.1em rgb(0 0 0 / 0.3);
  --d: 8s;
  --t: var(--aia-tool-retry-recover-t);
  --f: clamp(0, (var(--t) - 1) * 8, 1);
  --r: clamp(0, (var(--t) - 1.4) * 2, 1);
  --s: clamp(0, (var(--t) - 3) * 5, 1);
  width: 22em;
  animation: aia-tool-retry-recover-clock var(--d) linear infinite;
}
.aia-tool-retry-recover::before,
.aia-tool-retry-recover::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}
.aia-tool-retry-recover::before {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #dc2626 45%, transparent), 0 0 1.4em -0.6em #dc2626;
  opacity: calc(var(--f) * (1 - var(--r)));
}
.aia-tool-retry-recover::after {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #16a34a 45%, transparent), 0 0 1.4em -0.6em #16a34a;
  opacity: var(--s);
}
.aia-tool-retry-recover-head,
.aia-tool-retry-recover-log {
  animation: aia-tool-retry-recover-fade var(--d) linear infinite;
}
.aia-tool-retry-recover-head {
  display: flex;
  align-items: center;
  gap: 0.6em;
}
.aia-tool-retry-recover-icon {
  display: grid;
  place-items: center;
  width: 1.9em;
  height: 1.9em;
  border-radius: 0.55em;
  background: color-mix(in srgb, var(--c) 14%, transparent);
  color: color-mix(in srgb, var(--c) 80%, currentColor);
}
.aia-tool-retry-recover-icon svg {
  width: 1.1em;
  height: 1.1em;
}
.aia-tool-retry-recover-call {
  display: grid;
  margin-right: auto;
  min-width: 0;
  line-height: 1.3;
}
.aia-tool-retry-recover-call b { font-size: 0.92em; }
.aia-tool-retry-recover-call code {
  overflow: hidden;
  color: color-mix(in srgb, currentColor 55%, transparent);
  font: 0.74em ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.aia-tool-retry-recover-pips {
  display: flex;
  gap: 0.28em;
}
.aia-tool-retry-recover-pips i {
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  background: color-mix(in srgb, currentColor 18%, transparent);
}
.aia-tool-retry-recover-pips i:first-child {
  background: color-mix(in srgb, #dc2626 calc(var(--f) * 100%), var(--c));
}
.aia-tool-retry-recover-pips i:nth-child(2) {
  background: color-mix(in srgb, #16a34a calc(var(--s) * 100%), color-mix(in srgb, var(--c) calc(var(--r) * 100%), color-mix(in srgb, currentColor 18%, transparent)));
  transform: scale(calc(1 + var(--r) * (1 - var(--s)) * 0.25));
}
.aia-tool-retry-recover-log {
  display: grid;
  gap: 0.15em;
  margin: 0.75em 0 0;
  padding: 0.55em 0 0;
  border-top: 1px solid color-mix(in srgb, currentColor 9%, transparent);
  list-style: none;
}
.aia-tool-retry-recover-log li {
  display: flex;
  align-items: center;
  gap: 0.6em;
  height: 1.8em;
  font-size: 0.82em;
}
.aia-tool-retry-recover-a1 { opacity: calc(1 - var(--r) * 0.4); }
.aia-tool-retry-recover-a2 {
  opacity: var(--r);
  transform: translateY(calc((1 - var(--r)) * 0.6em));
}
.aia-tool-retry-recover-st {
  position: relative;
  display: grid;
  place-items: center;
  width: 1.15em;
  height: 1.15em;
}
.aia-tool-retry-recover-st > * { grid-area: 1 / 1; }
.aia-tool-retry-recover-st i {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: 0.15em solid color-mix(in srgb, var(--c) 22%, transparent);
  border-top-color: var(--c);
  animation: aia-tool-retry-recover-spin 0.8s linear infinite;
}
.aia-tool-retry-recover-st svg {
  width: 1.15em;
  height: 1.15em;
  stroke-width: 3;
}
.aia-tool-retry-recover-a1 i { opacity: calc(1 - var(--f)); }
.aia-tool-retry-recover-a1 svg {
  color: color-mix(in srgb, #dc2626 80%, currentColor);
  opacity: var(--f);
  transform: scale(calc(0.4 + var(--f) * 0.6)) rotate(calc((1 - var(--f)) * -90deg));
}
.aia-tool-retry-recover-a2 i { opacity: calc(1 - var(--s)); }
.aia-tool-retry-recover-a2 svg {
  color: color-mix(in srgb, #16a34a 78%, currentColor);
  opacity: var(--s);
  stroke-dasharray: 22;
  stroke-dashoffset: calc((1 - var(--s)) * 22);
  transform: scale(calc(0.6 + var(--s) * 0.4));
}
.aia-tool-retry-recover-msg {
  display: grid;
  margin-left: auto;
  text-align: right;
}
.aia-tool-retry-recover-msg em {
  grid-area: 1 / 1;
  font: 500 0.9em ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  font-style: normal;
  white-space: nowrap;
}
.aia-tool-retry-recover-msg em:first-child {
  background: linear-gradient(90deg, color-mix(in srgb, currentColor 45%, transparent) 35%, currentColor 50%, color-mix(in srgb, currentColor 45%, transparent) 65%) 0 0 / 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: aia-tool-retry-recover-shine 1.6s linear infinite;
}
.aia-tool-retry-recover-a1 .aia-tool-retry-recover-msg em:first-child { opacity: calc(1 - var(--f)); }
.aia-tool-retry-recover-a1 .aia-tool-retry-recover-msg em:last-child {
  color: color-mix(in srgb, #dc2626 80%, currentColor);
  opacity: var(--f);
}
.aia-tool-retry-recover-a2 .aia-tool-retry-recover-msg em:first-child { opacity: calc(1 - var(--s)); }
.aia-tool-retry-recover-a2 .aia-tool-retry-recover-msg em:last-child {
  color: color-mix(in srgb, #16a34a 78%, currentColor);
  opacity: var(--s);
  transform: translateY(calc((1 - var(--s)) * 0.4em));
}
@keyframes aia-tool-retry-recover-clock {
  0% { --aia-tool-retry-recover-t: 0; }
  22% { --aia-tool-retry-recover-t: 0.98; }
  25%, 31% { --aia-tool-retry-recover-t: 1.2; }
  38% { --aia-tool-retry-recover-t: 2; }
  60% { --aia-tool-retry-recover-t: 2.98; }
  63%, 95.5% { --aia-tool-retry-recover-t: 3.3; }
  96.5%, 100% { --aia-tool-retry-recover-t: 0; }
}
@keyframes aia-tool-retry-recover-fade {
  0%, 93% { opacity: 1; }
  95.5%, 97% { opacity: 0; }
  100% { opacity: 1; }
}
@keyframes aia-tool-retry-recover-spin {
  to { transform: rotate(360deg); }
}
@keyframes aia-tool-retry-recover-shine {
  from { background-position: 100% 0; }
  to { background-position: -50% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .aia-tool-retry-recover,
  .aia-tool-retry-recover-head,
  .aia-tool-retry-recover-log {
    animation: none;
    --aia-tool-retry-recover-t: 3.3;
  }
  .aia-tool-retry-recover-st i,
  .aia-tool-retry-recover-msg em:first-child { animation: none; }
}

@media (prefers-reduced-motion: reduce) {
  .aia-tool-retry-recover, .aia-tool-retry-recover *, .aia-tool-retry-recover::before, .aia-tool-retry-recover::after, .aia-tool-retry-recover *::before, .aia-tool-retry-recover *::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