Signal Week, a festival of graphic design in public space
Five days of posters, wayfinding and street type in the old tram depot. Talks at noon, walks at dusk, and every wall of the city open for print.
A festival hero for graphic design in public space whose headline is a trivision board built on the text22 component: the statement is cut into vertical triangular slats that turn one after another in a wave, flipping from a paper face to an ink face to an accent face, each carrying the next statement, with shading as faces turn away, and crisp, even seams between the slats. An eyebrow sits above, a ruled row below holds the intro, an Arrow Pill and an underline link. Pauses on hover and offscreen; with reduced motion the statement changes in place.
A type-led hero whose headline is a trivision board from the text22 component: the statement is cut into vertical triangular slats that turn in a wave, flipping between a paper, an ink and an accent face that each carry the next statement. An eyebrow sits above and a ruled row below holds the intro, an Arrow Pill and an underline link.
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/hero212?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/hero212?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/hero212.tsx plus the text22 trivision headline and the button23 and button2 buttons it uses.
The installed file exports hero212Demo alongside the block: the exact props behind the preview above. Spread it to get a working hero in one line.
import { Hero212, hero212Demo } from "@/components/beste/block/hero212";
export default function Page() {
return <Hero212 {...hero212Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Hero212 } from "@/components/beste/block/hero212";
export default function Page() {
return (
<Hero212
eyebrow="Signal Week, a festival of graphic design in public space"
description="Five days of posters, wayfinding and street type in the old tram depot."
buttons={[
{ label: "Get a festival pass", href: "/passes" },
{ label: "See the program", href: "/program" },
]}
board={{
faces: [
{ text: "Seen from across the street.", tone: "paper" },
{ text: "Read by someone in a hurry.", tone: "ink" },
{ text: "Remembered for years after.", tone: "accent" },
],
slats: 18,
interval: 3.2,
}}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | – | Small muted line above the board |
description | string | – | Intro on the left of the ruled row |
buttons | ActionButton[] | [] | The first renders as a dark Arrow Pill, every later one as a Button2 underline link |
board | Omit<Text22Props, "as" | "className" | "faceClassName"> | – | Forwarded to the Text22 component: the faces with their text and tone, slat count, interval, turn duration, stagger, shading and seams |
className | string | – | Extra classes for the outer <section> |
type ActionButton = {
label: string;
href: string;
};heading prop: the board is the headline, rendered by Text22 with as="h1", and it only mounts when board.faces has at least 1 entry.text-5xl up to lg:text-[9rem], so the faces stay the same size as they turn.Text22 pauses while hovered and while offscreen through an IntersectionObserver and on hidden tabs.prefers-reduced-motion the board swaps the statement in place instead of turning the slats, and the eyebrow and row render without their rise.max(640px, 100svh - 78px) tall with the content centered vertically.