Shimmer Border
Highlight a container or button with light that travels along its border.
import { ShimmerBorder, ShimmerButton } from '@youj/motion/interactions'
Installation & usage
npm i @youj/motionFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use it for the one place a screen invites input or an upgrade: an AI composer, a featured plan, a trial call to action. One shimmer draws the eye, and two compete, so use one per screen. ShimmerBorder and ShimmerButton are the Shimmer effect with a surface and a button look. Reach for the effect when the element already has its own look.
ShimmerBorder is a container with a 12px radius, the surface color and the traveling light on its border. Keep its content above the light with relative z-2.
import { ShimmerBorder } from '@youj/motion/interactions'
export function Composer() {
return (
<ShimmerBorder className="rounded-2xl">
<div className="relative z-2">
<label htmlFor="prompt" className="sr-only">Ask the assistant</label>
<textarea id="prompt" rows={3} placeholder="Ask anything about your workspace…" />
<ComposerToolbar />
</div>
</ShimmerBorder>
)
}Focus the prompt in the preview: focus inside the container intensifies the light exactly like hover does.
Description
An AI prompt box whose border carries the light; focus or hover it to lengthen the light.
A trial card with a ShimmerButton as its call to action.
ShimmerButton is a button with the same light. It's 36px tall, with an icon gap built in, and it presses down slightly when clicked. Keyboard focus counts as hover, so Tab to it to see the light lengthen.
import { ShimmerButton } from '@youj/motion/interactions'
import { SparklesIcon } from '@youj/icons'
<ShimmerButton onClick={openCheckout}>
<SparklesIcon size={16} className="text-accent" />
Upgrade to Pro
</ShimmerButton>Both take the effect's options: idle (subtle, visible or hidden), speed (slow, base or fast), intensify and borderWidth. See Shimmer for what each one does.
A pair of plans where the Pro plan's border carries the light at rest.
For a featured card, give ShimmerBorder the radius and surface of the cards beside it, so the light is the only difference.
idle="visible" runs the light at full strength at rest, so the plan stands out before anyone hovers it. The button inside stays plain, since a second light would compete with the first.
Code
shimmer-border-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propsidle, intensify, speed… (10)
Props
ShimmerBorder
| Prop | Type | |
|---|---|---|
children | ReactNode | – |
idle | 'subtle' | 'visible' | 'hidden'How visible the light is at rest. 'hidden' only shows it on hover / focus. default 'subtle' | 'subtle' |
intensify | booleanLengthen the comet and add a bloom on hover and keyboard focus. default true | true |
speed | 'slow' | 'base' | 'fast'One lap of the border. default 'base' | 'base' |
borderWidth | numberBorder width in px. default 1 | 1 |
Other props pass through to the container's div.
ShimmerButton
| Prop | Type | |
|---|---|---|
children | ReactNode | – |
idle | 'subtle' | 'visible' | 'hidden'How visible the light is at rest. 'hidden' only shows it on hover / focus. default 'subtle' | 'subtle' |
intensify | booleanLengthen the comet and add a bloom on hover and keyboard focus. default true | true |
speed | 'slow' | 'base' | 'fast'One lap of the border. default 'base' | 'base' |
borderWidth | numberBorder width in px. default 1 | 1 |
Other props pass through: extends ButtonHTMLAttributes<HTMLButtonElement>. | ||
MotionLap, intensify, press · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
Lapspeed: slow, base, fast | ease.linearcubic-bezier(0, 0, 1, 1) · deliberate × 10, × 6, × 3.5 | cubic-bezier(0, 0, 1, 1)deliberate × 10, × 6, × 3.5 | |
| Intensifyon hover and keyboard focus | duration.slowease.standard340ms · cubic-bezier(0.2, 0, 0, 1) | 340mscubic-bezier(0.2, 0, 0, 1) | |
PressShimmerButton scales to 0.98 | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) |
Reduced motion
The light is parked at a fixed point on the border and doesn't travel. Hover and focus still intensify it, instantly.
The loop is pure CSS on a registered @property angle, so it costs no JavaScript per frame, and it pauses while the element is offscreen or the tab is hidden.
Reduced motion
The light stops at a fixed point on the border, so the container keeps its accent glint without movement, and hover or focus still brightens it instantly.
AccessibilityKeyboard, ARIA, focus
Accessibility
ShimmerButtonis a nativebutton,type="button"unless you pass another type, with a visible focus ring.- The light's layers are
aria-hidden. InShimmerBorder, label the controls inside it as you would anywhere else, like the visually hidden label on the prompt in the demo. - Keyboard focus intensifies the light like hover, so the emphasis never depends on a pointer.