Border Beam

A light that travels around an element's border to draw the eye, and hides itself for readers who prefer reduced motion.

Usage

When a moving border is worth it, what each value controls, and what the wrapped element needs for the beam to appear.

When to use

Border Beam sends a light around the border of the element it wraps, for as long as it is on screen. It is the loudest decoration in the kit, so use one beam per view at most, and never around something the reader has to sit and read.
  • The recommended plan in a pricing row, where the card already says so in words.
  • A surface that is working: a panel generating content, a reply still streaming, an import in progress. Remove the beam when the work ends.
  • One hero or call to action on a marketing page.
  • The control a product tour is pointing at, removed once the reader has seen it.
Do not use when
  • The element is mostly text. Movement at the edge of the eye slows reading.
  • Two things on the screen would each get one. Two beams are two priorities, which is none.
  • It would be the only sign that something is selected, recommended, invalid or busy. The beam disappears under reduced motion.
  • The element already animates, or sits in a scrolling list. A beam on every row looks like a rendering fault.

The element it wraps

The beam is drawn inside the child, not inside <BorderBeam>. When nothing renders, the child is almost always missing one of these.
  • An HTML element. A native tag works. A component works only if it forwards its ref to one. An SVG child, or a component that ignores ref, renders no beam and no warning.
  • position: relative. Without it the beam escapes to the nearest positioned ancestor.
  • outset={0} when it clips. By default the beam sits on the child's border, and overflow: hidden cuts it off. 0 draws it inside the edge.
The border width is read once. The beam follows the child's corner radius, but it reads the border width only when it mounts. If the border changes later, at a breakpoint or on hover, set outset yourself.

Duration

duration is the seconds one lap takes, 6 by default. It is the one number that is not pixels. Zero, a negative or a non-number falls back to 6.
  • Slower reads as ambient, faster as urgent. Under about 3 seconds on a card the beam starts to look like a spinner.
  • The lap time is fixed, not the speed, so the same duration runs faster on a wide hero than on a button. Pick the number on the element it will ship on.
  • The beam never stops. To end it, stop rendering it, or pause it through className.

Size and line width

size is the length of the moving head, 100 pixels by default. lineWidth is its thickness, the theme's border width by default. Both take a number of pixels or a CSS length string like "0.125rem".
  • Keep lineWidth close to the element's border. A thicker beam reads as a second border that moves.
  • Scale size with the element. 100 is a long streak on a button and a short dash on a hero.
  • Keep the host taller than half of size. On a shorter host the beam reaches the opposite edge and lights both at once.
  • A large size on a small element also makes the beam cut across the corners. Lower it if the light seems to float there.

Color

By default the beam runs from your primary color to its hover shade, so it already follows your theme in both modes. Set color only when that is wrong.
  • A string is one color. The tail still fades out behind the head.
  • An array is a list of gradient stops, { color, percent }[] with percent from 0 to 100. The tail always fades out, so the last stop is not the tip.
  • Pass theme variables to color. var(--ant-color-primary) follows your theme and dark mode, and a hex does not.
  • Pick a color the host is not painted in. The beam uses your primary color by default, so on a primary Button it disappears into the fill. Pass color there.
  • Every beam on one element shares one color, so beams of different colors on one border are not possible.

More than one beam

count runs that many beams on the same border, 1 by default, spaced evenly around the loop from the first frame. A fraction is rounded down, and anything under 1 becomes 1.
  • Two is the useful case. It reads as one continuous circuit, which suits a surface that is working.
  • Three or more closes the gaps until the whole border looks lit. A static border is clearer at that point.
  • Raise duration as you raise count. Many lights moving fast read as noise.

Reduced motion

Reduced motion is handled. Under prefers-reduced-motion: reduce the beam is hidden, so you need no motion-safe: guard. Browsers without mask-composite or offset-path show the child with no beam.
Design the element so it is complete without the beam. A card that says “recommended” only by glowing says nothing to a reader who asked for less motion.