A centred serif headline that settles in word by word, then an ink panel that cycles through short request and reply pairs: the request in serif, the reply in mono a beat later, with thin progress bars to jump between them and a pause on hover. A pill that copies the install command on click and a sliding-marker pill close the section.
An ink panel that cycles through short exchanges: the request arrives in serif, the reply follows a beat later in mono, and thin bars underneath let you jump between them.
Upgrade to Pro
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://ui.beste.co/r/feature299?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/feature299?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/feature299.tsx plus the button22 sliding-marker pill and the text1 word stagger it uses.
The installed file exports feature299Demo alongside the block: the exact props behind the preview above. Spread it to get a working section in one line.
import { Feature299, feature299Demo } from "@/components/beste/block/feature299";
export default function Page() {
return <Feature299 {...feature299Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Feature299 } from "@/components/beste/block/feature299";
export default function Page() {
return (
<Feature299
eyebrow="Plain words in, finished code out"
heading="Every request is one sentence."
description="There is no syntax to learn and no picker to scroll."
exchanges={[
{ prompt: "Add a pricing section with three plans.", reply: "Placed under the hero, middle plan featured." },
{ prompt: "We need a changelog page.", reply: "Added a ruled changelog with version tags." },
]}
interval={4}
labels={{ you: "You", agent: "Agent" }}
command="npx your-cli@latest"
copyLabel="Copy command"
copiedLabel="Copied"
buttons={[{ label: "See what it builds", href: "/blocks" }]}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | – | Small-caps line above the heading |
heading | string | – | Section heading, through Text1 |
description | string | – | Paragraph under the heading |
exchanges | Exchange[] | [] | Request and reply pairs that take turns |
interval | number | 4 | Seconds each exchange stays on screen |
labels | { you: string; agent: string } | { you: "You", agent: "Agent" } | Wording of the two role labels |
command | string | – | The install command; clicking the pill copies it |
copyLabel | string | – | Accessible name for the pill in its resting state |
copiedLabel | string | – | Accessible name and live-region text once copied |
buttons | ActionButton[] | [] | Pill actions under the panel |
className | string | – | Extra classes for the outer <section> |
type Exchange = {
prompt: string;
reply: string;
};
type ActionButton = {
label: string;
href: string;
tone?: "primary" | "light" | "dark" | "outline";
};min-h, so a short exchange and a long one occupy the same height and the page below never shifts as the cycle turns.aria-current and the prompt as their accessible name, not tab roles, since there are no panels for them to control.