- Accordion
- Agent Thinking
- Alert
- Alert Dialog
- Announcement
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Checkbox
- Checkbox Card
- Code Block
- Collapsible
- Color Picker
- Combobox
- Command
- Composer
- Composer Loader
- Context Menu
- Data Grid
- Data Table
- Date Picker
- Dialog
- Direction
- Drawer
- Dropdown Menu
- Empty
- Field
- File Upload
- Hover Card
- Input
- Input Group
- Input Otp
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Meter
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Period Switcher
- Popover
- Progress
- Questionnaire
- Radio Card
- Radio Group
- Resizable
- Reveal
- Scroll Area
- Search Field
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Social Button
- Sonner
- Spinner
- Status Dot
- Switch
- Switch Card
- Table
- Tabs
- Tag Group
- Textarea
- Theme Toggle
- Time Field
- Toggle
- Toggle Group
- Tooltip
- Trace
- Tree View
- Agent Creator 01
- Agent Limits Card 01
- Agent Progress 01
- AI Chat 01
- AI Image Generation 01
- AI Profile 01
- Alerts Card 01
- Auth Card 01
- Calendar 01
- Composer 01
- Composer Attachments 01
- Composer Panel 01
- Finance Dashboard 01
- Home Dashboard 01
- Marketing Dashboard 01
- Medical Profile 01
- Meeting Scheduler 01
- Multi Agent Chat 01
- Notification Center 01
- Patient Info Card 01
- Project Board 01
- Questionnaire 01
- Settings Modal 01
- Sidebar 01
- Stat Cards 01
- Task List 01
- Web Search 01
- Activity Rings Card 01
- Area Chart Card 01
- Bar List Card 01
- Combo Chart Card 01
- Contributions Card 01
- Earnings Chart Card 01
- Funnel Chart Card 01
- Heatmap Chart Card 01
- Line Chart Card 01
- Most Active Days Card 01
- Orders Chart Card 01
- Radar Chart Card 01
- Radial Chart Card 01
- Revenue Chart Card 01
- Sankey Chart Card 01
- Scatter Chart Card 01
- Sleep Score Card 01
- Stage Bars Card 01
- Steps Card 01
Design review
Today, 10:30
Sprint planning
Today, 14:00
Customer call
Tomorrow, 09:15
Release notes
Friday, 16:45
With blur
The same rise, from a 3px blur. Used by page-level sections.
Installation
$ pnpm dlx shadcn@latest add @cobalt/reveal
The reveal keyframe is installed with the component, so it works in any project.
Usage
{
items.map((item, index) => (
<Reveal key={item.id} index={index} render={<Card />}>
...
</Reveal>
));
}| Prop | Default | Use |
|---|---|---|
index | 0 | Position in a list. Each step waits step ms longer. |
step | 40 | Length of one stagger step in ms. |
delay | 0 | Extra wait in ms, added to the stagger. |
blur | false | Also fades in from a 3px blur, for page sections. |
render | div | The element to render, for example <Card /> or <li />. |
Cap long lists yourself, for example index={Math.min(index, 8)}.
Motion
The element rises 12px and fades in over 500ms with cubic-bezier(0.22, 1, 0.36, 1). It keeps its end state afterwards. With prefers-reduced-motion: reduce the animation is removed and the element is shown in place, so nothing is hidden.
Source
"use client";
import { mergeProps } from "@base-ui/react/merge-props";
import { useRender } from "@base-ui/react/use-render";
import { cn } from "cn";
import * as React from "react";
/** True inside a `Reveal`, so a nested one does not play a second entrance. */
const RevealContext = React.createContext(false);
type RevealProps = useRender.ComponentProps<"div"> & {
/**
* `card` is the entrance of a whole card: 300ms, from a 6px blur and 4px
* lower.
*/
variant?: "default" | "card";
/** Position in a list. Each step waits `step` ms longer than the one before. */
index?: number;
/** Extra wait in ms, added to the stagger. */
delay?: number;
/** Length of one stagger step in ms. */
step?: number;
/** Also fades in from a 3px blur. */
blur?: boolean;
};
/**
* Entrance animation: the element fades in and rises 12px over 500ms.
*
* It is a plain CSS animation with `both` fill, so the server HTML already
* paints the first (hidden) frame and nothing waits for hydration: the content
* is never shown and then hidden. Without JavaScript it still plays, and
* reduced motion removes it, so the element stays readable.
*
* A `Reveal` inside another `Reveal` does not animate: the outer one already
* brings it in, and two stacked entrances read as a flicker.
*/
function Reveal({
className,
variant = "default",
index = 0,
delay = 0,
step = 40,
blur = false,
render,
...props
}: RevealProps) {
const nested = React.useContext(RevealContext);
const card = variant === "card";
const wait = Math.max(0, index * step + delay);
const element = useRender({
defaultTagName: "div",
props: mergeProps<"div">(
nested
? { className }
: {
className: cn(
"animate-[reveal_var(--reveal-duration,500ms)_cubic-bezier(0.22,1,0.36,1)_var(--reveal-delay)_both] motion-reduce:animate-none!",
className
),
style: {
"--reveal-delay": `${wait}ms`,
"--reveal-filter":
card || blur ? `blur(${card ? 6 : 3}px)` : "none",
...(card && {
"--reveal-duration": "300ms",
"--reveal-rise": "4px",
}),
} as React.CSSProperties,
},
props
),
render,
state: { slot: "reveal" },
});
return (
<RevealContext.Provider value={true}>{element}</RevealContext.Provider>
);
}
export { Reveal };
export type { RevealProps }; On this page