Shimmer

Utilities for adding a shimmer effect to text elements.

buridan add shimmer

The shimmer utility is purely composed of CSS and is based on shadcn/shimmer. No extensions to rxconfig.py are needed as it uses Tailwind v4 syntax.

If your project was set up with buridan init, you already have shimmer. It ships with the buridan package, which the CLI imports in your global CSS file.

Otherwise install the buirdan package:

uv run buridan init

You can also copy paste the shimmer source directly into your globals.css file. Make sure you also include the correct imports, such as @tailwind utilities; at the top of your CSS file.

@property --shimmer-angle {
    syntax: "<angle>";
    inherits: true;
    initial-value: 20deg;
}
@property --shimmer-image {
    syntax: "*";
    inherits: false;
}
@property --shimmer-text-fill {
    syntax: "*";
    inherits: false;
}

@theme inline {
    @keyframes tw-shimmer {
        from {
            background-position: 100% 0;
        }
        to {
            background-position: 0 0;
        }
    }
}

@utility shimmer {
    --_spread: var(--shimmer-spread, calc(3ch + 40px));
    --_base: currentColor;
    --_highlight: var(
        --shimmer-color,
        oklch(from currentColor l c h / calc(alpha* 0.2))
    );

    background-image: var(
        --shimmer-image,
        linear-gradient(
            calc(90deg + var(--shimmer-angle)),
            var(--_base) calc(50% - var(--_spread)),
            color-mix(in oklch, var(--_highlight), var(--_base) 50%)
                calc(50% - var(--_spread) * 0.5),
            var(--_highlight) 50%,
            color-mix(in oklch, var(--_highlight), var(--_base) 50%)
                calc(50% + var(--_spread) * 0.5),
            var(--_base) calc(50% + var(--_spread))
        )
    );
    background-repeat: no-repeat;
    background-size: calc(200% + var(--_spread) * 2) 100%;
    background-position: 0 0;
    background-clip: text;
    -webkit-background-clip: text;
    -webkit-text-fill-color: var(--shimmer-text-fill, transparent);
    animation: tw-shimmer var(--shimmer-duration, 2s) linear infinite;

    @variant dark {
        --_highlight: var(
            --shimmer-color,
            oklch(
                from currentColor max(0.8, calc(l + 0.4)) c h /
                    calc(alpha + 0.4)
            )
        );
    }

    &:where([dir="rtl"], [dir="rtl"] *) {
        animation-direction: reverse;
    }
}

@utility shimmer-once {
    animation-iteration-count: 1;
}

@utility shimmer-reverse {
    animation-direction: reverse;
}

@utility shimmer-none {
    --shimmer-image: none;
    --shimmer-text-fill: currentColor;
}

@utility shimmer-color-* {
    --shimmer-color: --value(--color, [color]);
    --shimmer-color: color-mix(
        in oklch,
        --value(--color, [color]) calc(--modifier(integer) * 1%),
        transparent
    );
}

@utility shimmer-duration-* {
    --shimmer-duration: calc(--value(integer) * 1ms);
}

@utility shimmer-spread-* {
    --shimmer-spread: calc(var(--spacing) * --value(integer));
    --shimmer-spread: --value([length], [percentage]);
}

@utility shimmer-angle-* {
    --shimmer-angle: calc(--value(integer) * 1deg);
}

@media (prefers-reduced-motion: reduce) {
    .shimmer {
        animation: none;
        background-image: none;
        -webkit-text-fill-color: currentColor;
    }
}

Usage

Class Styles
shimmer background-clip: text;
animation: tw-shimmer var(--shimmer-duration, 2s) linear infinite;
shimmer-once animation-iteration-count: 1;
shimmer-reverse animation-direction: reverse;
shimmer-none --shimmer-image: none;
--shimmer-text-fill: currentColor;
shimmer-color-<color> --shimmer-color: <color>;
shimmer-color-[<value>] --shimmer-color: <value>;
shimmer-color-<color>/<pct> --shimmer-color: color-mix(in oklch, <color> <pct>, transparent);
shimmer-duration-<number> --shimmer-duration: calc(<number> * 1ms);
shimmer-spread-<number> --shimmer-spread: calc(var(--spacing) * <number>);
shimmer-spread-[<value>] --shimmer-spread: <value>;
shimmer-angle-<number> --shimmer-angle: calc(<number> * 1deg);

Add shimmer to a text element.

rx.el.p("Generating response", class_name="shimmer text-muted-foreground w-fit")

The effect is pure CSS. The text is painted with background-clip: text, and the highlight sweeps across it in a seamless loop.

Note: If the shimmer utility isn't working properly, try adding w-fit to the text styling, as it ensures the animation doesn't exceed the inherit width of the text.

Color

Use shimmer-color-<color> to set the highlight color explicitly. It accepts theme colors with an optional opacity modifier, or any arbitrary color value.

Generating response

Generating response

Duration

Use shimmer-duration-<number> to set the duration of one sweep in milliseconds. The default is 2000, i.e. 2s.

Generating response...

shimmer

Generating response...

shimmer-duration-1000

Spread

Use shimmer-spread-<number> to set the width of the highlight band using the spacing scale. The default is calc(3ch + 40px): a fixed base plus a 3ch term that scales with the font size.

Generating response...

shimmer-spread-4

Generating response...

shimmer-spread-24

Angle

Use shimmer-angle-<number> to set the tilt of the highlight band in degrees. The default is 20.

Generating response...

shimmer

Generating response...

shimmer-angle-45

Reverse

Use shimmer-reverse to sweep the highlight in the opposite direction.

Generating response...

shimmer reverse

Play Once

Use shimmer-once to play a single sweep instead of looping, useful as a reveal when streaming completes. Pair it with shimmer-duration-<number> to control how long the sweep takes.

Generating response...

rx.el.p("Response Generated", class_name="shimmer shimmer-duration-1100 shimmer-once")

Disabling the Shimmer

Use shimmer-none to turn the effect off and render the text normally. It works in any class order, so the typical use is responsive or stateful.

Generating response...

shimmer md:shimmer-none