Scene
betaA picture drawn from the theme with something light floating on it: tiles, a preview card, people, or a hint.
Preview
SceneTiles on lake
ScenePreview on frost
ScenePeople on golden
SceneHint on dusk, at sm
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/scene
Use it for
- The top of an empty state that's a first step, a call to action, or a popup in a card. Inset it 6px from the card's edge, like p-1.5.
- It shows what's missing or what's next. What people must read goes under it, in the card.
- Float one thing on it. SceneTiles: up to three tiles, for what a feature brings together. ScenePreview: the real thing a feature makes, like "Q4 launch plan", with cursors for work done together. ScenePeople: up to four faces joined by a link, for sharing or a team. SceneHint: one round icon with a dark hint, for the one thing a step asks.
- Pick a backdrop: frost (the default, pale and quiet), alpine, golden, the blue lake, coast, and clubhouse, or the dark dusk and floodlights. Or pass image for a picture of your own.
- Use size sm (120px) in a popup.
Avoid it for
- More than one scene on a surface.
- A row, a list, or a chart. A scene is for the top of a card.
- Words people must read. Put them under the scene: a label only names it for screen readers.
Props
- backdrop
A picture drawn from the theme's colors. dusk and floodlights stay dark in both themes.
"frost" | "alpine" | "lake" | "coast" | "clubhouse" | "dusk" | "golden" | "floodlights" · default "frost"
- image
A picture of your own instead, as a URL.
string
- position
Where the picture is anchored, as a background-position: "center 70%".
string
- size
Scene, SceneTiles, and ScenePeople. A scene is 120px at sm, for a popup, 160px at md, and 200px at lg. Tiles are 48, 64, or 80px. Faces are 28, 36, or 44px.
"sm" | "md" | "lg" · default "md"
- label
Says what it shows to screen readers. Without it the scene is hidden from them.
string
- className
Extra classes on the scene. Later classes win.
string
- SceneTiles
Part. Up to three tiles rising left to right. SceneTile is { spot?: ReactNode; icon?: ReactNode; src?: string; tag?: string }.
{ tiles: SceneTile[]; size?: "sm" | "md" | "lg" }
- ScenePreview
Part. A card rising from the bottom, with a second card behind it. value is the change, in the brand color. lines defaults to 2. Two cursors at most: yours first, in tone "neutral".
{ title: string; spot?: ReactNode; src?: string; value?: string; lines?: number; cursors?: { name: string; tone?: "neutral" | "brand" }[] }
- ScenePeople
Part. Up to four faces on an arch, with icon in the middle. Default a link.
{ people: { name: string; src?: string }[]; icon?: ReactNode; size?: "sm" | "md" | "lg" }
- SceneHint
Part. One round icon with a dark hint under it: "Pair tablet".
{ icon: ReactNode; label?: string }
| Prop | Type | Default | What it does |
|---|---|---|---|
| backdrop | "frost" | "alpine" | "lake" | "coast" | "clubhouse" | "dusk" | "golden" | "floodlights" | "frost" | A picture drawn from the theme's colors. dusk and floodlights stay dark in both themes. |
| image | string | - | A picture of your own instead, as a URL. |
| position | string | - | Where the picture is anchored, as a background-position: "center 70%". |
| size | "sm" | "md" | "lg" | "md" | Scene, SceneTiles, and ScenePeople. A scene is 120px at sm, for a popup, 160px at md, and 200px at lg. Tiles are 48, 64, or 80px. Faces are 28, 36, or 44px. |
| label | string | - | Says what it shows to screen readers. Without it the scene is hidden from them. |
| className | string | - | Extra classes on the scene. Later classes win. |
| SceneTiles | { tiles: SceneTile[]; size?: "sm" | "md" | "lg" } | - | Part. Up to three tiles rising left to right. SceneTile is { spot?: ReactNode; icon?: ReactNode; src?: string; tag?: string }. |
| ScenePreview | { title: string; spot?: ReactNode; src?: string; value?: string; lines?: number; cursors?: { name: string; tone?: "neutral" | "brand" }[] } | - | Part. A card rising from the bottom, with a second card behind it. value is the change, in the brand color. lines defaults to 2. Two cursors at most: yours first, in tone "neutral". |
| ScenePeople | { people: { name: string; src?: string }[]; icon?: ReactNode; size?: "sm" | "md" | "lg" } | - | Part. Up to four faces on an arch, with icon in the middle. Default a link. |
| SceneHint | { icon: ReactNode; label?: string } | - | Part. One round icon with a dark hint under it: "Pair tablet". |
Accessibility
- Without label, the whole scene is hidden from screen readers, since the words under it say what it shows.
- With label, it is one image with that name, and what floats on it is part of the picture.
- The floating parts are cards with a border, so forced-colors mode keeps their edges.
- They pop in a beat apart. Reduced motion keeps only the fade.
Examples
At the top of a call to action
import { CalendarDays, Coins, Users } from "lucide-react";
<Card className="max-w-sm gap-0 p-1.5">
<Scene backdrop="lake">
<SceneTiles tiles={[{ spot: <CalendarDays /> }, { spot: <Users /> }, { spot: <Coins />, tag: "New" }]} />
</Scene>
<p className="p-3">Events, people and invoices, in one place.</p>
</Card>