Border beam
A card whose border carries a travelling light, in three lengths and three speeds.
One of 6 components in Beams, particles and motion
Variants
Details
What Border beam brings along, read from the registry and the source.
- Category
- Beams, particles and motion
- Area
- Elements
- Variants
- 3
- Version
- 1.0.0 · since 1.0.0
- File
- components/ui/border-beam.tsx
- Brings along
- utilskit
- npm packages
- None
- Languages
- Texts come through props
- Studio
- Adjust in code
- Build with AI
- Over MCP in Claude, Cursor, Copilot and Codex
Props
The BorderBeamProps interface from components/ui/border-beam.tsx, with the comments from the code. Also takes every prop of React.HTMLAttributes<HTMLDivElement>.
sizeoptionalBorderBeamSizesmmdlg
Length of the light: sm 70 px, md 130 px, lg 220 px
speedoptionalBorderBeamSpeed | numberTime for one lap: slow 12 s, normal 8 s, fast 5 s, or seconds as a number
doubleoptionalbooleanTwo lights on opposite sides
toneoptional'accent' | 'neutral'accent light, or neutral in the text color
reverseoptionalbooleanRun counterclockwise
radiusoptionalstringCorner radius of the card, default the kit's rounded-2xl
glowoptionalbooleanSoft bloom around the light, default true
childrenoptionalReact.ReactNode
Accessibility
Read from the source of the component.
- Needs no client state. The server writes it in full, so it reads without JavaScript too.
Motion
- Brings animations of its own, calmly timed and without wobble.
- With reduced motion (prefers-reduced-motion) it stays still.
Source
The file aiw add puts into your project. From then on it is yours.
Show the file, 83 lines
'use client';
import { cn } from '@/lib/kit';
export type BorderBeamSize = 'sm' | 'md' | 'lg';
export type BorderBeamSpeed = 'slow' | 'normal' | 'fast';
export interface BorderBeamProps extends React.HTMLAttributes<HTMLDivElement> {
/** Length of the light: sm 70 px, md 130 px, lg 220 px */
size?: BorderBeamSize;
/** Time for one lap: slow 12 s, normal 8 s, fast 5 s, or seconds as a number */
speed?: BorderBeamSpeed | number;
/** Two lights on opposite sides */
double?: boolean;
/** accent light, or neutral in the text color */
tone?: 'accent' | 'neutral';
/** Run counterclockwise */
reverse?: boolean;
/** Corner radius of the card, default the kit's rounded-2xl */
radius?: string;
/** Soft bloom around the light, default true */
glow?: boolean;
children?: React.ReactNode;
}
const LENGTH: Record<BorderBeamSize, number> = { sm: 70, md: 130, lg: 220 };
const SPEED: Record<BorderBeamSpeed, number> = { slow: 12, normal: 8, fast: 5 };
const CSS = `
@keyframes kitfx-border-beam { to { offset-distance: 100%; } }
.kitfx-bb-light { offset-path: rect(var(--kitfx-o) calc(100% - var(--kitfx-o)) calc(100% - var(--kitfx-o)) var(--kitfx-o) round var(--kitfx-r)); offset-anchor: 100% 50%; offset-rotate: auto; }
@media (prefers-reduced-motion: reduce) { .kitfx-bb-light { animation: none !important; } }
@supports not (offset-path: rect(0 auto auto 0)) { .kitfx-bb-light { display: none; } }
`;
/**
* A card whose border carries a travelling light. Children go inside, pass
* padding through className. The light moves along the real rounded border
* at an even pace (CSS offset-path), only the hairline ring shows it. With
* reduced motion the light rests in the top left corner.
*/
export function BorderBeam({ size = 'md', speed = 'normal', double, tone = 'accent', reverse, radius = 'calc(var(--radius) + 8px)', glow = true, className, style, children, ...rest }: BorderBeamProps) {
const len = LENGTH[size];
const dur = typeof speed === 'number' ? speed : SPEED[speed];
const color = tone === 'accent' ? 'var(--primary)' : 'var(--foreground)';
const light = `linear-gradient(to right, transparent, color-mix(in oklch, ${color} 50%, transparent) 55%, ${color} 90%, color-mix(in oklch, ${color} 40%, var(--foreground)))`;
const lights = double ? [0, 50] : [0];
const ring = 'linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0)';
const beam = (start: number, key: string, extra?: string) => (
<span
key={key}
className={cn('kitfx-bb-light absolute left-0 top-0 aspect-square', extra)}
style={{
width: len,
background: light,
offsetDistance: `${start + 6}%`,
animation: `kitfx-border-beam ${dur}s linear ${-((start / 100) * dur)}s infinite${reverse ? ' reverse' : ''}`,
}}
/>
);
return (
<div
{...rest}
className={cn('relative isolate border border-border bg-card text-card-foreground shadow-[var(--highlight),var(--shadow-md)]', className)}
style={{ borderRadius: radius, ['--kitfx-r' as string]: radius, ...style }}
>
<style dangerouslySetInnerHTML={{ __html: CSS }} />
{/* The light lives in a 1px ring on the border. A drop shadow on the wrapper lets it bloom softly. */}
<span
aria-hidden
className="pointer-events-none absolute -inset-px z-10 [--kitfx-bloom:50%] dark:[--kitfx-bloom:80%]"
style={{ borderRadius: 'inherit', filter: glow ? `drop-shadow(0 0 2px color-mix(in oklch, ${color} var(--kitfx-bloom), transparent)) drop-shadow(0 0 9px color-mix(in oklch, ${color} var(--kitfx-bloom), transparent))` : undefined }}
>
<span className="absolute inset-0 overflow-hidden" style={{ borderRadius: 'inherit', padding: 1, mask: ring, WebkitMask: ring, WebkitMaskComposite: 'xor', ['--kitfx-o' as string]: '0.5px' }}>
{lights.map((s, i) => beam(s, `b${i}`))}
</span>
</span>
{children}
</div>
);
}Frequent questions
How do I install Border beam?
With aiw add @ai-website/border-beam. The command puts components/ui/border-beam.tsx into your project, the code is yours from then on. It needs no further npm packages.
Which variants does Border beam have?
3 variants, Announcement card, Sizes and speeds and Two lights.
Can an AI assistant build with Border beam?
Yes. Through the MCP server of the aiw CLI, Claude, Cursor, GitHub Copilot, Windsurf, Codex and Gemini find Border beam with aiw_components and install it with aiw_add @ai-website/border-beam.