Loading shimmer
Un haz inclinado que cruza un marcador de carga, solo con transform y respetando el movimiento reducido.
Loading card
Instalación
$ pnpm dlx shadcn@latest add @ssntl/shimmerUso
/* app/globals.css */
@import "../styles/ssntl/shimmer.css";
/* En tu JSX */
<div aria-busy="true">
<span class="sr-only">Cargando</span>
<div aria-hidden class="shimmer h-4 w-1/2 rounded-full"></div>
</div>- Un haz inclinado con un núcleo más brillante cruza de izquierda a derecha con --ease-in-out.
- Solo anima translate: corre en el compositor, sin repintar.
- Velocidad y ángulo con [--shimmer-duration:2000ms] y [--shimmer-angle:100deg].
- Con movimiento reducido el haz desaparece; el estado vive en aria-busy y en texto, nunca solo en la animación.
Código
/* Loading shimmer: a soft slanted beam with a brighter core that crosses a
placeholder left to right on --ease-in-out, so the slash eases instead of
snapping. Only translate animates, so it runs on the compositor.
shimmer the placeholder and its beam
[--shimmer-duration:2000ms] speed
[--shimmer-angle:100deg] slant
With reduced motion the beam is removed and the placeholder stays still;
the loading state lives in aria-busy and text, never in the animation. */
:root,
[data-theme="light"],
.light {
--shimmer-highlight: rgb(255 255 255 / 0.75);
}
[data-theme="dark"],
.dark {
--shimmer-highlight: rgb(255 255 255 / 0.07);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"], .light) {
--shimmer-highlight: rgb(255 255 255 / 0.07);
}
}
@utility shimmer {
position: relative;
isolation: isolate;
overflow: clip;
background-color: var(--shimmer-base, var(--secondary));
&::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background: linear-gradient(
var(--shimmer-angle, 110deg),
transparent 25%,
color-mix(in oklab, var(--shimmer-highlight), transparent 55%) 42%,
var(--shimmer-highlight) 50%,
color-mix(in oklab, var(--shimmer-highlight), transparent 55%) 58%,
transparent 75%
);
translate: -100% 0;
animation: shimmer var(--shimmer-duration, 1600ms) var(--ease-in-out)
infinite;
}
@media (prefers-reduced-motion: reduce) {
&::after {
display: none;
}
}
}
@keyframes shimmer {
to {
translate: 100% 0;
}
}