Border Beam
betaAnimated light beam that travels around its parent's border — a quiet “an agent is working in here” signal for composers, cards and panels.
Card with a beam
minimal
Indexing workspace
The beam marks a container the agent is working in.
Composer with two beams
Glow, width, size, speed, a second opposite beam via offset, and toggling active.
full
Ask the agent to change something…
Agent is editing 3 files⌘↵
Running task row
ShimmerText status, a Stop Button and a beam that fades out when stopped.
composition
auth/session.ts
Refactoring token refresh…Installation
npx shadcn@latest add https://kuro.abstergo.fyi/r/border-beam.jsonCopies the source into components/kura/border-beam with its dependencies (kura-theme, utils). Tokens come from the kura-theme item.
When to use it
Use it for
- A composer, card or panel whose content an agent is currently editing or generating.
- A running tool call or background task row that should read as live without a spinner.
- Highlighting the one container on screen that is waiting for or producing AI output.
Reach for something else when
- Small inline 'thinking' feedback — use thinking-indicator or shimmer-text.
- Filling a background with colour — use mesh-gradient or aurora.
- Focus or selection states — use the focus ring (shadow-kura-focus), not a beam.
- Several beams on one screen at once: it is a signal; more than one or two becomes noise.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| active | boolean | true | Show the beam. Toggling fades it in/out over 480ms and pauses the animation while hidden — keep it mounted. |
| duration | number | 6 | Seconds per lap. |
| size | number | 0.25 | Beam length as a share of the lap, 0.05–0.9. |
| width | number | 1 | Ring thickness in px. |
| colors | readonly string[] | — | CSS colours from tail to head. Default: transparent → jade at 45% → jade → accent-text. Tokens and color-mix() work. |
| glow | boolean | false | Add a blurred copy of the beam for a soft glow (not clipped by the parent unless it has overflow-hidden). |
| reverse | boolean | false | Travel counter-clockwise. |
| offset | number | 0 | Starting position as a share of the lap, 0–1. Use 0.5 on a second beam to run two beams opposite each other. |
All native attributes of the root element, className and ref pass through.
Anatomy
- BorderBeamAbsolutely positioned overlay (`inset-0`, `rounded-[inherit]`, pointer-events none). `data-active` mirrors `active`.
data-slot="border-beam" - GlowBlurred wrapper around a thicker ring; only rendered with `glow`.
data-slot="border-beam-glow" - RingThe masked conic gradient ring that carries the animation.
data-slot="border-beam-ring"
Accessibility
- Purely decorative (aria-hidden). Pair it with real text — a role=status label such as “Editing 3 files…” — so the busy state is not conveyed by motion alone.
- It never intercepts pointer events, so the container stays fully interactive.
- Reduced motion: The beam stops travelling and becomes a static, softly tinted ring (head colour at 45%) so the container still reads as active.
Notes for agents
- The parent must be `relative` (or otherwise positioned) and should carry the border radius; the beam uses `rounded-[inherit]`.
- Render it as the last child so it paints above the content edge; it does not take space.
- Pure CSS: no 'use client' needed. Styles are injected once via a React 19 hoisted <style>.
- Keep it mounted and flip `active` instead of conditionally rendering, so it fades rather than pops.
- On wide, short elements the beam speeds up along the short sides (it is an angle sweep); prefer size 0.2–0.35 there.