Pulsing Border
snap-cn/pulsing-borderFreeComponent
Glowing 'AI is working' frame — a soft pulsing border overlay that breathes around the scene edges while a response streams.
Install it
npx shadcn@latest add https://snapcn.dev/r/pulsing-border.jsonSource
1"use client";23import {4 PulsingBorder as PaperPulsingBorder,5 type PulsingBorderProps as PaperPulsingBorderProps,6} from "@paper-design/shaders-react";7import { useCallback, useState } from "react";8import {9 continueRender,10 delayRender,11 useCurrentFrame,12 useVideoConfig,13} from "remotion";14import {15 mixOklch,16 parseColor,17 type SnapCnTheme,18 toCss,19 useSnapCnTheme,20} from "@/lib/snap-cn-ui";2122export interface PulsingBorderProps23 extends Omit<PaperPulsingBorderProps, "frame" | "ref"> {24 /** First glow color. Defaults to the design system's `primary`. */25 glowColorA?: string;26 /** Second glow color. Defaults to `primary` lifted toward the page. */27 glowColorB?: string;28 /** Design-system token overrides. */29 theme?: Partial<SnapCnTheme>;30 mode?: "light" | "dark";31}3233/**34 * "AI is working" frame — a soft glowing border that pulses around the35 * edges of the scene while a response streams. Transparent by default so36 * it layers over any content (e.g. a chat UI mid-response).37 *38 * Deterministic: the shader's clock is driven from `useCurrentFrame()`39 * via the `frame` prop, so renders are frame-exact.40 */41export function PulsingBorder({42 speed = 0.6,43 colorBack = "#00000000",44 colors,45 glowColorA,46 glowColorB,47 roundness = 0.08,48 thickness = 0.06,49 intensity = 0.15,50 bloom = 0.2,51 className,52 theme,53 mode,54 ...rest55}: PulsingBorderProps) {56 const frame = useCurrentFrame();57 const { fps, width, height } = useVideoConfig();58 const t = useSnapCnTheme(theme, mode);5960 // The shader parses colors in WebGL, not in CSS — it has no `oklch()`. Tokens61 // are handed over as concrete rgb, which `toCss`/`mixOklch` already emit.62 const glowA = glowColorA ?? toCss(parseColor(t.primary));63 const glowB = glowColorB ?? mixOklch(t.primary, t.background, 0.35);6465 const [handle] = useState(() => delayRender("pulsing-border"));66 const gate = useCallback(67 (element: HTMLDivElement | null) => {68 if (!element) return;69 requestAnimationFrame(() =>70 requestAnimationFrame(() => continueRender(handle)),71 );72 },73 [handle],74 );7576 return (77 <div78 ref={gate}79 className={className}80 style={{ position: "absolute", inset: 0 }}81 >82 <PaperPulsingBorder83 speed={0}84 frame={(frame / fps) * speed * 1000}85 colorBack={colorBack}86 colors={colors ?? [glowA, glowB]}87 roundness={roundness}88 thickness={thickness}89 intensity={intensity}90// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from snap-cn
All 24 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Answer Streamanswer-stream | snap-cn | Components | Free | 2 deps | |
| UI Caretcaret | snap-cn | Components | Free | 1 dep | |
| Follower Rushfollower-rush | snap-cn | Components | Free | 2 deps | |
| Product Herohero-launch | snap-cn | Components | Free | 2 deps | |
| UI Inputinput | snap-cn | Components | Free | 2 deps · 2 files | |
| Karaoke Captionskaraoke-captions | snap-cn | Components | Free | 2 deps | |
| Laptop Framelaptop-frame | snap-cn | Components | Free | 1 dep | |
| Logo Assemblelogo-assemble | snap-cn | Components | Free | 1 dep |
