<script module lang="ts">
import { tv, type VariantProps } from "tailwind-variants";
export const shimmerText = tv({
base: "inline-block w-fit",
variants: {
color: {
inherit: "",
default: "text-foreground-default",
muted: "text-foreground-muted",
subtle: "text-foreground-subtle",
success: "text-foreground-success",
danger: "text-foreground-danger",
warning: "text-foreground-warning",
info: "text-foreground-info"
}
},
defaultVariants: {
color: "inherit"
}
});
export type ShimmerTextVariants = VariantProps<typeof shimmerText>;
</script>
<script lang="ts">
import type { Snippet } from "svelte";
import type { HTMLAttributes } from "svelte/elements";
interface Props extends Omit<HTMLAttributes<HTMLSpanElement>, "class" | "children"> {
color?: ShimmerTextVariants["color"];
duration?: number;
delay?: number;
class?: string;
children: Snippet;
}
let {
color = "inherit",
duration = 3,
delay = 0,
class: className,
children,
...rest
}: Props = $props();
const classes = $derived(shimmerText({ color, class: className }));
</script>
<span
class="shimmer {classes}"
style:--shimmer-duration="{duration}s"
style:--shimmer-delay="{delay}s"
{...rest}
>
{@render children()}
</span>
<style>
@keyframes shimmer {
from {
background-position-x: -100%;
}
50%,
to {
background-position-x: 250%;
}
}
.shimmer {
/* mixing towards the page colour lightens the highlight in light mode and
darkens it in dark mode, so no dark: override is needed. override the
custom property when the text sits on a non-default surface. */
--shimmer-highlight: color-mix(in srgb, currentColor 40%, var(--color-background-default));
background-color: currentColor;
background-image: linear-gradient(
to right,
currentColor 0%,
var(--shimmer-highlight) 40%,
var(--shimmer-highlight) 60%,
currentColor 100%
);
background-repeat: no-repeat;
background-size: 50% 200%;
background-position-x: -100%;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer var(--shimmer-duration) linear var(--shimmer-delay) infinite;
}
@media (prefers-reduced-motion: reduce) {
.shimmer {
animation: none;
}
}
</style>