caliper uidocs

Shared highlight

Free

A highlight that glides between the items of a list as focus or hover moves.

Installs components/ui/shared-highlight.tsxDepends on none beyond ReactMotion shared motion tokens
pnpm dlx shadcn@latest add https://caliperui.site/r/shared-highlight.json

When to use #

Use it for

  • Menus, tab strips and lists where one item is active.
  • You want one moving indicator instead of per-row backgrounds.

Skip it when

  • The list is long and scrolls; the glide becomes distracting.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/shared-highlight.json

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above. It is built from SharedHighlight and SharedHighlightItem.

example.tsx
import { useState } from "react"
import { SharedHighlight, SharedHighlightItem } from "@/components/ui/shared-highlight"

export function SharedHighlightDemo() {
  const [value, setValue] = useState("overview");
  const items = [["overview", "Overview"], ["activity", "Activity"], ["settings", "Settings"]] as const;
  return (
    <SharedHighlight value={value} onValueChange={setValue} className="grid w-56 gap-0.5">
      {items.map(([id, label]) => (
        <SharedHighlightItem key={id} value={id} as="button" onClick={() => setValue(id)} className="rounded-control px-3 py-2 text-left text-sm">
          {label}
        </SharedHighlightItem>
      ))}
    </SharedHighlight>
  );
}

API reference #

SharedHighlight

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
children *ReactNode—
valuestring—The selected item. Omit for hover-only highlighting.
defaultValuestring | (string & readonly string[])—
onValueChange((value: string) => void)—
asElementType"div"

SharedHighlightItem

component
PropTypeDefault
value *string—
asElementType"div"
children *ReactNode—
classNamestring—
onPointerEnter((event: PointerEvent<Element>) => void)—
onFocus((event: FocusEvent<Element, Element>) => void)—
onClick((event: MouseEvent<Element, MouseEvent>) => void)—

Accessibility #

It is built on native elements and keeps aria-hidden and aria-current in step with state.

No custom key handling; Tab and the native activation keys do the work.

Motion #

Transitions use the shared duration and easing tokens. Under prefers-reduced-motion those tokens collapse to instant and travel distances go to zero, so state changes still happen without movement.