Highlight Overlay
Imperative overlay to highlight any DOM element via id or ref.
Overview
The HighlightOverlay
renders a temporary, animated ring on top of a target element. You can target by providing a highlightedElement
id (string) and/or a map of element refs.
It automatically positions and tracks layout changes, supports optional auto-clear, and can be styled via className
.
This component is designed to help AI agents guide the user's attention to specific elements on a page (i.e. via a tool call).
Usage
Basic
import { Button, Card, HighlightOverlay } from "@pipecat-ai/voice-ui-kit";
function Demo() {
const [highlightedElement, setHighlightedElement] = React.useState<string | null>(null);
const card1Ref = React.useRef<HTMLDivElement>(null);
const card2Ref = React.useRef<HTMLDivElement>(null);
const elementRefs = {
card1: card1Ref.current,
card2: card2Ref.current,
};
return (
<div className="flex flex-col gap-4 items-center">
<ButtonGroup>
<Button variant="outline" onClick={() => setHighlightedElement("card1")}>Highlight Card 1</Button>
<Button variant="outline" onClick={() => setHighlightedElement("card2")}>Highlight Card 2</Button>
<Button variant="outline" onClick={() => setHighlightedElement(null)}>Clear Highlight</Button>
</ButtonGroup>
<div className="grid grid-cols-2 gap-6 my-4">
<Card ref={card1Ref} className="p-6 w-64 flex items-center justify-center">
<CardHeader>
<CardTitle className="font-semibold">Card 1</CardTitle>
</CardHeader>
</Card>
<Card ref={card2Ref} className="p-6 w-64 flex items-center justify-center">
<CardHeader>
<CardTitle className="font-semibold">Card 2</CardTitle>
</CardHeader>
</Card>
</div>
<HighlightOverlay
highlightedElement={highlightedElement}
elementRefs={elementRefs}
onHighlightElement={setHighlightedElement}
className="ring-2 ring-blue-500 ring-offset-2"
offset={8}
autoClearDuration={4000}
/>
</div>
);
}
render(<Demo />)
Custom styling
import { Button, Card, HighlightOverlay } from "@pipecat-ai/voice-ui-kit";
function Demo() {
const [highlightedElement, setHighlightedElement] = React.useState<string | null>(null);
return (
<div className="flex flex-col gap-4 items-center">
<ButtonGroup>
<Button variant="outline" onClick={() => setHighlightedElement("card1")}>Highlight Card 1</Button>
<Button variant="outline" onClick={() => setHighlightedElement("card2")}>Highlight Card 2</Button>
<Button variant="outline" onClick={() => setHighlightedElement(null)}>Clear Highlight</Button>
</ButtonGroup>
<div className="grid grid-cols-2 gap-6 my-4">
<Card id="card1" className="p-6 w-64 flex items-center justify-center">
<CardHeader>
<CardTitle className="font-semibold">Card 1</CardTitle>
</CardHeader>
</Card>
<Card id="card2" className="p-6 w-64 flex items-center justify-center">
<CardHeader>
<CardTitle className="font-semibold">Card 2</CardTitle>
</CardHeader>
</Card>
</div>
<HighlightOverlay
highlightedElement={highlightedElement}
onHighlightElement={setHighlightedElement}
className={highlightedElement === "card1" ? "ring-4 ring-green-500" : "ring-4 ring-red-500"}
offset={12}
autoClearDuration={3000}
/>
</div>
);
}
render(<Demo />)
No auto-clear
import { Button, Card, HighlightOverlay } from "@pipecat-ai/voice-ui-kit";
function Demo() {
const [highlightedElement, setHighlightedElement] = React.useState<string | null>(null);
return (
<div className="flex flex-col gap-4 items-center">
<ButtonGroup>
<Button variant="outline" onClick={() => setHighlightedElement("card3")}>Highlight Card 1</Button>
<Button variant="outline" onClick={() => setHighlightedElement("card4")}>Highlight Card 2</Button>
<Button variant="outline" onClick={() => setHighlightedElement(null)}>Clear Highlight</Button>
</ButtonGroup>
<div className="grid grid-cols-2 gap-6 my-4">
<Card id="card3" className="p-6 w-64 flex items-center justify-center">
<CardHeader>
<CardTitle className="font-semibold">Card 1</CardTitle>
</CardHeader>
</Card>
<Card id="card4" className="p-6 w-64 flex items-center justify-center">
<CardHeader>
<CardTitle className="font-semibold">Card 2</CardTitle>
</CardHeader>
</Card>
</div>
<HighlightOverlay
highlightedElement={highlightedElement}
onHighlightElement={setHighlightedElement}
className="ring-2 ring-purple-500 ring-offset-4"
offset={6}
autoClearDuration={0}
/>
</div>
);
}
render(<Demo />)
Props
Prop | Type | Default |
---|---|---|
highlightedElement? | string | null | null |
elementRefs? | Record<string, HTMLElement | null> | {} |
onHighlightElement? | (elementId: string | null) => void | undefined |
className? | string | "ring-4" |
offset? | number | 4 |
autoClearDuration? | number | 3000 |
CSS variables
- --animate-highlight: controls the highlight animation shorthand. Default is
highlight-focus 1.2s ease-in-out forwards
. - --highlight-final-opacity: controls the final opacity at the end of the animation. The component sets this automatically based on
autoClearDuration
(0 →1
, otherwise →0
), but you can override it.
Override globally (scoped styles use .voice-ui-kit
):
.voice-ui-kit {
--animate-highlight: highlight-focus 900ms ease-out forwards;
}
Per-instance override:
<HighlightOverlay
highlightedElement="card1"
style={{ "--highlight-final-opacity": "0.6" }}
/>