A markdown table from a chat answer filling in cell by cell, each value lifting out of a blur along its row's hairline, then a Popular badge springs onto the Team row once the table is done.
Also known as: ai table streaming animation, markdown table animation, table rows animation css, chatgpt table loading.
| Plan | Seats | Per month |
|---|---|---|
| Starter | 3 | $0 |
| Team Popular | 25 | $12 |
| Business | 100 | $24 |
| Enterprise | Unlimited | Custom |
<div class="aia-streaming-table" role="status" aria-label="Writing a table">
<table class="aia-streaming-table-grid">
<thead><tr><th>Plan</th><th>Seats</th><th>Per month</th></tr></thead>
<tbody>
<tr><td style="--i: 0">Starter</td><td style="--i: 1">3</td><td style="--i: 2">$0</td></tr>
<tr><td style="--i: 3">Team <span class="aia-streaming-table-badge">Popular</span></td><td style="--i: 4">25</td><td style="--i: 5">$12</td></tr>
<tr><td style="--i: 6">Business</td><td style="--i: 7">100</td><td style="--i: 8">$24</td></tr>
<tr><td style="--i: 9">Enterprise</td><td style="--i: 10">Unlimited</td><td style="--i: 11">Custom</td></tr>
</tbody>
</table>
</div>@property --aia-streaming-table-t {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
.aia-streaming-table {
--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;
--t: var(--aia-streaming-table-t);
width: 23em;
max-width: 100%;
animation: aia-streaming-table-run 9s linear infinite;
}
.aia-streaming-table-grid {
width: 100%;
border-collapse: collapse;
font-variant-numeric: tabular-nums;
}
.aia-streaming-table-grid th {
padding: 0 0 0.5em;
text-align: left;
font-size: 0.72em;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: color-mix(in srgb, currentColor 50%, transparent);
}
.aia-streaming-table-grid th:last-child,
.aia-streaming-table-grid td:last-child {
text-align: right;
}
.aia-streaming-table-grid tbody {
animation: aia-streaming-table-fade 9s linear infinite;
}
.aia-streaming-table-grid td {
--r: clamp(0, calc((var(--t) - var(--i)) * 2.2), 1);
padding: 0.5em 0;
border-top: 1px solid color-mix(in srgb, currentColor 11%, transparent);
white-space: nowrap;
opacity: var(--r);
filter: blur(calc((1 - var(--r)) * 0.15em));
}
.aia-streaming-table-grid td > * {
vertical-align: 0.1em;
}
.aia-streaming-table-grid td:first-child {
font-weight: 500;
}
.aia-streaming-table-badge {
--b: clamp(0, calc((var(--t) - 12.2) * 3), 1);
display: inline-block;
margin-left: 0.4em;
padding: 0.15em 0.5em;
border-radius: 99em;
background: color-mix(in srgb, var(--c) 14%, transparent);
color: color-mix(in srgb, var(--c) 75%, currentColor);
font-size: 0.7em;
font-weight: 600;
opacity: var(--b);
transform: scale(calc(0.4 + 0.6 * var(--b) + 1.2 * var(--b) * (1 - var(--b))));
}
@keyframes aia-streaming-table-run {
0%, 6% { --aia-streaming-table-t: 0; }
68% { --aia-streaming-table-t: 12; }
74%, 100% { --aia-streaming-table-t: 13; }
}
@keyframes aia-streaming-table-fade {
0%, 90% { opacity: 1; }
97%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.aia-streaming-table,
.aia-streaming-table-grid tbody { animation: none; }
.aia-streaming-table { --aia-streaming-table-t: 13; }
}
@media (prefers-reduced-motion: reduce) {
.aia-streaming-table, .aia-streaming-table *, .aia-streaming-table::before, .aia-streaming-table::after, .aia-streaming-table *::before, .aia-streaming-table *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--aia-color on the element or a parent.
Currently previewing #3b82f6.--aia-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 16px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.