A frosted 4 by 4 grid clears tile by tile in a diagonal wave, each tile flashing an accent outline as it renders, to reveal a glossy sphere. One @property clock drives all sixteen tiles.
Also known as: progressive image loading animation, image render tiles css, ai image generation animation, tile by tile reveal.
<div class="aia-tile-render" role="status" aria-label="Rendering image tiles">
<div class="aia-tile-render-frame">
<span class="aia-tile-render-image"></span>
<div class="aia-tile-render-grid" aria-hidden="true">
<i style="--d: 0"></i>
<i style="--d: 1"></i>
<i style="--d: 2"></i>
<i style="--d: 3"></i>
<i style="--d: 1"></i>
<i style="--d: 2"></i>
<i style="--d: 3"></i>
<i style="--d: 4"></i>
<i style="--d: 2"></i>
<i style="--d: 3"></i>
<i style="--d: 4"></i>
<i style="--d: 5"></i>
<i style="--d: 3"></i>
<i style="--d: 4"></i>
<i style="--d: 5"></i>
<i style="--d: 6"></i>
</div>
<span class="aia-tile-render-bar"></span>
</div>
<span class="aia-tile-render-caption">Rendering 1024 × 1024</span>
</div>@property --aia-tile-render-t {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
.aia-tile-render {
--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;
display: grid;
justify-items: center;
gap: 0.6em;
animation: aia-tile-render-clock 5.5s linear infinite;
}
.aia-tile-render-frame {
position: relative;
width: 10.4em;
height: 10.4em;
border-radius: 0.8em;
overflow: hidden;
box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 12%, transparent), 0 0.8em 1.6em -0.9em rgba(0, 0, 0, 0.45);
}
.aia-tile-render-image {
position: absolute;
inset: 0;
background:
radial-gradient(circle at 40% 36%, #fff 0 4%, transparent 14%),
radial-gradient(circle at 46% 44%, color-mix(in srgb, var(--c) 30%, #fff) 0 10%, var(--c) 27%, color-mix(in srgb, var(--c) 45%, #000) 34%, transparent 34.5%),
radial-gradient(ellipse 34% 7% at 50% 80%, rgba(0, 0, 0, 0.35), transparent 70%),
linear-gradient(180deg, color-mix(in srgb, var(--c) 85%, #fff), color-mix(in srgb, var(--c) 70%, #fff) 62%, color-mix(in srgb, var(--c) 55%, #fff) 62%, color-mix(in srgb, var(--c) 78%, #fff));
}
.aia-tile-render-grid {
position: absolute;
inset: 0;
display: grid;
grid-template: repeat(4, 1fr) / repeat(4, 1fr);
}
.aia-tile-render-grid i {
--s: calc(0.06 + var(--d) * 0.085);
--in: clamp(0, (var(--aia-tile-render-t) - var(--s)) * 12, 1);
--back: clamp(0, (var(--aia-tile-render-t) - 0.9) * 14, 1);
position: relative;
background: color-mix(in srgb, color-mix(in srgb, var(--c) 35%, #000) 70%, rgba(20, 20, 28, 0.4));
-webkit-backdrop-filter: blur(0.6em) saturate(0.6);
backdrop-filter: blur(0.6em) saturate(0.6);
box-shadow: inset 0 0 0 0.5px rgba(255, 255, 255, 0.14);
opacity: calc(1 - var(--in) + var(--back));
}
.aia-tile-render-grid i::after {
content: "";
position: absolute;
inset: 0;
box-shadow: inset 0 0 0 0.12em var(--c), 0 0 0.8em color-mix(in srgb, var(--c) 70%, transparent);
opacity: min(clamp(0, (var(--aia-tile-render-t) - var(--s) + 0.07) * 14, 1), clamp(0, (var(--s) + 0.03 - var(--aia-tile-render-t)) * 14, 1));
}
.aia-tile-render-bar {
position: absolute;
left: 0;
bottom: 0;
height: 0.2em;
width: calc(min(var(--aia-tile-render-t) / 0.7, 1) * 100%);
background: var(--c);
opacity: clamp(0, (0.88 - var(--aia-tile-render-t)) * 10, 1);
}
.aia-tile-render-caption {
font-size: 0.82em;
font-weight: 500;
font-variant-numeric: tabular-nums;
color: color-mix(in srgb, currentColor 65%, transparent);
}
@keyframes aia-tile-render-clock {
to { --aia-tile-render-t: 1; }
}
@media (prefers-reduced-motion: reduce) {
.aia-tile-render { animation: none; --aia-tile-render-t: 0.8; }
}
@media (prefers-reduced-motion: reduce) {
.aia-tile-render, .aia-tile-render *, .aia-tile-render::before, .aia-tile-render::after, .aia-tile-render *::before, .aia-tile-render *::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.