Working with an AI assistant? Copy this prompt into Claude Code, Cursor or Copilot — it carries the install steps, a no-CLI fallback and a verification checklist.
Add the "scroll-reveal" item from the logic2b ui registry (https://ui.logic2b.com) to this project. Follow every step and verify at the end.
## Steps
1. If the project has no `components.json` yet, set up logic2b ui first:
```bash
npx logic2b@latest init
```
2. Install the item (the CLI resolves registry dependencies automatically):
```bash
npx logic2b@latest add scroll-reveal
```
3. Install any npm dependencies the command prints.
## If the CLI is not available
Do it manually from the registry payload:
1. Fetch https://ui.logic2b.com/r/scroll-reveal.json.
2. Write every entry in `files[]` to its mapped project path (see below).
3. Repeat (recursively) for each name in `registryDependencies`.
4. Install every package listed in `dependencies` across those payloads.
## Registry reference
- Index of every item: https://ui.logic2b.com/r/index.json
- Item payload (full source): https://ui.logic2b.com/r/<name>.json
- Usage examples per item: https://ui.logic2b.com/r/demos/index.json → https://ui.logic2b.com/r/demos/<demo>.json
- Docs index for agents: https://ui.logic2b.com/llms.txt (full docs: https://ui.logic2b.com/llms-full.txt)
- Every docs page is Markdown when you append `.md` to its URL.
Registry file paths map to project paths like this:
- `ui/*` → `@/components/ui/*`
- `blocks/<name>/*` → `@/components/*`
- `charts/*` → `@/components/charts/*`
- `hooks/*` → `@/hooks/*`
- `lib/utils.ts` → `@/lib/utils.ts`
- `theme.css` → next to the project's Tailwind entry stylesheet
## Conventions (do not break these)
- Tailwind CSS v4 (CSS-first config; no tailwind.config.js needed) and React 19.
- All colors go through semantic tokens (`bg-primary`, `text-muted-foreground`,
`border-border`, …). Never hardcode hex/oklch values in components.
- Dark mode is class-based: toggle `.dark` on `<html>`.
- Icons come from lucide-react.
## Verify before finishing
1. The dev server starts and the app renders without console errors.
2. TypeScript compiles (`tsc --noEmit` or the framework's check).
3. Components use the theme: a `<Button>` shows the primary token color,
and toggling `.dark` on `<html>` switches the palette.
Install the dependencies:
npm install radix-ui
Copy into ui/scroll-reveal.tsx:
"use client"import * as React from "react"import { Slot } from "radix-ui"import { useInView, type UseInViewOptions } from "@/hooks/use-in-view"import { cn } from "@/lib/utils"import { motionEnter, type MotionPreset } from "@/components/ui/motion"/** * Plays a motion enter recipe when the element scrolls into view (rather than on * mount like `<Motion>`). Built on the `useInView` IntersectionObserver hook and * the motion engine's recipes, so it shares the same presets, timing and * prefers-reduced-motion fallback. * * SSR- and no-JS-safe: it renders visible until it has mounted and armed, so * search engines and scriptless clients always see the content. */export interface ScrollRevealProps extends React.ComponentProps<"div">, Pick<UseInViewOptions, "once" | "amount" | "margin" | "root"> { /** Enter recipe to play when revealed. Default `"fade-up"`. */ preset?: MotionPreset /** Reveal duration in milliseconds. Overrides the 500ms default. */ duration?: number /** Reveal delay in milliseconds — the knob for staggering a group. */ delay?: number /** Merge props onto the single child instead of rendering a wrapper. */ asChild?: boolean}function ScrollReveal({ className, preset = "fade-up", duration, delay, asChild = false, once = true, amount = 0.3, margin = "0px", root, style, ...props}: ScrollRevealProps) { const [ref, inView] = useInView<HTMLDivElement>({ once, amount, margin, root }) const [armed, setArmed] = React.useState(false) React.useEffect(() => setArmed(true), []) const timing: React.CSSProperties = {} if (duration != null) (timing as Record<string, string>)["--tw-animation-duration"] = `${duration}ms` if (delay != null) (timing as Record<string, string>)["--tw-animation-delay"] = `${delay}ms` const Comp = asChild ? Slot.Root : "div" return ( <Comp ref={ref} data-slot="scroll-reveal" data-in-view={inView ? "" : undefined} className={cn( inView ? motionEnter(preset) : armed ? "opacity-0" : undefined, className )} style={{ ...timing, ...style }} {...props} /> )}export { ScrollReveal }
Usage
import { ScrollReveal } from "@/components/ui/scroll-reveal"<ScrollReveal preset="fade-up"> <Card>…</Card></ScrollReveal>
ScrollReveal is the scroll-triggered counterpart to
Motion: instead of playing on mount, it waits until
the element scrolls into view (via the
useInView IntersectionObserver hook), then plays the same enter
recipe. It shares every preset — fade, fade-up/down/left/right, scale,
blur — and the same duration, delay, asChild props and
prefers-reduced-motion fallback.
It renders visible until it has mounted and armed, so search engines and clients
without JavaScript always see the content — the reveal is pure progressive
enhancement.
Staggering a group
Give each item an increasing delay to cascade a list as it enters:
scroll-reveal installs the useInView hook, which you can use on its own to
gate anything on visibility — a lazy-loaded chart, a
useCountUp counter, an autoplaying video: