Utilities for adding a shimmer effect to text elements.
❯buridan add shimmerThe 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;
}
}
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.
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
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
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
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
Use shimmer-reverse to sweep the highlight in the opposite direction.
Generating response...
shimmer reverse
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")
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