# Border Beam

> Animated light beam that travels around its parent's border — a quiet “an agent is working in here” signal for composers, cards and panels.

- **Slug:** `border-beam` · **Category:** effects · **Status:** beta
- **Import:** `import { BorderBeam } from "@kura-ui/react/border-beam";`
- **Docs:** https://kuro.abstergo.fyi/components/border-beam
- **Registry item:** https://kuro.abstergo.fyi/r/border-beam.json
- **Tags:** border, beam, glow, loading, busy, agent, conic-gradient, css

## Install

```bash
# shadcn registry (copies source into your project)
npx shadcn@latest add https://kuro.abstergo.fyi/r/border-beam.json
# Kura CLI (same, plus dependency resolution and --json output)
npx @kura-ui/cli add border-beam
# npm package
npm install @kura-ui/react
```

## When to use

- 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.

## When not to use

- 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 | Required | Description |
| --- | --- | --- | --- | --- |
| `active` | `boolean` | `true` | no | Show the beam. Toggling fades it in/out over 480ms and pauses the animation while hidden — keep it mounted. |
| `duration` | `number` | `6` | no | Seconds per lap. |
| `size` | `number` | `0.25` | no | Beam length as a share of the lap, 0.05–0.9. |
| `width` | `number` | `1` | no | Ring thickness in px. |
| `colors` | `readonly string[]` | — | no | CSS colours from tail to head. Default: transparent → jade at 45% → jade → accent-text. Tokens and color-mix() work. |
| `glow` | `boolean` | `false` | no | Add a blurred copy of the beam for a soft glow (not clipped by the parent unless it has overflow-hidden). |
| `reverse` | `boolean` | `false` | no | Travel counter-clockwise. |
| `offset` | `number` | `0` | no | Starting position as a share of the lap, 0–1. Use 0.5 on a second beam to run two beams opposite each other. |

Native attributes of the root element, `className` and `ref` are passed through.

## Anatomy

- **BorderBeam** (`data-slot="border-beam"`) — Absolutely positioned overlay (`inset-0`, `rounded-[inherit]`, pointer-events none). `data-active` mirrors `active`.
- **Glow** (`data-slot="border-beam-glow"`) — Blurred wrapper around a thicker ring; only rendered with `glow`.
- **Ring** (`data-slot="border-beam-ring"`) — The masked conic gradient ring that carries the animation.

## 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.

## Examples

### Card with a beam (minimal)

Drop it inside any relative, rounded element.

```tsx
import { BorderBeam } from "@kura-ui/react/border-beam";

export function BorderBeamMinimal() {
  return (
    <div className="relative w-full max-w-sm rounded-kura-xl bg-kura-surface p-5 text-sm shadow-kura-sm">
      <p className="font-medium text-kura-fg">Indexing workspace</p>
      <p className="text-kura-fg-muted">The beam marks a container the agent is working in.</p>
      <BorderBeam />
    </div>
  );
}
```

### Composer with two beams (full)

Glow, width, size, speed, a second opposite beam via offset, and toggling active.

```tsx
"use client";

import * as React from "react";
import { Button } from "@kura-ui/react/button";
import { BorderBeam } from "@kura-ui/react/border-beam";

export function BorderBeamFull() {
  const [active, setActive] = React.useState(true);
  const [glow, setGlow] = React.useState(true);
  const [fast, setFast] = React.useState(false);
  return (
    <div className="flex w-full max-w-md flex-col items-center gap-5">
      <div className="relative flex h-40 w-full flex-col justify-between rounded-kura-2xl bg-kura-surface p-4 shadow-kura-sm">
        <p className="text-kura-fg-muted text-sm">Ask the agent to change something…</p>
        <div className="flex items-center justify-between text-kura-fg-subtle text-xs">
          <span>{active ? "Agent is editing 3 files" : "Idle"}</span>
          <span className="font-kura-mono">⌘↵</span>
        </div>
        <BorderBeam active={active} glow={glow} duration={fast ? 3 : 7} size={0.3} width={1.5} />
        <BorderBeam active={active} duration={fast ? 3 : 7} size={0.18} offset={0.5} />
      </div>
      <div className="flex flex-wrap justify-center gap-1.5">
        <Button
          size="sm"
          variant="secondary"
          aria-pressed={active}
          onClick={() => setActive((v) => !v)}
        >
          {active ? "Stop" : "Start"}
        </Button>
        <Button size="sm" variant="ghost" aria-pressed={glow} onClick={() => setGlow((v) => !v)}>
          Glow {glow ? "on" : "off"}
        </Button>
        <Button size="sm" variant="ghost" aria-pressed={fast} onClick={() => setFast((v) => !v)}>
          {fast ? "3s lap" : "7s lap"}
        </Button>
      </div>
    </div>
  );
}
```

### Running task row (composition)

ShimmerText status, a Stop Button and a beam that fades out when stopped.

```tsx
"use client";

import { FileCode2, Square } from "lucide-react";
import * as React from "react";
import { Button } from "@kura-ui/react/button";
import { ShimmerText } from "@kura-ui/react/shimmer-text";
import { BorderBeam } from "@kura-ui/react/border-beam";

export function BorderBeamAgentTask() {
  const [running, setRunning] = React.useState(true);
  return (
    <div className="relative flex w-full max-w-md items-center gap-3 rounded-kura-lg bg-kura-surface p-3 shadow-kura-sm">
      <span className="flex size-9 shrink-0 items-center justify-center rounded-kura-md bg-kura-accent-subtle text-kura-accent-text">
        <FileCode2 className="size-4" />
      </span>
      <div className="min-w-0 flex-1 text-sm" role="status" aria-live="polite">
        <p className="truncate font-medium text-kura-fg">auth/session.ts</p>
        {running ? (
          <ShimmerText className="text-xs">Refactoring token refresh…</ShimmerText>
        ) : (
          <span className="text-kura-fg-muted text-xs">Stopped by you</span>
        )}
      </div>
      <Button size="sm" variant="secondary" onClick={() => setRunning((r) => !r)}>
        {running ? <Square className="size-3 fill-current" /> : null}
        {running ? "Stop" : "Resume"}
      </Button>
      <BorderBeam active={running} glow duration={5} />
    </div>
  );
}
```

## Dependencies

- npm: none
- Kura registry items: `kura-theme`, `utils`
- Related: `thinking-indicator`, `shimmer-text`, `composer`, `tool-call`, `mesh-gradient`
