AngleRing

A comet travelling a card's border — a conic gradient driven by a registered @property angle, so it animates on the compositor.

Install

$ npx shadcn@latest add wisp.pouriah.com/r/angle-ring.json
CSSno JS
Frame budget
0.41 ms
▲ 118 fps · M1 Air

AngleRingesc or double-click to exit full screendouble-tap to exit

primitives/angle-ring.tsx
"use client";

import * as React from "react";
import { cx, useTokenColorExpr } from "@pouriahlabs/wisp-ui";

/**
 * AngleRing — a comet travelling a card's whole border.
 *
 * A conic gradient rotated by animating a registered `@property` angle. That
 * registration is the point: without it, `--angle` is an untyped string and the
 * browser can't interpolate it, so the whole thing falls back to a stepped
 * repaint on the main thread. Typed as `<angle>`, it animates on the compositor
 * and costs nothing.
 *
 * The ring is a padded parent whose background is the gradient; the child sits
 * on top with its own surface, leaving only the padding visible as the border.
 */

export interface AngleRingProps extends React.HTMLAttributes<HTMLDivElement> {
  fromToken?: string;
  toToken?: string;
  /** Milliseconds per revolution. */
  duration?: number;
  /** Border thickness, px. */
  width?: number;
  /** Class for the inner surface. Give it your card background. */
  innerClassName?: string;
}

export function AngleRing({
  fromToken = "--wisp",
  toToken = "--lantern",
  duration = 3400,
  width = 1.5,
  className,
  innerClassName,
  children,
  style,
  ...props
}: AngleRingProps) {
  const from = useTokenColorExpr(fromToken);
  const to = useTokenColorExpr(toToken);
  return (
    <div
      className={cx("wisp-ring", className)}
      style={
        {
          "--wisp-ring-from": from,
          "--wisp-ring-to": to,
          "--wisp-ring-duration": `${duration}ms`,
          "--wisp-ring-width": `${width}px`,
          ...style,
        } as React.CSSProperties
      }
      {...props}
    >
      <div className={cx("wisp-ring-inner", innerClassName)}>{children}</div>
    </div>
  );
}

Certified against the contract