Components
Cycling placeholder
A text field whose placeholder rolls through examples of what to type, so an empty search box teaches what it can find. The roll pauses while the field has focus and the examples disappear once anything is typed.
pnpm dlx shadcn@latest add @caliper/cycling-placeholderWhen to use
Use it for
- Search and command fields that can do more than people expect.
- AI prompts where an example sets the tone.
Skip it when
- Fields with one obvious input, such as email.
- Using the examples as the field's only label; keep a visible or accessible name.
Decisions
Why it is built this way, so you know what to keep when you change your copy.
Behaviour
- Pauses on focus
- Moving text next to the caret is distracting, so the roll stops while the field has focus.
Usage
- One stable hint
- The first example is the field's real placeholder, so assistive technology hears one hint and not a changing one.
Installation
pnpm dlx shadcn@latest add @caliper/cycling-placeholder
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.
import { Search } from "lucide-react" import { type ReactNode } from "react" import { CyclingPlaceholder } from "@/components/ui/cycling-placeholder" function Panel({ title, context, className = "max-w-md", children }: { title: string; context: string; className?: string; children: ReactNode }) { return ( <section className={`grid w-full gap-5 rounded-overlay border border-border bg-background p-6 shadow-resting max-sm:p-4 ${className}`}> <div className="grid gap-1"> <p className="font-medium">{title}</p> <p className="text-sm text-muted-foreground">{context}</p> </div> {children} </section> ); } export function CyclingPlaceholderDemo() { return ( <Panel title="Search" context="The examples change until you start typing."> <div className="relative"> <Search className="pointer-events-none absolute top-1/2 left-3 z-10 size-4 -translate-y-1/2 text-muted-foreground" aria-hidden="true" /> <CyclingPlaceholder aria-label="Search" prefix="Search" examples={["overdue invoices", "Maya's last comment", "deploys this week", "refunds over $500"]} className="[&_input]:pl-9 [&_[data-slot=cp-hint]]:left-9" /> </div> </Panel> ); }
API reference
CyclingPlaceholder
component
Also accepts the native props of its root element and a ref.
Accessibility
It is built on native elements and keeps aria-describedby and aria-hidden 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.
Related
- SearchA search field with a clear button and the right keyboard on phones.
- InputA recessed text field with optional leading and trailing affixes; it never jumps when validation appears.
- Text loopOne word in a sentence that cycles through a few options. The widest word reserves the space, it pauses on hover, focus, off screen and in a hidden tab, and a pause button stops it.