Scan revealPro
A top to bottom reveal behind a hairline scan edge, tied to real byte progress when you stream the file.
On this page
Installation
Usage
import { ScanReveal } from "@/components/ui/chatcn/pro/scan-reveal"
import { fetchImageWithProgress } from "@/lib/chatcn-pro-reveal"
const url = await fetchImageWithProgress(imageUrl, { onProgress: setProgress })
<ScanReveal src={url} alt={image.prompt} progress={progress} />fetchImageWithProgress reads the response stream and reports bytes received over content-length (or null when the server sends no length). A file cannot decode until its last byte, so until then the received part fills with fine scan lines: the edge is honest about bytes, not pixels. Once the file decodes the photo shows above the edge.
Masking only: clip-path on the image and a transform on the edge, no canvas. The edge uses the accent token, which defaults to ink.
Examples
Real byte progress
The image downloads with fetch. The slow connection paces the real bytes at 40 KB/s so you can watch the edge follow them.
States
Waiting for the image, running at 40%, running with no numbers, held by revealed={false}, revealed, a missing file, a timer and right to left.
Props
| Prop | Type | Notes |
|---|---|---|
src, alt | string | alt is required: the accessible name, announced once the image is in |
progress | number | null | Real job progress, 0 to 1. null means running with no numbers. Leave it out for timer mode |
revealed | boolean | Timer mode: false holds the first frame, true plays once the image loads. Default true |
duration | number | Timer length in ms. Default 1400 |
onRevealed | () => void | Once per image, when the plain image shows |
onStatusChange | (status: "loading" | "ready" | "revealing" | "revealed" | "error") => void | |
aspectRatio, width, height | Set the frame before the image loads, so nothing shifts | |
paper | string | The ground under the effect. Token or CSS color. Default var(--chatcn-bg) |
announcement | string | false | What the live region says once revealed. Default Image ready: <alt> |
errorText, retryLabel | Copy for the error state and its reload button | |
edgeColor | string | The scan edge. Default var(--chatcn-accent) |
Keyboard
| Key | Action |
|---|---|
Tab | Reaches the Reload image button in the error state. The reveal itself takes no focus |
Accessibility
- The effect layer is
aria-hidden. The real<img>with youraltis always there, and the frame isaria-busyuntil the reveal ends. - A polite live region says
Image ready: <alt>once, when the plain image shows. Progress ticks are never announced. - Under
forced-colors: activethe effect hides and the plain image shows with system colors. - A broken file shows what happened and a Reload image button that retries the download.
Motion
- The edge snaps to device pixels so it stays one crisp line. Only
clip-pathandtransformchange. - Under reduced motion one still frame holds while the job runs, then the image crossfades in over 200 ms. Nothing moves.
- The timer only runs on screen in a visible tab; offscreen it waits where it was.