Aurora Background
BackgroundsAmbient gradient field: three soft colour fields drifting at three coprime speeds, a slow hue rotation so the colours themselves move, and a film-grain layer stepped at 12 fps.
A beam of light travels around the edge of a card. A conic gradient behind two composited masks leaves only the ring; the start angle animates as a registered custom property. Pure CSS, inherits the parent radius.
Pro
Sections, composed.
Live, from the library itself: scroll, hover, navigate away and back. The demo is never gated.
Adjust · props are custom properties, set inline, so you edit the running instance
JavaScript of its own
0 B
CSS 5.1 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.
Browser support
Chrome 120 · Firefox 128 · Safari 16.4; needs property, mask-image. Elsewhere: the beam holds still at its start angle.
One command adds the integration, the base tokens and every component. Then import what you use.
npx astro add moonarcpnpm astro add moonarcbunx astro add moonarc---
import BorderBeam from '@moonarc/core/BorderBeam';
---
<div class="relative rounded-2xl border p-6">
<BorderBeam />
<h3>Pro</h3>
</div>Owns the file, no dependency. The registry item also installs the base tokens.
npx shadcn@latest add https://moonarc.dev/r/border-beam.jsonThe CLI needs a components.json and the @/* alias, which Setup has. The file lands in src/components/moonarc/.
The whole component. Self-contained styles in a cascade layer so your classes always win; it imports nothing.
---
/**
* BorderBeam — a light travels around the edge of a card, zero JS.
*
* The element carries a conic gradient whose start angle is a registered
* custom property; two masks composited with `exclude` keep only the border
* ring. The angle grows, so the beam runs clockwise and the head colour sits
* at the leading end. Animating the angle repaints the ring, nothing else.
* Absolutely positioned: give the parent `position: relative` and a radius.
*/
import type { HTMLAttributes } from 'astro/types';
interface Props extends HTMLAttributes<'div'> {
/** Seconds per lap. */
duration?: number;
/** Length of the beam as a fraction of the perimeter, 0.05–0.5. */
length?: number;
/** Ring thickness in px. */
width?: number;
/** Beam colours: head and tail. */
colors?: [string, string];
/** Start angle in degrees. */
from?: number;
}
const { duration = 6, length = 0.2, width = 1.5, colors = ['oklch(78% 0.16 60)', 'oklch(70% 0.19 300)'], from = 0, class: className, style, ...rest } = Astro.props;
const span = Math.max(0.05, Math.min(length, 0.5)) * 360;
const vars = [`--ma-beam-duration:${duration}s`, `--ma-beam-span:${span}deg`, `--ma-beam-width:${width}px`, `--ma-beam-1:${colors[0]}`, `--ma-beam-2:${colors[1]}`, `--ma-beam-from:${from}deg`, typeof style === 'string' ? style : ''].filter(Boolean).join(';');
---
<div
class:list={['ma-beam', className]}
aria-hidden="true"
style={vars}
{...rest}
>
</div>
<style is:global>
@property --ma-beam-angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
@layer components {
:where(.ma-beam) {
position: absolute;
inset: 0;
border-radius: inherit;
pointer-events: none;
padding: var(--ma-beam-width, 1.5px);
/* clockwise, so the head is the far end of the span: transparent at the start, the tail colour 60% of the length
behind the head, and a hard edge at the head. Without @property the angle falls back to 0 and the beam holds
still at `from` (the keyframe then flips it from 0 to a full turn, the same place) */
background: conic-gradient(from calc(var(--ma-beam-angle, 0deg) + var(--ma-beam-from, 0deg)), transparent 0deg, var(--ma-beam-2) calc(var(--ma-beam-span, 72deg) * 0.4), var(--ma-beam-1) var(--ma-beam-span, 72deg), transparent var(--ma-beam-span, 72deg));
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
mask-composite: exclude;
animation: ma-beam var(--ma-beam-duration, 6s) linear infinite;
}
@media (prefers-reduced-motion: reduce) {
:where(.ma-beam) {
animation: none;
}
}
}
@keyframes ma-beam {
to {
--ma-beam-angle: 360deg;
}
}
</style>| Prop | Type | Default | Description |
|---|---|---|---|
duration | number | 6 | Seconds per lap. Under 3 reads as a spinner. |
length | number | 0.2 | Beam length as a fraction of the perimeter, 0.05–0.5. |
width | number | 1.5 | Ring thickness in px. |
colors | [string, string] | amber, violet | Head and tail colours. |
from | number | 0 | Start angle in degrees; offset several cards so they do not sync. |
The beam holds still at its start angle (from).
ClientRouterCSS-only; nothing to rebind.
Replaces: BorderBeam (Magic UI) · MovingBorder (Aceternity) · StarBorder (React Bits). See the migration table.
Ambient gradient field: three soft colour fields drifting at three coprime speeds, a slow hue rotation so the colours themselves move, and a film-grain layer stepped at 12 fps.
A soft radial light follows the cursor over a card and fades in only while hovered.
A card leans toward the cursor in 3D, lifts slightly, catches a glare, and settles back on the active preset's spring when the cursor leaves.
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown